From d7c1dc6c0578b55eefc3bc94556146dce6e1a8cb Mon Sep 17 00:00:00 2001 From: Mihai Bazon Date: Mon, 3 Sep 2012 12:05:10 +0300 Subject: [PATCH] a LabeledStatement should be in fact a StatementWithBody This fixes output for: if (foo) { moo: if (bar) { break moo; } } else { baz(); } (the labeled statement must be outputted inside brackets) --- lib/ast.js | 69 ++++++++++++++++++++++++++++++++++------------- lib/compress.js | 4 +-- lib/output.js | 2 +- lib/parse.js | 2 +- test/run-tests.js | 4 +-- 5 files changed, 57 insertions(+), 24 deletions(-) diff --git a/lib/ast.js b/lib/ast.js index d0acbb41..c93ed4f3 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -41,6 +41,8 @@ ***********************************************************************/ +var NODE_HIERARCHY = {}; + function DEFNODE(type, props, methods, base) { if (arguments.length < 4) base = AST_Node; if (!props) props = []; @@ -73,6 +75,10 @@ function DEFNODE(type, props, methods, base) { ctor.DEFMETHOD = function(name, method) { this.prototype[name] = method; }; + NODE_HIERARCHY[type] = { + def: ctor, + base: base + }; return ctor; }; @@ -108,16 +114,6 @@ var AST_Directive = DEFNODE("Directive", "value", { /* -----[ loops ]----- */ -var AST_LabeledStatement = DEFNODE("LabeledStatement", "label statement", { - $documentation: "Statement with a label", - _walk: function(visitor) { - return visitor._visit(this, function(){ - this.label._walk(visitor); - this.statement._walk(visitor); - }); - } -}); - var AST_Statement = DEFNODE("Statement", "body", { $documentation: "Base class of all statements", _walk: function(visitor) { @@ -153,6 +149,16 @@ var AST_StatementWithBody = DEFNODE("StatementWithBody", null, { $documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`." }, AST_Statement); +var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", { + $documentation: "Statement with a label", + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.label._walk(visitor); + this.body._walk(visitor); + }); + } +}, AST_StatementWithBody); + var AST_DWLoop = DEFNODE("DWLoop", "condition", { $documentation: "Base class for do/while statements.", _walk: function(visitor) { @@ -434,9 +440,11 @@ var AST_Seq = DEFNODE("Seq", "first second", { }); var AST_PropAccess = DEFNODE("PropAccess", "expression property", { + $documentation: "Base class for property access expressions, i.e. `a.foo` or `a[\"foo\"]`" }); var AST_Dot = DEFNODE("Dot", null, { + $documentation: "A dotted property access expression", _walk: function(visitor) { return visitor._visit(this, function(){ this.expression._walk(visitor); @@ -445,6 +453,7 @@ var AST_Dot = DEFNODE("Dot", null, { }, AST_PropAccess); var AST_Sub = DEFNODE("Sub", null, { + $documentation: "Index-style property access, i.e. `a[\"foo\"]`", _walk: function(visitor) { return visitor._visit(this, function(){ this.expression._walk(visitor); @@ -454,6 +463,7 @@ var AST_Sub = DEFNODE("Sub", null, { }, AST_PropAccess); var AST_Unary = DEFNODE("Unary", "operator expression", { + $documentation: "Base class for unary expressions", _walk: function(visitor) { return visitor._visit(this, function(){ this.expression._walk(visitor); @@ -462,12 +472,15 @@ var AST_Unary = DEFNODE("Unary", "operator expression", { }); var AST_UnaryPrefix = DEFNODE("UnaryPrefix", null, { + $documentation: "Unary prefix expression, i.e. `typeof i` or `++i`" }, AST_Unary); var AST_UnaryPostfix = DEFNODE("UnaryPostfix", null, { + $documentation: "Unary postfix expression, i.e. `i++`" }, AST_Unary); var AST_Binary = DEFNODE("Binary", "left operator right", { + $documentation: "Binary expression, i.e. `a + b`", _walk: function(visitor) { return visitor._visit(this, function(){ this.left._walk(visitor); @@ -477,6 +490,7 @@ var AST_Binary = DEFNODE("Binary", "left operator right", { }); var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative", { + $documentation: "Conditional expression using the ternary operator, i.e. `a ? b : c`", _walk: function(visitor) { return visitor._visit(this, function(){ this.condition._walk(visitor); @@ -487,11 +501,13 @@ var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative", }); var AST_Assign = DEFNODE("Assign", "left operator right", { + $documentation: "An assignment expression — `a = b + 5`", }, AST_Binary); /* -----[ LITERALS ]----- */ var AST_Array = DEFNODE("Array", "elements", { + $documentation: "An array literal", _walk: function(visitor) { return visitor._visit(this, function(){ this.elements.forEach(function(el){ @@ -502,6 +518,7 @@ var AST_Array = DEFNODE("Array", "elements", { }); var AST_Object = DEFNODE("Object", "properties", { + $documentation: "An object literal", _walk: function(visitor) { return visitor._visit(this, function(){ this.properties.forEach(function(prop){ @@ -512,6 +529,7 @@ var AST_Object = DEFNODE("Object", "properties", { }); var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", { + $documentation: "Base class for literal object properties", _walk: function(visitor) { return visitor._visit(this, function(){ this.value._walk(visitor); @@ -520,89 +538,104 @@ var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", { }); var AST_ObjectKeyVal = DEFNODE("ObjectKeyval", null, { + $documentation: "A key: value object property", }, AST_ObjectProperty); var AST_ObjectSetter = DEFNODE("ObjectSetter", null, { + $documentation: "An object setter property", }, AST_ObjectProperty); var AST_ObjectGetter = DEFNODE("ObjectGetter", null, { + $documentation: "An object getter property", }, AST_ObjectProperty); var AST_Symbol = DEFNODE("Symbol", "scope name global undeclared", { + $documentation: "Base class for all symbols", }); var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "references", { + $documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)", }, AST_Symbol); var AST_SymbolVar = DEFNODE("SymbolVar", null, { - $documentation: "Symbol defining a variable or constant" + $documentation: "Symbol defining a variable or constant", }, AST_SymbolDeclaration); var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, { - $documentation: "Symbol naming a function argument" + $documentation: "Symbol naming a function argument", }, AST_SymbolVar); var AST_SymbolDefun = DEFNODE("SymbolDefun", null, { - $documentation: "Symbol defining a function" + $documentation: "Symbol defining a function", }, AST_SymbolDeclaration); var AST_SymbolLambda = DEFNODE("SymbolLambda", null, { - $documentation: "Symbol naming a function expression" + $documentation: "Symbol naming a function expression", }, AST_SymbolDeclaration); var AST_SymbolCatch = DEFNODE("SymbolCatch", null, { - $documentation: "Symbol naming the exception in catch" + $documentation: "Symbol naming the exception in catch", }, AST_SymbolDeclaration); var AST_Label = DEFNODE("Label", null, { - $documentation: "Symbol naming a label (declaration)" + $documentation: "Symbol naming a label (declaration)", }, AST_SymbolDeclaration); var AST_SymbolRef = DEFNODE("SymbolRef", "symbol", { - $documentation: "Reference to some symbol (not definition/declaration)" + $documentation: "Reference to some symbol (not definition/declaration)", }, AST_Symbol); var AST_LabelRef = DEFNODE("LabelRef", null, { - $documentation: "Reference to a label symbol" + $documentation: "Reference to a label symbol", }, AST_SymbolRef); var AST_This = DEFNODE("This", null, { + $documentation: "The `this` symbol", }, AST_Symbol); var AST_Constant = DEFNODE("Constant", null, { + $documentation: "Base class for all constants", getValue: function() { return this.value; } }); var AST_String = DEFNODE("String", "value", { + $documentation: "A string literal", }, AST_Constant); var AST_Number = DEFNODE("Number", "value", { + $documentation: "A number literal", }, AST_Constant); var AST_RegExp = DEFNODE("Regexp", "pattern mods", { + $documentation: "A regexp literal", initialize: function() { this.value = new RegExp(this.pattern, this.mods); } }, AST_Constant); var AST_Atom = DEFNODE("Atom", null, { + $documentation: "Base class for atoms", }, AST_Constant); var AST_Null = DEFNODE("Null", null, { + $documentation: "The `null` atom", value: null }, AST_Atom); var AST_Undefined = DEFNODE("Undefined", null, { + $documentation: "The `undefined` value", value: (function(){}()) }, AST_Atom); var AST_False = DEFNODE("False", null, { + $documentation: "The `false` atom", value: false }, AST_Atom); var AST_True = DEFNODE("True", null, { + $documentation: "The `true` atom", value: true }, AST_Atom); diff --git a/lib/compress.js b/lib/compress.js index 5dbe60b0..04d5720b 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -113,8 +113,8 @@ function Compressor(options, false_by_default) { SQUEEZE(AST_LabeledStatement, function(self, compressor){ self = self.clone(); - self.statement = self.statement.squeeze(compressor); - return self.label.references.length == 0 ? self.statement : self; + self.body = self.body.squeeze(compressor); + return self.label.references.length == 0 ? self.body : self; }); SQUEEZE(AST_Statement, function(self, compressor){ diff --git a/lib/output.js b/lib/output.js index 6d86a087..810a66a1 100644 --- a/lib/output.js +++ b/lib/output.js @@ -457,7 +457,7 @@ function OutputStream(options) { DEFPRINT(AST_LabeledStatement, function(self, output){ self.label.print(output); output.colon(); - self.statement.print(output); + self.body.print(output); }); DEFPRINT(AST_SimpleStatement, function(self, output){ self.body.print(output); diff --git a/lib/parse.js b/lib/parse.js index 416c363a..9c3f3fb1 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -892,7 +892,7 @@ function parse($TEXT, exigent_mode) { S.labels.push(label); var start = S.token, stat = statement(); S.labels.pop(); - return new AST_LabeledStatement({ statement: stat, label: label }); + return new AST_LabeledStatement({ body: stat, label: label }); }; function simple_statement() { diff --git a/test/run-tests.js b/test/run-tests.js index 52018329..1f554e11 100755 --- a/test/run-tests.js +++ b/test/run-tests.js @@ -81,7 +81,7 @@ function parse_test(file) { if (node instanceof U.AST_LabeledStatement && tw.parent() instanceof U.AST_Toplevel) { var name = node.label.name; - tests[name] = get_one_test(name, node.statement); + tests[name] = get_one_test(name, node.body); return true; } if (!(node instanceof U.AST_Toplevel)) croak(node); @@ -118,7 +118,7 @@ function parse_test(file) { col: node.label.start.col }) ); - var stat = node.statement; + var stat = node.body; if (stat instanceof U.AST_BlockStatement) stat.required = 1; test[node.label.name] = stat; -- 2.34.1