diff --git a/lib/ast.js b/lib/ast.js index 0541dc89..358862b1 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -384,7 +384,7 @@ var AST_ArrowParametersOrSeq = DEFNODE("ArrowParametersOrSeq", "expressions", { as_params: function (croak) { // We don't want anything which doesn't belong in a destructuring var root = this; - return this.expressions.map(function to_fun_args(ex) { + return this.expressions.map(function to_fun_args(ex, _, __, default_seen_above) { if (ex instanceof AST_Object) { if (ex.properties.length == 0) croak("Invalid destructuring function parameter", ex.start.line, ex.start.col); @@ -392,6 +392,7 @@ var AST_ArrowParametersOrSeq = DEFNODE("ArrowParametersOrSeq", "expressions", { start: ex.start, end: ex.end, is_array: false, + default: default_seen_above, names: ex.properties.map(to_fun_args) }); } else if (ex instanceof AST_ObjectSymbol) { @@ -408,6 +409,7 @@ var AST_ArrowParametersOrSeq = DEFNODE("ArrowParametersOrSeq", "expressions", { } else if (ex instanceof AST_SymbolRef) { return new AST_SymbolFunarg({ name: ex.name, + default: default_seen_above, start: ex.start, end: ex.end }); @@ -420,8 +422,11 @@ var AST_ArrowParametersOrSeq = DEFNODE("ArrowParametersOrSeq", "expressions", { start: ex.start, end: ex.end, is_array: true, + default: default_seen_above, names: ex.elements.map(to_fun_args) }); + } else if (ex instanceof AST_Assign) { + return to_fun_args(ex.left, undefined, undefined, ex.right); } else { croak("Invalid function parameter", ex.start.line, ex.start.col); } @@ -485,7 +490,7 @@ var AST_Defun = DEFNODE("Defun", null, { }, AST_Lambda); /* -----[ DESTRUCTURING ]----- */ -var AST_Destructuring = DEFNODE("Destructuring", "names is_array", { +var AST_Destructuring = DEFNODE("Destructuring", "names is_array default", { $documentation: "A destructuring of several names. Used in destructuring assignment and with destructuring function argument names", _walk: function(visitor) { return visitor._visit(this, function(){ @@ -1011,10 +1016,16 @@ var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, { $documentation: "The name of a property accessor (setter/getter function)" }, AST_Symbol); -var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", { +var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init default", { $documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)", $propdoc: { - init: "[AST_Node*/S] array of initializers for this declaration." + init: "[AST_Node*/S] array of initializers for this declaration.", + default: "[AST_Expression] The default for this parameter. For example, `= 6`" + }, + _walk: function (visitor) { + return visitor._visit(this, function() { + if (this.default) this.default._walk(visitor); + }); } }, AST_Symbol); diff --git a/lib/output.js b/lib/output.js index 80f66e0e..c0b86b96 100644 --- a/lib/output.js +++ b/lib/output.js @@ -639,6 +639,12 @@ function OutputStream(options) { name.print(output); }) output.print(self.is_array ? "]" : "}"); + if (self.default) { + output.space(); + output.print('='); + output.space(); + self.default.print(output) + } }) DEFPRINT(AST_Debugger, function(self, output){ @@ -1275,9 +1281,21 @@ function OutputStream(options) { output.space(); self.value.print(output); }); - DEFPRINT(AST_Symbol, function(self, output){ - var def = self.definition(); - output.print_name(def ? def.mangled_name || def.name : self.name); + AST_Symbol.DEFMETHOD("_do_print", function(output){ + var def = this.definition(); + output.print_name(def ? def.mangled_name || def.name : this.name); + }); + DEFPRINT(AST_Symbol, function (self, output) { + self._do_print(output); + }); + DEFPRINT(AST_SymbolDeclaration, function(self, output){ + self._do_print(output); + if (self.default) { + output.space(); + output.print('='); + output.space(); + self.default.print(output) + } }); DEFPRINT(AST_ObjectSymbol, function(self, output){ var name = self.mangled_key || self.symbol.name; diff --git a/lib/parse.js b/lib/parse.js index 1a7bca94..8c8d19dd 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -1255,8 +1255,12 @@ function parse($TEXT, options) { })); next(); } else if (is("name")) { - children.push(_make_symbol(sym_type)); - next(); + children.push(new (sym_type)({ + name : String(S.token.value), + start : S.token, + default: (next(), is("operator", "=")) ? (next(), expression(false)) : undefined, + end : S.token + })); } else { children.push(expression()); } @@ -1483,7 +1487,21 @@ function parse($TEXT, options) { continue; } - if (!is("punc", ":")) { + if (is("operator", "=")) { + next(); + a.push(new AST_Assign({ + start: start, + // Symbol class doesn't matter. This is only meant to carry the symbol name into .as_params() since this is not normally valid. + left: new AST_SymbolRef({ + start: start, + end: start, + name: name + }), + operator: "=", + right: expression(false), + end: prev() + })); + } else if (!is("punc", ":")) { // It's one of those object destructurings, the value is its own name a.push(new AST_ObjectSymbol({ start: start, diff --git a/test/compress/harmony.js b/test/compress/harmony.js index e53f9458..c3f48b42 100644 --- a/test/compress/harmony.js +++ b/test/compress/harmony.js @@ -132,6 +132,25 @@ destructuring_arguments: { } } +default_arguments: { + input: { + function x(a = 6) { } + function x(a = (6 + 5)) { } + function x({ foo } = {}, [ bar ] = [ 1 ]) { } + } + expect_exact: "function x(a=6){}function x(a=6+5){}function x({foo}={},[bar]=[1]){}" +} + +default_values_in_destructurings: { + input: { + function x({a=(4), b}) {} + function x([b, c=(12)]) {} + var { x = (6), y } = x; + var [ x, y = (6) ] = x; + } + expect_exact: "function x({a=4,b}){}function x([b,c=12]){}var{x=6,y}=x;var[x,y=6]=x;" +} + concise_methods: { input: { x = {