codegen and dropped the useless walker
authorMihai Bazon <mihai@bazon.net>
Thu, 16 Aug 2012 15:11:04 +0000 (18:11 +0300)
committerMihai Bazon <mihai@bazon.net>
Thu, 16 Aug 2012 15:11:04 +0000 (18:11 +0300)
lib/ast.js
lib/node.js
lib/output.js
lib/parse.js

index b8b6b07..c5be5f6 100644 (file)
@@ -23,6 +23,9 @@ function DEFNODE(type, props, methods, base) {
     if (methods) for (i in methods) if (HOP(methods, i)) {
         ctor.prototype[i] = methods[i];
     }
+    ctor.DEFMETHOD = function(name, method) {
+        this.prototype[name] = method;
+    };
     return ctor;
 };
 
@@ -34,66 +37,33 @@ var AST_Node = DEFNODE("Node", "start end", {
     clone: function() {
         return new this.CTOR(this);
     },
-    // XXX: what was this for?
-    // renew: function(args) {
-    //     var ctor = this.CTOR, props = ctor.props;
-    //     for (var i in props) if (!HOP(args, i)) args[i] = this[i];
-    //     return new ctor(args);
-    // },
-    walk: function(w) {
-        w._visit(this);
-    }
 }, null);
 
 var AST_Directive = DEFNODE("Directive", "value", {
-    print: function(output) {
-        output.string(this.value);
-    }
+
 });
 
 var AST_Debugger = DEFNODE("Debugger", null, {
-    print: function(output) {
-        output.print("debugger");
-    }
+
 });
 
 var AST_Parenthesized = DEFNODE("Parenthesized", "expression", {
     $documentation: "Represents an expression which is always parenthesized.  Used for the \
 conditions in IF/WHILE/DO and expression in SWITCH/WITH.",
-    walk: function(w) {
-        w._visit(this, function(){
-            this.expression.walk(w);
-        });
-    }
 });
 
 var AST_Bracketed = DEFNODE("Bracketed", "body", {
     $documentation: "Represents a block of statements that are always included in brackets. \
 Used for bodies of FUNCTION/TRY/CATCH/THROW/SWITCH.",
-    walk: function(w) {
-        w._visit(this, function(){
-            this.body.forEach(function(stat){
-                stat.walk(w);
-            });
-        });
-    }
 });
 
 /* -----[ loops ]----- */
 
