From 4db28f7851fda0393c4676956efc85deba2de6ef Mon Sep 17 00:00:00 2001 From: Dan Onoshko Date: Fri, 2 Jan 2015 02:41:48 +0700 Subject: [PATCH] ES6 for ...of is added --- README.md | 3 ++- lib/ast.js | 17 ++++++++++++++++ lib/compress.js | 16 ++++++++-------- lib/parse.js | 50 ++++++++++++++++++++++++++++++++++++------------ lib/translate.js | 40 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 105 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index d46fed88..6ec9a76b 100644 --- a/README.md +++ b/README.md @@ -547,7 +547,8 @@ Future plans console.log(name); }, this); -- `@use client` and `@use server` commands +- `@use client` and `@use server` commands, status: done +- control flows without braces, status: done - static typing - `@use` expressions diff --git a/lib/ast.js b/lib/ast.js index becf41cf..79b4bf5a 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -281,6 +281,23 @@ Cola.AST_ForIn = Cola.DEFNODE("ForIn", "init name object", { } }, Cola.AST_IterationStatement); +Cola.AST_ForOf = Cola.DEFNODE("ForOf", "init name object", { + $iscola: true, + $documentation: "A `for ... of` statement", + $propdoc: { + init: "[AST_Node] the `for/in` initialization code", + name: "[AST_SymbolRef?] the loop variable, only if `init` is AST_Var", + object: "[AST_Node] the object that we're looping through" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.init._walk(visitor); + this.object._walk(visitor); + this.body._walk(visitor); + }); + } +}, Cola.AST_IterationStatement); + Cola.AST_With = Cola.DEFNODE("With", "expression", { $documentation: "A `with` statement", $propdoc: { diff --git a/lib/compress.js b/lib/compress.js index 87f502d2..7bbdf5ef 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -2243,19 +2243,19 @@ Cola.Compressor.MathFuncs = { return self; }); - OPT(AST_Infinity, function (self, compressor) { - return make_node(AST_Binary, self, { + OPT(Cola.AST_Infinity, function (self, compressor) { + return make_node(Cola.AST_Binary, self, { operator : '/', - left : make_node(AST_Number, null, {value: 1}), - right : make_node(AST_Number, null, {value: 0}) + left : make_node(Cola.AST_Number, null, {value: 1}), + right : make_node(Cola.AST_Number, null, {value: 0}) }); }); - OPT(AST_NaN, function (self, compressor) { - return make_node(AST_Binary, self, { + OPT(Cola.AST_NaN, function (self, compressor) { + return make_node(Cola.AST_Binary, self, { operator : '/', - left : make_node(AST_Number, null, {value: 0}), - right : make_node(AST_Number, null, {value: 0}) + left : make_node(Cola.AST_Number, null, {value: 0}), + right : make_node(Cola.AST_Number, null, {value: 0}) }); }); diff --git a/lib/parse.js b/lib/parse.js index bfdf1298..52bc3455 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -49,7 +49,7 @@ !this.Cola && (this.Cola = {}); Cola.KEYWORDS = 'break case catch const continue debugger default delete do else finally for function if in instanceof new return switch throw try typeof var void while with'; -Cola.cKEYWORDS = Cola.KEYWORDS.replace(' void', '') + ' static covert export async get set when clone is isnt class extends singleton resolve reject await'; +Cola.cKEYWORDS = Cola.KEYWORDS.replace(' void', '') + ' static covert export async get set when clone of is isnt class extends singleton resolve reject await'; Cola.KEYWORDS_ATOM = 'false null true'; Cola.cKEYWORDS_ATOM = Cola.KEYWORDS_ATOM + ' on yes off no'; @@ -127,6 +127,7 @@ Cola.OPERATORS = [ // d - different left and right types of vars, s - same // ColaScript "await", "clone", + "of", "is", "isnt", "**", @@ -813,7 +814,7 @@ Cola.cPRECEDENCE = Cola.mergeTokens( ["^"], ["&"], ["==", "===", "!=", "!=="], - ["<", ">", "<=", ">=", "in", "instanceof", "is", "isnt"], + ["<", ">", "<=", ">=", "in", "of", "instanceof", "is", "isnt"], [">>", "<<", ">>>"], ["+", "-"], ["*", "/", "%", "**", "%%"] @@ -840,7 +841,8 @@ Cola.Parser = function ($TEXT, options) { toplevel : null, expression : false, html5_comments : true, - is_js : false + is_js : false, + braces_free : true }); this.is_js = !!this.options.is_js; @@ -972,14 +974,14 @@ Cola.Parser.prototype.unexpected = function (token) { this.token_error(token, "Unexpected token: " + token.type + " `" + token.value + "`"); }; -Cola.Parser.prototype.expect_token = function (type, val) { +Cola.Parser.prototype.expect_token = function (type, val, stay) { if (this.is(type, val)) { - return this.next(); + return stay ? undefined : this.next(); } this.token_error(this.S.token, "Unexpected token " + this.S.token.type + " `" + this.S.token.value + "`" + ", expected " + type + " `" + val + "`"); }; -Cola.Parser.prototype.expect = function (punc) { return this.expect_token("punc", punc); }; +Cola.Parser.prototype.expect = function (punc, stay) { return this.expect_token("punc", punc, stay); }; Cola.Parser.prototype.can_insert_semicolon = function () { if(!this.is_js) return false; @@ -994,9 +996,10 @@ Cola.Parser.prototype.semicolon = function () { }; Cola.Parser.prototype.parenthesised = function () { - this.expect("("); + if (this.is_js || !this.options.braces_free) this.expect("("); var exp = this.expression(true); - this.expect(")"); + if (this.is_js || !this.options.braces_free) this.expect(")"); + else this.expect("{", true); return exp; }; @@ -1519,17 +1522,24 @@ Cola.Parser.prototype.break_cont = function (type) { }; Cola.Parser.prototype.for_ = function () { - this.expect("("); + if (this.is_js || !this.options.braces_free) this.expect("("); var init = null; if (!this.is("punc", ";")) { init = this.is("keyword", "var") || !this.is_js && this.is("name") && this.next_is("name") ? (this.next(), this.var_(true, this.prev().value)) : this.expression(true, true); + if (this.is("operator", "in")) { if (init instanceof Cola.AST_Var && init.definitions.length > 1) this.croak("Only one variable declaration allowed in for..in loop"); this.next(); return this.for_in(init); + } else + if (!this.is_js && this.is("operator", "of")) { + if (init instanceof Cola.AST_Var && init.definitions.length > 1) + this.croak("Only one variable declaration allowed in for..of loop"); + this.next(); + return this.for_of(init); } } return this.regular_for(init); @@ -1540,7 +1550,8 @@ Cola.Parser.prototype.regular_for = function (init) { var test = this.is("punc", ";") ? null : this.expression(true); this.expect(";"); var step = this.is("punc", ")") ? null : this.expression(true); - this.expect(")"); + if (this.is_js || !this.options.braces_free) this.expect(")"); + else this.expect("{", true); var _this = this; return new Cola.AST_For({ init : init, @@ -1553,7 +1564,8 @@ Cola.Parser.prototype.regular_for = function (init) { Cola.Parser.prototype.for_in = function (init) { var lhs = init instanceof Cola.AST_Var ? init.definitions[0].name : null; var obj = this.expression(true); - this.expect(")"); + if (this.is_js || !this.options.braces_free) this.expect(")"); + else this.expect("{", true); var _this = this; return new Cola.AST_ForIn({ init : init, @@ -1563,6 +1575,20 @@ Cola.Parser.prototype.for_in = function (init) { }); }; +Cola.Parser.prototype.for_of = function (init) { + var lhs = init instanceof Cola.AST_Var ? init.definitions[0].name : null; + var obj = this.expression(true); + if (!this.options.braces_free) this.expect(")"); + else this.expect("{", true); + var _this = this; + return new Cola.AST_ForOf({ + init : init, + name : lhs, + object : obj, + body : this.in_loop(function(){ return this.statement() }) + }); +}; + Cola.Parser.prototype.class_ = function(mods){ if(this.S.in_class) this.token_error(this.prev(), "You can define class or singleton only in root scope."); @@ -2579,7 +2605,7 @@ Cola.Parser.prototype.make_unary = function (ctor, op, expr) { */ Cola.Parser.prototype.expr_op = function(left, min_prec, no_in, is_comp, rightest) { var op = this.is("operator") ? this.S.token.value : null, cop = Cola.COMPARISON(op); - if (op == "in" && no_in) op = null; + if ((op == "in" || op == "of") && no_in) op = null; var prec = op != null ? this.PRECEDENCE[op] : null; if (!this.is_js && is_comp && cop) { this.next(); diff --git a/lib/translate.js b/lib/translate.js index 92b1cd83..6025091f 100644 --- a/lib/translate.js +++ b/lib/translate.js @@ -2991,6 +2991,46 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ node = branches; } else + /* + for _ of [0...9] { + + } + + to + + var _ColaRuntime$$forOfHolder; + for (_ in [0...9]) { + _ = _ColaRuntime$$forOfHolder[_]; + } + + */ + if(node instanceof Cola.AST_ForOf){ + node = new Cola.AST_ForIn(node); + newNode = [new Cola.AST_Var({ + type : "dynamic", + definitions : [new Cola.AST_VarDef({ + type : "dynamic", + name : new Cola.AST_SymbolVar({ name: '_ColaRuntime$$forOfHolder' }) + })]}), node]; + + node.object = new Cola.AST_Assign({ + operator : '=', + left : new Cola.AST_SymbolRef({ name : "_ColaRuntime$$forOfHolder" }), + right : node.object + }); + + node.body.body.unshift(new Cola.AST_SimpleStatement({ body : new Cola.AST_Assign({ + operator : '=', + left : new Cola.AST_SymbolRef(node.name || node.init), + right : new Cola.AST_Sub({ + expression : new Cola.AST_SymbolRef({ name : "_ColaRuntime$$forOfHolder" }), + property : new Cola.AST_SymbolRef(node.name || node.init) + }) + })})); + + node = newNode; + } else + /* MyClass::prop = (){