a LabeledStatement should be in fact a StatementWithBody
authorMihai Bazon <mihai@bazon.net>
Mon, 3 Sep 2012 09:05:10 +0000 (12:05 +0300)
committerMihai Bazon <mihai@bazon.net>
Mon, 3 Sep 2012 09:05:10 +0000 (12:05 +0300)
This fixes output for:

    if (foo) {
        moo: if (bar) {
            break moo;
        }
    } else {
        baz();
    }

(the labeled statement must be outputted inside brackets)

lib/ast.js
lib/compress.js
lib/output.js
lib/parse.js
test/run-tests.js

index d0acbb4..c93ed4f 100644 (file)
@@ -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);
 
index 5dbe60b..04d5720 100644 (file)
@@ -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){
index 6d86a08..810a66a 100644 (file)
@@ -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);
index 416c363..9c3f3fb 100644 (file)
@@ -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() {
index 5201832..1f554e1 100755 (executable)
@@ -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;