-var AST_Statement = DEFNODE("Statement", "label body", {
-    walk: function(w) {
-        w._visit(this, function(){
-            if (this.label) this.label.walk(w);
-            if (this.body) {
-                if (this.body instanceof AST_Node)
-                    this.body.walk(w);
-                else
-                    this.walk_array(w);
-            }
-        });
-    },
-    walk_array: AST_Bracketed.prototype.walk
+var AST_LabeledStatement = DEFNODE("LabeledStatement", "label statement", {
+    
+});
+
+var AST_Statement = DEFNODE("Statement", "body", {
 });
 
 var AST_SimpleStatement = DEFNODE("SimpleStatement", null, {
@@ -109,64 +79,23 @@ var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
 }, AST_Statement);
 
 var AST_Do = DEFNODE("Do", "condition", {
-    walk: function(w) {
-        w._visit(this, function(){
-            this.condition.walk(w);
-            AST_Statement.prototype.walk.call(this, w);
-        });
-    }
 }, AST_Statement);
 
 var AST_While = DEFNODE("While", "condition", {
-    walk: function(w) {
-        w._visit(this, function(){
-            this.condition.walk(w);
-            AST_Statement.prototype.walk.call(this, w);
-        });
-    }
 }, AST_Statement);
 
 var AST_For = DEFNODE("For", "init condition step", {
-    walk: function(w) {
-        w._visit(this, function(){
-            if (this.init) this.init.walk(w);
-            if (this.condition) this.condition.walk(w);
-            if (this.step) this.step.walk(w);
-            AST_Statement.prototype.walk.call(this, w);
-        });
-    }
 }, AST_Statement);
 
 var AST_ForIn = DEFNODE("ForIn", "init name object", {
-    walk: function(w) {
-        w._visit(this, function(){
-            if (this.init) this.init.walk(w);
-            this.object.walk(w);
-            AST_Statement.prototype.walk.call(this, w);
-        });
-    }
 }, AST_Statement);
 
 var AST_With = DEFNODE("With", "expression", {
-    walk: function(w) {
-        w._visit(this, function(){
-            this.expression.walk(w);
-            AST_Statement.prototype.walk.call(this, w);
-        });
-    }
 }, AST_Statement);
 
 /* -----[ functions ]----- */
 
 var AST_Scope = DEFNODE("Scope", "identifiers", {
-    walk: function(w) {
-        w._visit(this, function(){
-            if (this.identifiers) this.identifiers.forEach(function(el){
-                el.walk(w);
-            });
-            AST_Statement.prototype.walk.call(this, w);
-        });
-    }
 }, AST_Statement);
 
 var AST_Toplevel = DEFNODE("Toplevel", null, {
@@ -174,15 +103,6 @@ var AST_Toplevel = DEFNODE("Toplevel", null, {
 }, AST_Scope);
 
 var AST_Lambda = DEFNODE("Lambda", "name argnames", {
-    walk: function(w) {
-        w._visit(this, function(){
-            if (this.name) this.name.walk(w);
-            this.argnames.forEach(function(el){
-                el.walk(w);
-            });
-            AST_Scope.prototype.walk.call(this, w);
-        });
-    }
 }, AST_Scope);
 
 var AST_Function = DEFNODE("Function", null, {
@@ -200,11 +120,6 @@ var AST_Jump = DEFNODE("Jump", null, {
 });
 
 var AST_Exit = DEFNODE("Exit", "value", {
-    walk: function(w) {
-        w._visit(this, function(){
-            if (this.value) this.value.walk(w);
-        });
-    }
 }, AST_Jump);
 
 var AST_Return = DEFNODE("Return", null, {
@@ -216,11 +131,6 @@ var AST_Throw = DEFNODE("Throw", null, {
 }, AST_Exit);
 
 var AST_LoopControl = DEFNODE("LoopControl", "label", {
-    walk: function(w) {
-        w._visit(this, function(){
-            if (this.label) this.label.walk(w);
-        });
-    }
 }, AST_Jump);
 
 var AST_Break = DEFNODE("Break", null, {
@@ -234,34 +144,17 @@ var AST_Continue = DEFNODE("Continue", null, {
 /* -----[ IF ]----- */
 
 var AST_If = DEFNODE("If", "condition consequent alternative", {
-    walk: function(w) {
-        w._visit(this, function(){
-            this.condition.walk(w);
-            this.consequent.walk(w);
-            if (this.alternative) this.alternative.walk(w);
-        });
-    }
 });
 
 /* -----[ SWITCH ]----- */
 
 var AST_Switch = DEFNODE("Switch", "expression", {
-    walk: function(w) {
-        w._visit(this, function(){
-            this.expression.walk(w);
-            AST_Statement.prototype.walk.call(this, w);
-        });
-    }
 }, AST_Statement);
 
 var AST_SwitchBlock = DEFNODE("SwitchBlock", null, {
-    walk       : AST_Statement.prototype.walk,
-    walk_array : AST_Bracketed.prototype.walk
 }, AST_Bracketed);
 
 var AST_SwitchBranch = DEFNODE("SwitchBranch", "body", {
-    walk       : AST_Statement.prototype.walk,
-    walk_array : AST_Bracketed.prototype.walk
 });
 
 var AST_Default = DEFNODE("Default", null, {
@@ -269,49 +162,22 @@ var AST_Default = DEFNODE("Default", null, {
 }, AST_SwitchBranch);
 
 var AST_Case = DEFNODE("Case", "expression", {
-    walk: function(w) {
-        w._visit(this, function(){
-            this.expression.walk(w);
-            AST_Statement.prototype.walk.call(this, w);
-        });
-    }
 }, AST_SwitchBranch);
 
 /* -----[ EXCEPTIONS ]----- */
 
 var AST_Try = DEFNODE("Try", "btry bcatch bfinally", {
-    walk: function(w) {
-        w._visit(this, function(){
-            this.btry.walk(w);
-            if (this.bcatch) this.bcatch.walk(w);
-            if (this.bfinally) this.bfinally.walk(w);
-        });
-    }
 });
 
 var AST_Catch = DEFNODE("Catch", "argname body", {
-    walk: function(w) {
-        w._visit(this, function(){
-            this.argname.walk(w);
-            this.body.walk(w);
-        });
-    }
 });
 
-var AST_Finally = DEFNODE("Finally", null, {
-
-}, AST_Bracketed);
+var AST_Finally = DEFNODE("Finally", "body", {
+});
 
 /* -----[ VAR/CONST ]----- */
 
 var AST_Definitions = DEFNODE("Definitions", "definitions", {
-    walk: function(w) {
-        w._visit(this, function(){
-            this.definitions.forEach(function(el){
-                el.walk(w);
-            });
-        });
-    }
 });
 
 var AST_Var = DEFNODE("Var", null, {
@@ -323,25 +189,11 @@ var AST_Const = DEFNODE("Const", null, {
 }, AST_Definitions);
 
 var AST_VarDef = DEFNODE("VarDef", "name value", {
-    walk: function(w) {
-        w._visit(this, function(){
-            this.name.walk(w);
-            if (this.value) this.value.walk(w);
-        });
-    }
 });
 
 /* -----[ OTHER ]----- */
 
 var AST_Call = DEFNODE("Call", "expression args", {
-    walk: function(w) {
-        w._visit(this, function(){
-            this.expression.walk(w);
-            this.args.forEach(function(el){
-                el.walk(w);
-            });
-        });
-    }
 });
 
 var AST_New = DEFNODE("New", null, {
@@ -349,12 +201,6 @@ var AST_New = DEFNODE("New", null, {
 }, AST_Call);
 
 var AST_Seq = DEFNODE("Seq", "first second", {
-    walk: function(w) {
-        w._visit(this, function(){
-            this.first.walk(w);
-            this.second.walk(w);
-        });
-    }
 });
 
 var AST_PropAccess = DEFNODE("PropAccess", "expression property", {
@@ -362,28 +208,12 @@ var AST_PropAccess = DEFNODE("PropAccess", "expression property", {
 });
 
 var AST_Dot = DEFNODE("Dot", null, {
-    walk: function(w) {
-        w._visit(this, function(){
-            this.expression.walk(w);
-        });
-    }
 }, AST_PropAccess);
 
 var AST_Sub = DEFNODE("Sub", null, {
-    walk: function(w) {
-        w._visit(this, function(){
-            this.expression.walk(w);
-            this.property.walk(w);
-        });
-    }
 }, AST_PropAccess);
 
 var AST_Unary = DEFNODE("Unary", "operator expression", {
-    walk: function(w) {
-        w._visit(this, function(){
-            this.expression.walk(w);
-        });
-    }
 });
 
 var AST_UnaryPrefix = DEFNODE("UnaryPrefix", null, {
@@ -395,22 +225,9 @@ var AST_UnaryPostfix = DEFNODE("UnaryPostfix", null, {
 }, AST_Unary);
 
 var AST_Binary = DEFNODE("Binary", "left operator right", {
-    walk: function(w) {
-        w._visit(this, function(){
-            this.left.walk(w);
-            this.right.walk(w);
-        });
-    }
 });
 
 var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative", {
-    walk: function(w) {
-        w._visit(this, function(){
-            this.condition.walk(w);
-            this.consequent.walk(w);
-            this.alternative.walk(w);
-        });
-    }
 });
 
 var AST_Assign = DEFNODE("Assign", null, {
@@ -420,49 +237,20 @@ var AST_Assign = DEFNODE("Assign", null, {
 /* -----[ LITERALS ]----- */
 
 var AST_Array = DEFNODE("Array", "elements", {
-    walk: function(w) {
-        w._visit(this, function(){
-            this.elements.forEach(function(el){
-                el.walk(w);
-            });
-        });
-    }
 });
 
 var AST_Object = DEFNODE("Object", "properties", {
-    walk: function(w) {
-        w._visit(this, function(){
-            this.properties.forEach(function(prop){
-                prop.walk(w);
-            });
-        });
-    }
 });
 
 var AST_ObjectProperty = DEFNODE("ObjectProperty");
 
 var AST_ObjectKeyVal = DEFNODE("ObjectKeyval", "key value", {
-    walk: function(w) {
-        w._visit(this, function(){
-            this.value.walk(w);
-        });
-    }
 }, AST_ObjectProperty);
 
 var AST_ObjectSetter = DEFNODE("ObjectSetter", "name func", {
-    walk: function(w) {
-        w._visit(this, function(){
-            this.func.walk(w);
-        });
-    }
 }, AST_ObjectProperty);
 
 var AST_ObjectGetter = DEFNODE("ObjectGetter", "name func", {
-    walk: function(w) {
-        w._visit(this, function(){
-            this.func.walk(w);
-        });
-    }
 }, AST_ObjectProperty);
 
 var AST_Symbol = DEFNODE("Symbol", "name", {
@@ -519,31 +307,3 @@ var AST_False = DEFNODE("False", null, {
 var AST_True = DEFNODE("True", null, {
     value: true
 }, AST_Atom);
-
-/* -----[ Walker ]----- */
-
-function TreeWalker(visitor) {
-    this.stack = [];
-    if (visitor) this.visit = visitor;
-};
-
-TreeWalker.prototype = {
-    visit: function(node){},
-    parent: function(n) {
-        if (n == null) n = 1;
-        return this.stack[this.stack.length - n];
-    },
-    find_parent: function(type) {
-        for (var a = this.stack, i = a.length; --i >= 0;)
-            if (a[i] instanceof type) return a[i];
-        return null;
-    },
-    _visit: function(node, descend) {
-        this.visit(node);
-        if (descend) {
-            this.stack.push(node);
-            descend.call(node);
-            this.stack.pop();
-        }
-    }
-};
index 571cdd8..9dba3c1 100755 (executable)
@@ -12,9 +12,9 @@
     };
 
     load_global("./utils.js");
-    load_global("./output.js");
     load_global("./ast.js");
     load_global("./parse.js");
+    load_global("./output.js");
 
     ///
 
     var ast = parse(fs.readFileSync(filename, "utf8"));
     console.timeEnd("parse");
 
-    console.time("walk");
-    var w = new TreeWalker(function(node){
-        console.log(node.TYPE + " [ start: " + node.start.line + ":" + node.start.col + ", end: " + node.end.line + ":" + node.end.col + "]");
-    });
-    ast.walk(w);
-    console.timeEnd("walk");
+    // console.time("walk");
+    // var w = new TreeWalker(function(node){
+    //     console.log(node.TYPE + " [ start: " + node.start.line + ":" + node.start.col + ", end: " + node.end.line + ":" + node.end.col + "] " + node.name);
+    // });
+    // ast.walk(w);
+    // console.timeEnd("walk");
+
+    //console.log(JSON.stringify(ast));
 
 })();
index cf17c3e..52ab39d 100644 (file)
@@ -6,12 +6,16 @@ function OutputStream(options) {
         space_colon   : false,
         ascii_only    : false,
         inline_script : false,
-        width         : 80
+        width         : 80,
+        beautify      : true
     });
 
+    function noop() {};
+
     var indentation = 0;
     var current_col = 0;
     var current_line = 0;
+    var current_pos = 0;
     var OUTPUT = "";
 
     function to_ascii(str) {
@@ -44,13 +48,6 @@ function OutputStream(options) {
         else return '"' + str.replace(/\x22/g, '\\"') + '"';
     };
 
-    function print(str) {
-        var a = str.split(/\r?\n/), n = a.length;
-        current_line += n;
-        current_col += a[n - 1].length;
-        OUTPUT += str;
-    };
-
     function encode_string(str) {
         var ret = make_string(str);
         if (options.inline_script)
@@ -72,60 +69,452 @@ function OutputStream(options) {
         return line;
     };
 
-    function with_indent(col, cont) {
+    function last_char() {
+        return OUTPUT.charAt(OUTPUT.length - 1);
+    };
+
+    /* -----[ beautification/minification ]----- */
+
+    var might_need_space = false;
+
+    function print(str) {
+        if (might_need_space) {
+            var ch = str.charAt(0);
+            if ((is_identifier_char(last_char()) && (is_identifier_char(ch) || ch == "\\"))
+                || (/[\+\-]$/.test(OUTPUT) && /^[\+\-]/.test(str)))
+            {
+                OUTPUT += " ";
+                current_col++;
+                current_pos++;
+            }
+        }
+        might_need_space = false;
+        var a = str.split(/\r?\n/), n = a.length;
+        current_line += n;
+        current_col += a[n - 1].length;
+        current_pos += str.length;
+        OUTPUT += str;
+    };
+
+    var space = options.beautify ? function() {
+        print(" ");
+    } : function() {
+        might_need_space = true;
+    };
+
+    var indent = options.beautify ? function() {
+        if (options.beautify) {
+            print(make_indent());
+        }
+    } : noop;
+
+    var with_indent = options.beautify ? function(col, cont) {
         if (col === true) col = next_indent();
         var save_indentation = indentation;
         indentation = col;
         var ret = cont();
         indentation = save_indentation;
         return ret;
-    };
+    } : function(col, cont) { return cont() };
 
-    function indent() {
-        print(make_indent());
-    };
-
-    function newline() {
+    var newline = options.indent ? function() {
         print("\n");
-    };
+    } : noop;
 
     function next_indent() {
         return indentation + options.indent_level;
     };
 
-    function with_block(cont, beautify) {
+    function with_block(cont) {
         var ret;
         print("{");
+        newline();
         with_indent(next_indent(), function(){
-            if (beautify) newline();
             ret = cont();
-            if (beautify) newline();
         });
-        if (beautify) indent();
+        newline();
+        indent();
         print("}");
         return ret;
     };
 
-    function with_parens(cont, beautify) {
+    function with_parens(cont) {
         print("(");
         var ret = with_indent(current_col, cont);
         print(")");
         return ret;
     };
 
+    function with_square(cont) {
+        print("[");
+        var ret = with_indent(current_col, cont);
+        print("]");
+        return ret;
+    };
+
+    function semicolon() {
+        print(";");
+    };
+
+    function comma() {
+        print(",");
+        space();
+    };
+
+    function colon() {
+        print(":");
+        space();
+    };
+
     return {
-        get         : function() { return OUTPUT },
-        indent      : indent,
-        newline     : newline,
-        print       : print,
-        string      : function(str) { print(encode_string(str)) },
-        with_indent : with_indent,
-        with_block  : with_block,
-        with_parens : with_parens,
-        options     : function() { return options },
-        line        : function() { return current_line },
-        col         : function() { return current_col },
-        pos         : function() { return OUTPUT.length }
+        get          : function() { return OUTPUT },
+        indent       : indent,
+        newline      : newline,
+        print        : print,
+        space        : space,
+        comma        : comma,
+        colon        : colon,
+        print_name   : function(name) { print(make_name(name)) },
+        print_string : function(str) { print(encode_string(str)) },
+        with_indent  : with_indent,
+        with_block   : with_block,
+        with_parens  : with_parens,
+        with_square  : with_square,
+        options      : function() { return options },
+        line         : function() { return current_line },
+        col          : function() { return current_col },
+        pos          : function() { return current_pos }
     };
 
 };
+
+/* -----[ code generators ]----- */
+
+(function(DEF){
+    DEF(AST_Directive, function(self, output){
+        output.print_string(self.value);
+    });
+    DEF(AST_Debugger, function(self, output){
+        output.print_string("debugger");
+    });
+    DEF(AST_Parenthesized, function(self, output){
+        output.with_parens(function(){
+            self.expression.print(output);
+        });
+    });
+    DEF(AST_Bracketed, function(self, output){
+        output.with_block(function(){
+            self.body.forEach(function(stmt){
+                output.indent();
+                stmt.print(output);
+            });
+        });
+    });
+    /* -----[ statements ]----- */
+    DEF(AST_LabeledStatement, function(self, output){
+        output.print(self.label + ":");
+        output.space();
+        self.statement.print(output);
+    });
+    DEF(AST_SimpleStatement, function(self, output){
+        self.body.print(output);
+        output.semicolon();
+    });
+    DEF(AST_BlockStatement, function(self, output){
+        AST_Bracketed.prototype.print.call(self, output);
+    });
+    DEF(AST_EmptyStatement, function(self, output){
+        // do nothing here?
+        // output.semicolon();
+    });
+    DEF(AST_Do, function(self, output){
+        output.print("do");
+        output.space();
+        self.body.print(output);
+        output.space();
+        output.print("while");
+        self.condition.print(output);
+        self.semicolon();
+    });
+    DEF(AST_For, function(self, output){
+        output.print("for");
+        output.space();
+        output.with_parens(function(){
+            self.init.print(output);
+            output.semicolon();
+            self.condition.print(output);
+            output.semicolon();
+            self.step.print(output);
+        });
+        output.space();
+        self.body.print(output);
+    });
+    DEF(AST_ForIn, function(self, output){
+        output.print("for");
+        output.space();
+        output.with_parens(function(){
+            if (self.init) {
+                self.init.print(output);
+            } else {
+                self.name.print(output);
+            }
+            output.print(" in ");
+            self.object.print(output);
+        });
+        output.space();
+        self.body.print(output);
+    });
+    DEF(AST_With, function(self, output){
+        output.print("with");
+        output.space();
+        output.with_parens(function(){
+            self.expression.print(output);
+        });
+        output.space();
+        self.body.print(output);
+    });
+    /* -----[ functions ]----- */
+    DEF(AST_Lambda, function(self, output){
+        output.print("function");
+        output.space();
+        if (self.name) {
+            self.name.print(output);
+            output.space();
+        }
+        output.with_parens(function(){
+            self.argnames.forEach(function(arg, i){
+                if (i) output.comma();
+                arg.print(output);
+            });
+        });
+        output.space();
+        self.body.print(output);
+    });
+    /* -----[ exits ]----- */
+    AST_Exit.DEFMETHOD("_do_print", function(output, kind){
+        output.print(kind);
+        output.space();
+        self.value.print(output);
+        output.semicolon();
+    });
+    DEF(AST_Return, function(self, output){
+        self._do_print(output, "return");
+    });
+    DEF(AST_Throw, function(self, output){
+        self._do_print(output, "throw");
+    });
+    /* -----[ loop control ]----- */
+    AST_LoopControl.DEFMETHOD("_do_print", function(output, kind){
+        output.print(kind);
+        if (self.label) {
+            output.space();
+            self.label.print(output);
+        }
+        output.semicolon();
+    });
+    DEF(AST_Break, function(self, output){
+        self._do_print(output, "break");
+    });
+    DEF(AST_Continue, function(self, output){
+        self._do_print(output, "continue");
+    });
+    /* -----[ if ]----- */
+    DEF(AST_If, function(self, output){
+        output.print("if");
+        output.space();
+        output.with_parens(function(){
+            self.condition.print(output);
+        });
+        output.space();
+        self.consequent.print(output);
+        if (self.alternative) {
+            output.space();
+            self.alternative.print(output);
+        }
+    });
+    /* -----[ switch ]----- */
+    DEF(AST_Switch, function(self, output){
+        output.print("switch");
+        output.space();
+        output.with_parens(function(){
+            self.expression.print(output);
+        });
+        output.space();
+        self.body.print(output);
+    });
+    AST_SwitchBranch.DEFMETHOD("_do_print_body", function(output){
+        self.body.forEach(function(stmt){
+            output.indent();
+            stmt.print(output);
+            output.newline();
+        });
+    });
+    DEF(AST_Default, function(self, output){
+        output.print("default:");
+        output.newline();
+        self._do_print_body(output);
+    });
+    DEF(AST_Case, function(self, output){
+        output.print("case");
+        output.space();
+        self.expression.print(output);
+        output.print(":");
+        output.newline();
+        self._do_print_body(output);
+    });
+    /* -----[ exceptions ]----- */
+    DEF(AST_Try, function(self, output){
+        output.print("try");
+        output.space();
+        self.btry.print(output);
+        if (self.bcatch) {
+            output.space();
+            self.bcatch.print(output);
+        }
+        if (self.bfinally) {
+            output.space();
+            self.bfinally.print(output);
+        }
+    });
+    DEF(AST_Catch, function(self, output){
+        output.print("catch");
+        output.space();
+        self.body.print(output);
+    });
+    DEF(AST_Finally, function(self, output){
+        output.print("finally");
+        output.space();
+        self.body.print(output);
+    });
+    /* -----[ var/const ]----- */
+    AST_Definitions.DEFMETHOD("_do_print", function(output, kind){
+        output.print(kind);
+        output.space();
+        self.definitions.forEach(function(def, i){
+            if (i) output.space();
+            def.print(output);
+        });
+        output.semicolon();
+    });
+    DEF(AST_Var, function(self, output){
+        self._do_print(output, "var");
+    });
+    DEF(AST_Const, function(self, output){
+        self._do_print(output, "const");
+    });
+    DEF(AST_VarDef, function(self, output){
+        self.name.print(output);
+        if (self.value) {
+            output.space();
+            output.print("=");
+            output.space();
+            self.value.print(output);
+        }
+    });
+    /* -----[ other expressions ]----- */
+    DEF(AST_Call, function(self, output){
+        self.expression.print(output);
+        output.with_parens(function(){
+            self.args.forEach(function(arg, i){
+                if (i) output.comma();
+                arg.print(output);
+            });
+        });
+    });
+    DEF(AST_New, function(self, output){
+        output.print("new");
+        output.space();
+        AST_Call.prototype.print.call(self, output);
+    });
+    DEF(AST_Seq, function(self, output){
+        self.first.print(output);
+        output.comma();
+        self.second.print(output);
+    });
+    DEF(AST_Dot, function(self, output){
+        self.expression.print(output);
+        output.print(".");
+        output.print_name(self.property);
+    });
+    DEF(AST_Sub, function(self, output){
+        self.expression.print(output);
+        output.print("[");
+        self.property.print(output);
+        output.print("]");
+    });
+    DEF(AST_UnaryPrefix, function(self, output){
+        output.print(self.operator);
+        self.expression.print(output);
+    });
+    DEF(AST_UnaryPostfix, function(self, output){
+        self.expression.print(output);
+        output.print(self.operator);
+    });
+    DEF(AST_Binary, function(self, output){
+        self.left.print(output);
+        output.print(self.operator);
+        self.right.print(output);
+    });
+    DEF(AST_Conditional, function(self, output){
+        self.condition.print(output);
+        output.space();
+        output.print("?");
+        output.space();
+        self.consequent.print(output);
+        output.colon();
+        self.alternative.print(output);
+    });
+    /* -----[ literals ]----- */
+    DEF(AST_Array, function(self, output){
+        output.with_square(function(){
+            self.elements.forEach(function(exp, i){
+                if (i) output.comma();
+                exp.print(output);
+            });
+        });
+    });
+    DEF(AST_Object, function(self, output){
+        output.with_block(function(){
+            self.properties.forEach(function(prop, i){
+                if (i) output.comma();
+                prop.print(output);
+            });
+        });
+    });
+    DEF(AST_ObjectKeyVal, function(self, output){
+        output.print_name(self.key);
+        output.colon();
+        self.value.print(output);
+    });
+    DEF(AST_ObjectSetter, function(self, output){
+        throw "not yet done";
+    });
+    DEF(AST_ObjectGetter, function(self, output){
+        throw "not yet done";
+    });
+    DEF(AST_Symbol, function(self, output){
+        output.print_name(self.name);
+    });
+    DEF(AST_This, function(self, output){
+        output.print("this");
+    });
+    DEF(AST_Label, function(self, output){
+        output.print_name(self.name);
+    });
+    DEF(AST_Constant, function(self, output){
+        output.print(self.getValue());
+    });
+    DEF(AST_String, function(self, output){
+        output.print_string(self.getValue());
+    });
+    DEF(AST_RegExp, function(self, output){
+        output.print("/");
+        output.print(self.pattern);
+        output.print("/");
+        if (self.mods) output.print(self.mods);
+    });
+})(function DEF(nodetype, generator) {
+    nodetype.DEFMETHOD("print", function(stream){
+        generator(this, stream);
+    });
+});
index 2cd160f..fdf44ce 100644 (file)
@@ -833,15 +833,12 @@ function parse($TEXT, exigent_mode) {
         expect(":");
         S.labels.push(label);
         var start = S.token, stat = statement();
-        if (exigent_mode && !(stat instanceof AST_LabeledStatement))
-            unexpected(start);
         S.labels.pop();
-        stat.label = label;
-        return stat;
+        return new AST_LabeledStatement({ statement: stat });
     };
 
     function simple_statement() {
-        return new AST_Statement({ body: prog1(expression, semicolon) });
+        return new AST_SimpleStatement({ body: prog1(expression, semicolon) });
     };
 
     function break_cont(type) {
@@ -1024,7 +1021,11 @@ function parse($TEXT, exigent_mode) {
             next();
             bfinally = new AST_Finally({
                 start : start,
-                body  : block_(),
+                body  : new AST_Bracketed({
+                    start : S.token,
+                    body  : block_(),
+                    end   : prev()
+                }),
                 end   : prev()
             });
         }