From b7abd50b059f766c1685cee25a3af5d62327db02 Mon Sep 17 00:00:00 2001 From: jchbh Date: Wed, 20 May 2015 09:59:12 -0700 Subject: [PATCH 1/7] support generator function declare --- lib/ast.js | 3 ++- lib/output.js | 7 +++++++ lib/parse.js | 4 ++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/ast.js b/lib/ast.js index 2e539cff..1892a30a 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -359,9 +359,10 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", { } }, AST_Scope); -var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", { +var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments isgenerator", { $documentation: "Base class for functions", $propdoc: { + isgenerator: "is generatorFn or not", name: "[AST_SymbolDeclaration?] the name of this function", argnames: "[AST_SymbolFunarg*] array of function arguments", uses_arguments: "[boolean/S] tells whether this function accesses the arguments array" diff --git a/lib/output.js b/lib/output.js index 135636b9..2a8cc201 100644 --- a/lib/output.js +++ b/lib/output.js @@ -220,6 +220,10 @@ function OutputStream(options) { might_need_space = true; }; + var star = function(){ + print("*"); + } + var indent = options.beautify ? function(half) { if (options.beautify) { print(make_indent(half ? 0.5 : 0)); @@ -332,6 +336,7 @@ function OutputStream(options) { newline : newline, print : print, space : space, + star : star, comma : comma, colon : colon, last : function() { return last }, @@ -731,6 +736,8 @@ function OutputStream(options) { var self = this; if (!nokeyword) { output.print("function"); + if(this.isgenerator) + output.star(); } if (self.name) { output.space(); diff --git a/lib/parse.js b/lib/parse.js index 95a9f776..f6ea9b40 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -953,12 +953,16 @@ function parse($TEXT, options) { var function_ = function(ctor) { var in_statement = ctor === AST_Defun; + var isgenerator = is("operator", "*"); + if (isgenerator) + next(); var name = is("name") ? as_symbol(in_statement ? AST_SymbolDefun : AST_SymbolLambda) : null; if (in_statement && !name) unexpected(); expect("("); return new ctor({ name: name, + isgenerator: isgenerator, argnames: (function(first, a){ while (!is("punc", ")")) { if (first) first = false; else expect(","); From f8a9649c2a0cb3bc3af0580a72a56cf4c5bf37f1 Mon Sep 17 00:00:00 2001 From: jchbh Date: Wed, 20 May 2015 11:20:34 -0700 Subject: [PATCH 2/7] add support for yield and yield apply --- lib/parse.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/parse.js b/lib/parse.js index f6ea9b40..2d8a1a3a 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -44,7 +44,7 @@ "use strict"; -var 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'; +var KEYWORDS = 'break case catch const continue debugger default delete do else finally for function if in instanceof new return yield switch throw try typeof var let void while with'; var KEYWORDS_ATOM = 'false null true'; var RESERVED_WORDS = 'abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized this throws transient volatile yield' + " " + KEYWORDS_ATOM + " " + KEYWORDS; @@ -66,6 +66,7 @@ var OPERATORS = makePredicate([ "instanceof", "typeof", "new", + "yield", "void", "delete", "++", @@ -577,6 +578,7 @@ var UNARY_PREFIX = makePredicate([ "typeof", "void", "delete", + "yield", "--", "++", "!", @@ -1353,6 +1355,10 @@ function parse($TEXT, options) { var start = S.token; if (is("operator") && UNARY_PREFIX(start.value)) { next(); + if(start.type === "operator" && start.value === "yield" && is("operator", "*")) { + start.value = "yield*"; + next() + } handle_regexp(); var ex = make_unary(AST_UnaryPrefix, start.value, maybe_unary(allow_calls)); ex.start = start; From 906837dc9092554d0f484a6fb7b4ee3ea1565e0b Mon Sep 17 00:00:00 2001 From: jchbh Date: Wed, 20 May 2015 11:24:35 -0700 Subject: [PATCH 3/7] add let support --- lib/ast.js | 4 ++++ lib/output.js | 3 +++ lib/parse.js | 15 ++++++++++++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/ast.js b/lib/ast.js index 1892a30a..65a236e7 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -547,6 +547,10 @@ var AST_Const = DEFNODE("Const", null, { $documentation: "A `const` statement" }, AST_Definitions); +var AST_Let = DEFNODE("Let", null, { + $documentation: "A `let` statement" +}, AST_Definitions); + var AST_VarDef = DEFNODE("VarDef", "name value", { $documentation: "A variable declaration; only appears in a AST_Definitions node", $propdoc: { diff --git a/lib/output.js b/lib/output.js index 2a8cc201..c424a632 100644 --- a/lib/output.js +++ b/lib/output.js @@ -934,6 +934,9 @@ function OutputStream(options) { DEFPRINT(AST_Const, function(self, output){ self._do_print(output, "const"); }); + DEFPRINT(AST_Let, function(self, output){ + self._do_print(output, "let"); + }); function parenthesize_for_noin(node, output, noin) { if (!noin) node.print(output); diff --git a/lib/parse.js b/lib/parse.js index 2d8a1a3a..edeea0fa 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -844,6 +844,9 @@ function parse($TEXT, options) { case "var": return tmp = var_(), semicolon(), tmp; + case "let": + return tmp = let_(), semicolon(), tmp; + case "const": return tmp = const_(), semicolon(), tmp; @@ -916,7 +919,9 @@ function parse($TEXT, options) { if (!is("punc", ";")) { init = is("keyword", "var") ? (next(), var_(true)) - : expression(true, true); + : is("keyword", "let") + ? (next(), let_(true)) + :expression(true, true); if (is("operator", "in")) { if (init instanceof AST_Var && init.definitions.length > 1) croak("Only one variable declaration allowed in for..in loop"); @@ -1111,6 +1116,14 @@ function parse($TEXT, options) { }); }; + var let_ = function(no_in) { + return new AST_Let({ + start : prev(), + definitions : vardefs(no_in, false), + end : prev() + }); + }; + var new_ = function() { var start = S.token; expect_token("operator", "new"); From 89793f12cef8b0e3f8f00aecc5f1e55189767ddd Mon Sep 17 00:00:00 2001 From: jchbh Date: Wed, 20 May 2015 11:33:04 -0700 Subject: [PATCH 4/7] support for of --- lib/ast.js | 16 ++++++++++++++++ lib/output.js | 17 +++++++++++++++-- lib/parse.js | 25 ++++++++++++++++++++++--- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/lib/ast.js b/lib/ast.js index 65a236e7..bc631166 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -262,6 +262,22 @@ var AST_ForIn = DEFNODE("ForIn", "init name object", { } }, AST_IterationStatement); +var AST_ForOf = DEFNODE("ForOf", "init name object", { + $documentation: "A `for ... of` statement", + $propdoc: { + init: "[AST_Node] the `for/of` 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); + }); + } +}, AST_IterationStatement); + var AST_With = DEFNODE("With", "expression", { $documentation: "A `with` statement", $propdoc: { diff --git a/lib/output.js b/lib/output.js index c424a632..8271a6f7 100644 --- a/lib/output.js +++ b/lib/output.js @@ -721,6 +721,19 @@ function OutputStream(options) { output.space(); self._do_print_body(output); }); + DEFPRINT(AST_ForOf, function(self, output){ + output.print("for"); + output.space(); + output.with_parens(function(){ + self.init.print(output); + output.space(); + output.print("of"); + output.space(); + self.object.print(output); + }); + output.space(); + self._do_print_body(output); + }); DEFPRINT(AST_With, function(self, output){ output.print("with"); output.space(); @@ -923,7 +936,7 @@ function OutputStream(options) { def.print(output); }); var p = output.parent(); - var in_for = p instanceof AST_For || p instanceof AST_ForIn; + var in_for = p instanceof AST_For || p instanceof AST_ForIn || p instanceof AST_ForOf; var avoid_semicolon = in_for && p.init === this; if (!avoid_semicolon) output.semicolon(); @@ -961,7 +974,7 @@ function OutputStream(options) { output.print("="); output.space(); var p = output.parent(1); - var noin = p instanceof AST_For || p instanceof AST_ForIn; + var noin = p instanceof AST_For || p instanceof AST_ForIn || p instanceof AST_ForOf; parenthesize_for_noin(self.value, output, noin); } }); diff --git a/lib/parse.js b/lib/parse.js index edeea0fa..0323d2cc 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -44,7 +44,7 @@ "use strict"; -var KEYWORDS = 'break case catch const continue debugger default delete do else finally for function if in instanceof new return yield switch throw try typeof var let void while with'; +var KEYWORDS = 'break case catch const continue debugger default delete do else finally for function if in of instanceof new return yield switch throw try typeof var let void while with'; var KEYWORDS_ATOM = 'false null true'; var RESERVED_WORDS = 'abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized this throws transient volatile yield' + " " + KEYWORDS_ATOM + " " + KEYWORDS; @@ -63,6 +63,7 @@ var RE_DEC_NUMBER = /^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i; var OPERATORS = makePredicate([ "in", + "of", "instanceof", "typeof", "new", @@ -607,7 +608,7 @@ var PRECEDENCE = (function(a, ret){ ["^"], ["&"], ["==", "===", "!=", "!=="], - ["<", ">", "<=", ">=", "in", "instanceof"], + ["<", ">", "<=", ">=", "in", "of", "instanceof"], [">>", "<<", ">>>"], ["+", "-"], ["*", "/", "%"] @@ -928,6 +929,12 @@ function parse($TEXT, options) { next(); return for_in(init); } + if (is("operator", "of")) { + if (init instanceof AST_Var && init.definitions.length > 1) + croak("Only one variable declaration allowed in for..of loop"); + next(); + return for_of(init); + } } return regular_for(init); }; @@ -958,6 +965,18 @@ function parse($TEXT, options) { }); }; + function for_of(init){ + var lhs = init instanceof AST_Var ? init.definitions[0].name : null; + var obj = expression(true); + expect(")"); + return new AST_ForOf({ + init : init, + name : lhs, + object : obj, + body : in_loop(statement) + }); + } + var function_ = function(ctor) { var in_statement = ctor === AST_Defun; var isgenerator = is("operator", "*"); @@ -1396,7 +1415,7 @@ function parse($TEXT, options) { var expr_op = function(left, min_prec, no_in) { var op = is("operator") ? S.token.value : null; - if (op == "in" && no_in) op = null; + if ((op == "in" || op == "of")&& no_in) op = null; var prec = op != null ? PRECEDENCE[op] : null; if (prec != null && prec > min_prec) { next(); From 014f873ec1b5069b944cea615b96d0cf1626e7a1 Mon Sep 17 00:00:00 2001 From: jchbh Date: Fri, 22 May 2015 10:02:41 -0700 Subject: [PATCH 5/7] remove of from operator. need more test. --- lib/parse.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/parse.js b/lib/parse.js index f1874b30..4347161c 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -44,7 +44,7 @@ "use strict"; -var KEYWORDS = 'break case catch const continue debugger default delete do else finally for function if in of instanceof new return yield switch throw try typeof var let void while with'; +var KEYWORDS = 'break case catch const continue debugger default delete do else finally for function if in instanceof new return yield switch throw try typeof var let void while with'; var KEYWORDS_ATOM = 'false null true'; var RESERVED_WORDS = 'abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized this throws transient volatile yield' + " " + KEYWORDS_ATOM + " " + KEYWORDS; @@ -63,7 +63,6 @@ var RE_DEC_NUMBER = /^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i; var OPERATORS = makePredicate([ "in", - "of", "instanceof", "typeof", "new", @@ -613,7 +612,7 @@ var PRECEDENCE = (function(a, ret){ ["^"], ["&"], ["==", "===", "!=", "!=="], - ["<", ">", "<=", ">=", "in", "of", "instanceof"], + ["<", ">", "<=", ">=", "in", "instanceof"], [">>", "<<", ">>>"], ["+", "-"], ["*", "/", "%"] @@ -934,7 +933,7 @@ function parse($TEXT, options) { next(); return for_in(init); } - if (is("operator", "of")) { + if (is("name", "of")) { if (init instanceof AST_Var && init.definitions.length > 1) croak("Only one variable declaration allowed in for..of loop"); next(); @@ -1420,7 +1419,7 @@ function parse($TEXT, options) { var expr_op = function(left, min_prec, no_in) { var op = is("operator") ? S.token.value : null; - if ((op == "in" || op == "of")&& no_in) op = null; + if (op == "in"&& no_in) op = null; var prec = op != null ? PRECEDENCE[op] : null; if (prec != null && prec > min_prec) { next(); From 426cebc0e1aa4045aa25d30b944496d78d9f5059 Mon Sep 17 00:00:00 2001 From: jchbh Date: Fri, 29 May 2015 01:49:42 -0700 Subject: [PATCH 6/7] fix bug when ||, && then yield, should has () wrap the code. --- lib/output.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/output.js b/lib/output.js index cb743bd6..e539bc1b 100644 --- a/lib/output.js +++ b/lib/output.js @@ -1068,7 +1068,10 @@ function OutputStream(options) { // the space is optional depending on "beautify" output.space(); } + var isYield = (self.right.operator == "yield" || self.right.operator === "yield*"); + isYield && output.print("("); self.right.print(output); + isYield && output.print(")"); }); DEFPRINT(AST_Conditional, function(self, output){ self.condition.print(output); From 9715ef854472607942b5d6f9ad67fe2f50637f97 Mon Sep 17 00:00:00 2001 From: jchbh Date: Fri, 29 May 2015 09:51:56 -0700 Subject: [PATCH 7/7] maybe left side also need wrap to keep order. --- lib/output.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/output.js b/lib/output.js index e539bc1b..f5040050 100644 --- a/lib/output.js +++ b/lib/output.js @@ -1053,7 +1053,10 @@ function OutputStream(options) { output.print(self.operator); }); DEFPRINT(AST_Binary, function(self, output){ + var isYield = (self.left.operator == "yield" || self.left.operator === "yield*"); + isYield && output.print("("); self.left.print(output); + isYield && output.print(")"); output.space(); output.print(self.operator); if (self.operator == "<" @@ -1068,7 +1071,7 @@ function OutputStream(options) { // the space is optional depending on "beautify" output.space(); } - var isYield = (self.right.operator == "yield" || self.right.operator === "yield*"); + isYield = (self.right.operator == "yield" || self.right.operator === "yield*"); isYield && output.print("("); self.right.print(output); isYield && output.print(")");