code generator finally seems to work properly
authorMihai Bazon <mihai@bazon.net>
Fri, 17 Aug 2012 12:59:42 +0000 (15:59 +0300)
committerMihai Bazon <mihai@bazon.net>
Fri, 17 Aug 2012 12:59:42 +0000 (15:59 +0300)
lib/ast.js
lib/node.js
lib/output.js
lib/parse.js

index f66853e..8ee5868 100644 (file)
@@ -9,7 +9,7 @@ function DEFNODE(type, props, methods, base) {
         code += "this." + props[i] + " = props." + props[i] + ";";
     }
     if (methods && methods.initialize)
-        code += "this.initialize();"
+        code += "this.initialize();";
     code += " } }";
     var ctor = new Function(code)();
     if (base) {
@@ -36,7 +36,7 @@ var AST_Token = DEFNODE("Token", "type value line col pos endpos nlb comments_be
 var AST_Node = DEFNODE("Node", "start end", {
     clone: function() {
         return new this.CTOR(this);
-    },
+    }
 }, null);
 
 var AST_Directive = DEFNODE("Directive", "value", {
@@ -47,16 +47,6 @@ var AST_Debugger = DEFNODE("Debugger", null, {
 
 });
 
-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.",
-});
-
-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.",
-});
-
 /* -----[ loops ]----- */
 
 var AST_LabeledStatement = DEFNODE("LabeledStatement", "label statement", {
@@ -78,11 +68,14 @@ var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
 
 }, AST_Statement);
 
-var AST_Do = DEFNODE("Do", "condition", {
+var AST_DWLoop = DEFNODE("DWLoop", "condition", {
 }, AST_Statement);
 
-var AST_While = DEFNODE("While", "condition", {
-}, AST_Statement);
+var AST_Do = DEFNODE("Do", null, {
+}, AST_DWLoop);
+
+var AST_While = DEFNODE("While", null, {
+}, AST_DWLoop);
 
 var AST_For = DEFNODE("For", "init condition step", {
 }, AST_Statement);
@@ -152,7 +145,7 @@ var AST_Switch = DEFNODE("Switch", "expression", {
 }, AST_Statement);
 
 var AST_SwitchBlock = DEFNODE("SwitchBlock", null, {
-}, AST_Bracketed);
+}, AST_BlockStatement);
 
 var AST_SwitchBranch = DEFNODE("SwitchBranch", "body", {
 });
@@ -177,7 +170,7 @@ var AST_Finally = DEFNODE("Finally", "body", {
 
 /* -----[ VAR/CONST ]----- */
 
-var AST_Definitions = DEFNODE("Definitions", "definitions inline", {
+var AST_Definitions = DEFNODE("Definitions", "definitions", {
 });
 
 var AST_Var = DEFNODE("Var", null, {
index 6986749..ae2ec84 100755 (executable)
     ///
 
     var filename = process.argv[2];
-    console.time("parse");
+    //console.time("parse");
     var ast = parse(fs.readFileSync(filename, "utf8"));
-    console.timeEnd("parse");
+    //console.timeEnd("parse");
 
+    //console.time("generate");
     var stream = OutputStream({ beautify: true });
     ast.print(stream);
-    console.log(stream.get());
+    //console.timeEnd("generate");
+    sys.puts(stream.get());
 
     // console.time("walk");
     // var w = new TreeWalker(function(node){
index c581fa8..8500329 100644 (file)
@@ -8,7 +8,8 @@ function OutputStream(options) {
         ascii_only    : false,
         inline_script : false,
         width         : 80,
-        beautify      : true
+        beautify      : true,
+        scope_style   : "negate"
     });
 
     function noop() {};
@@ -63,11 +64,8 @@ function OutputStream(options) {
         return name;
     };
 
-    function make_indent(line) {
-        if (line == null)
-            line = "";
-        line = repeat_string(" ", options.indent_start + indentation) + line;
-        return line;
+    function make_indent(back) {
+        return repeat_string(" ", options.indent_start + indentation - back * options.indent_level);
     };
 
     function last_char() {
@@ -77,11 +75,14 @@ function OutputStream(options) {
     /* -----[ beautification/minification ]----- */
 
     var might_need_space = false;
+    var might_need_semicolon = false;
+    var last = null;
 
     function print(str) {
+        last = str;
         str = String(str);
+        var ch = str.charAt(0);
         if (might_need_space) {
-            var ch = str.charAt(0);
             if ((is_identifier_char(last_char())
                  && (is_identifier_char(ch) || ch == "\\"))
                 ||
@@ -91,8 +92,16 @@ function OutputStream(options) {
                 current_col++;
                 current_pos++;
             }
+            might_need_space = false;
+        }
+        if (might_need_semicolon) {
+            if (";{}".indexOf(ch) < 0 && !/[;]$/.test(OUTPUT)) {
+                OUTPUT += ";";
+                current_col++;
+                current_pos++;
+            }
+            might_need_semicolon = false;
         }
-        might_need_space = false;
         var a = str.split(/\r?\n/), n = a.length;
         current_line += n;
         if (n == 1) {
@@ -110,9 +119,9 @@ function OutputStream(options) {
         might_need_space = true;
     };
 
-    var indent = options.beautify ? function() {
+    var indent = options.beautify ? function(half) {
         if (options.beautify) {
-            print(make_indent());
+            print(make_indent(half ? 0.5 : 0));
         }
     } : noop;
 
@@ -129,6 +138,12 @@ function OutputStream(options) {
         print("\n");
     } : noop;
 
+    var semicolon = options.beautify ? function() {
+        print(";");
+    } : function() {
+        might_need_semicolon = true;
+    };
+
     function next_indent() {
         return indentation + options.indent_level;
     };
@@ -156,15 +171,12 @@ function OutputStream(options) {
 
     function with_square(cont) {
         print("[");
-        var ret = with_indent(current_col, cont);
+        //var ret = with_indent(current_col, cont);
+        var ret = cont();
         print("]");
         return ret;
     };
 
-    function semicolon() {
-        print(";");
-    };
-
     function comma() {
         print(",");
         space();
@@ -184,6 +196,7 @@ function OutputStream(options) {
         space        : space,
         comma        : comma,
         colon        : colon,
+        last         : function() { return last },
         semicolon    : semicolon,
         print_name   : function(name) { print(make_name(name)) },
         print_string : function(str) { print(encode_string(str)) },
@@ -191,57 +204,170 @@ function OutputStream(options) {
         with_block   : with_block,
         with_parens  : with_parens,
         with_square  : with_square,
-        options      : function() { return options },
+        options      : function(opt) { return options[opt] },
         line         : function() { return current_line },
         col          : function() { return current_col },
         pos          : function() { return current_pos },
         push_node    : function(node) { stack.push(node) },
         pop_node     : function() { return stack.pop() },
         stack        : function() { return stack },
-        parent       : function() { return stack[stack.length - 2] }
+        parent       : function(n) {
+            return stack[stack.length - 2 - (n || 0)];
+        }
     };
 
 };
 
 /* -----[ code generators ]----- */
 
-(function(DEFPRINT){
+(function(){
+
+    /* -----[ utils ]----- */
+
+    function DEFPRINT(nodetype, generator) {
+        nodetype.DEFMETHOD("print", function(stream){
+            var self = this;
+            stream.push_node(self);
+            if (self.needs_parens(stream)) {
+                stream.with_parens(function(){
+                    generator(self, stream);
+                });
+            } else {
+                generator(self, stream);
+            }
+            stream.pop_node();
+        });
+    };
+
+    function PARENS(nodetype, func) {
+        nodetype.DEFMETHOD("needs_parens", func);
+    };
+
+    /* -----[ PARENTHESES ]----- */
+
+    PARENS(AST_Node, function(){
+        return false;
+    });
+
+    // a function expression needs parens around it when it's provably
+    // the first token to appear in a statement.
+    PARENS(AST_Lambda, function(output){
+        return first_in_statement(output);
+    });
+
+    // same goes for an object literal, because otherwise it would be
+    // interpreted as a block of code.
+    PARENS(AST_Object, function(output){
+        return first_in_statement(output);
+    });
+
+    // Defun inherits from Lambda, but we don't want parens here.
+    PARENS(AST_Defun, function(){
+        return false;
+    });
+
+    PARENS(AST_Seq, function(output){
+        var p = output.parent();
+        return p instanceof AST_Call
+            || p instanceof AST_Binary
+            || p instanceof AST_VarDef
+            || p instanceof AST_Dot
+            || p instanceof AST_Array
+            || p instanceof AST_ObjectProperty
+            || p instanceof AST_Conditional;
+    });
+
+    PARENS(AST_Binary, function(output){
+        var p = output.parent();
+        if (p instanceof AST_Call && p.expression === this)
+            return true;
+        if (p instanceof AST_Unary)
+            return true;
+        if (p instanceof AST_PropAccess && p.expression === this)
+            return true;
+        if (p instanceof AST_Binary) {
+            var po = p.operator, pp = PRECEDENCE[po];
+            var so = this.operator, sp = PRECEDENCE[so];
+            if (pp > sp
+                || (pp == sp
+                    && this === p.right
+                    && !(so == po &&
+                         (so == "*" ||
+                          so == "&&" ||
+                          so == "||")))) {
+                return true;
+            }
+        }
+        if (this.operator == "in") {
+            // the “NoIn” stuff :-\
+            // UglifyJS 1.3.3 misses this one.
+            if ((p instanceof AST_For || p instanceof AST_ForIn) && p.init === this)
+                return true;
+            if (p instanceof AST_VarDef) {
+                var v = output.parent(1), p2 = output.parent(2);
+                if ((p2 instanceof AST_For || p2 instanceof AST_ForIn) && p2.init === v)
+                    return true;
+            }
+        }
+    });
+
+    PARENS(AST_New, function(output){
+        var p = output.parent();
+        if (p instanceof AST_Dot && no_constructor_parens(this, output))
+            return true;
+    });
+
+    function assign_and_conditional_paren_rules(output) {
+        var p = output.parent();
+        if (p instanceof AST_Unary)
+            return true;
+        if (p instanceof AST_Binary && !(p instanceof AST_Assign))
+            return true;
+        if (p instanceof AST_Call && p.expression === this)
+            return true;
+        if (p instanceof AST_Conditional && p.condition === this)
+            return true;
+        if (p instanceof AST_PropAccess && p.expression === this)
+            return true;
+    };
+
+    PARENS(AST_Conditional, assign_and_conditional_paren_rules);
+    PARENS(AST_Assign, assign_and_conditional_paren_rules);
+
+    /* -----[ PRINTERS ]----- */
+
     DEFPRINT(AST_Directive, function(self, output){
         output.print_string(self.value);
     });
     DEFPRINT(AST_Debugger, function(self, output){
-        output.print_string("debugger");
-    });
-    DEFPRINT(AST_Parenthesized, function(self, output){
-        output.with_parens(function(){
-            self.expression.print(output);
-        });
+        output.print("debugger");
+        output.semicolon();
     });
-    DEFPRINT(AST_Bracketed, function(self, output){
-        if (self.body.length > 0) output.with_block(function(){
-            self.body.forEach(function(stmt){
+
+    /* -----[ statements ]----- */
+
+    function display_body(body, is_toplevel, output) {
+        body.forEach(function(stmt){
+            if (!(stmt instanceof AST_EmptyStatement)) {
                 output.indent();
                 stmt.print(output);
                 output.newline();
-            });
+                if (is_toplevel) output.newline();
+            }
         });
-        else output.print("{}");
-    });
-    /* -----[ statements ]----- */
+    };
+
     DEFPRINT(AST_Statement, function(self, output){
         if (self.body instanceof AST_Node) {
             self.body.print(output);
             output.semicolon();
         } else {
-            self.body.forEach(function(stmt){
-                stmt.print(output);
-                output.newline();
-            });
+            display_body(self.body, self instanceof AST_Toplevel, output);
         }
     });
     DEFPRINT(AST_LabeledStatement, function(self, output){
-        output.print(self.label + ":");
-        output.space();
+        self.label.print(output);
+        output.colon();
         self.statement.print(output);
     });
     DEFPRINT(AST_SimpleStatement, function(self, output){
@@ -249,7 +375,10 @@ function OutputStream(options) {
         output.semicolon();
     });
     DEFPRINT(AST_BlockStatement, function(self, output){
-        AST_Bracketed.prototype.print.call(self, output);
+        if (self.body.length > 0) output.with_block(function(){
+            display_body(self.body, false, output);
+        });
+        else output.print("{}");
     });
     DEFPRINT(AST_EmptyStatement, function(self, output){
         output.semicolon();
@@ -260,8 +389,20 @@ function OutputStream(options) {
         self.body.print(output);
         output.space();
         output.print("while");
-        self.condition.print(output);
-        self.semicolon();
+        output.space();
+        output.with_parens(function(){
+            self.condition.print(output);
+        });
+        output.semicolon();
+    });
+    DEFPRINT(AST_While, function(self, output){
+        output.print("while");
+        output.space();
+        output.with_parens(function(){
+            self.condition.print(output);
+        });
+        output.space();
+        self.body.print(output);
     });
     DEFPRINT(AST_For, function(self, output){
         output.print("for");
@@ -269,17 +410,17 @@ function OutputStream(options) {
         output.with_parens(function(){
             if (self.init) {
                 self.init.print(output);
-                output.semicolon();
+                output.print(";");
                 output.space();
             } else {
-                output.semicolon();
+                output.print(";");
             }
             if (self.condition) {
                 self.condition.print(output);
-                output.semicolon();
+                output.print(";");
                 output.space();
             } else {
-                output.semicolon();
+                output.print(";");
             }
             if (self.step) {
                 self.step.print(output);
@@ -312,11 +453,15 @@ function OutputStream(options) {
         output.space();
         self.body.print(output);
     });
+
     /* -----[ functions ]----- */
-    DEFPRINT(AST_Lambda, function(self, output){
-        output.print("function");
-        output.space();
+    AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword){
+        var self = this;
+        if (!nokeyword) {
+            output.print("function");
+        }
         if (self.name) {
+            output.space();
             self.name.print(output);
         }
         output.with_parens(function(){
@@ -328,11 +473,17 @@ function OutputStream(options) {
         output.space();
         self.body.print(output);
     });
+    DEFPRINT(AST_Lambda, function(self, output){
+        self._do_print(output);
+    });
+
     /* -----[ exits ]----- */
     AST_Exit.DEFMETHOD("_do_print", function(output, kind){
         output.print(kind);
-        output.space();
-        this.value.print(output);
+        if (this.value) {
+            output.space();
+            this.value.print(output);
+        }
         output.semicolon();
     });
     DEFPRINT(AST_Return, function(self, output){
@@ -341,6 +492,7 @@ function OutputStream(options) {
     DEFPRINT(AST_Throw, function(self, output){
         self._do_print(output, "throw");
     });
+
     /* -----[ loop control ]----- */
     AST_LoopControl.DEFMETHOD("_do_print", function(output, kind){
         output.print(kind);
@@ -356,6 +508,7 @@ function OutputStream(options) {
     DEFPRINT(AST_Continue, function(self, output){
         self._do_print(output, "continue");
     });
+
     /* -----[ if ]----- */
     DEFPRINT(AST_If, function(self, output){
         output.print("if");
@@ -366,10 +519,13 @@ function OutputStream(options) {
         output.space();
         self.consequent.print(output);
         if (self.alternative) {
+            output.space();
+            output.print("else");
             output.space();
             self.alternative.print(output);
         }
     });
+
     /* -----[ switch ]----- */
     DEFPRINT(AST_Switch, function(self, output){
         output.print("switch");
@@ -380,16 +536,28 @@ function OutputStream(options) {
         output.space();
         self.body.print(output);
     });
+    DEFPRINT(AST_SwitchBlock, function(self, output){
+        if (self.body.length > 0) output.with_block(function(){
+            self.body.forEach(function(stmt, i){
+                if (i) output.newline();
+                output.indent(true);
+                stmt.print(output);
+            });
+        });
+        else output.print("{}");
+    });
     AST_SwitchBranch.DEFMETHOD("_do_print_body", function(output){
-        this.body.forEach(function(stmt){
-            output.indent();
-            stmt.print(output);
+        if (this.body.length > 0) {
             output.newline();
-        });
+            this.body.forEach(function(stmt){
+                output.indent();
+                stmt.print(output);
+                output.newline();
+            });
+        }
     });
     DEFPRINT(AST_Default, function(self, output){
         output.print("default:");
-        output.newline();
         self._do_print_body(output);
     });
     DEFPRINT(AST_Case, function(self, output){
@@ -397,9 +565,9 @@ function OutputStream(options) {
         output.space();
         self.expression.print(output);
         output.print(":");
-        output.newline();
         self._do_print_body(output);
     });
+
     /* -----[ exceptions ]----- */
     DEFPRINT(AST_Try, function(self, output){
         output.print("try");
@@ -417,6 +585,10 @@ function OutputStream(options) {
     DEFPRINT(AST_Catch, function(self, output){
         output.print("catch");
         output.space();
+        output.with_parens(function(){
+            self.argname.print(output);
+        });
+        output.space();
         self.body.print(output);
     });
     DEFPRINT(AST_Finally, function(self, output){
@@ -424,6 +596,7 @@ function OutputStream(options) {
         output.space();
         self.body.print(output);
     });
+
     /* -----[ var/const ]----- */
     AST_Definitions.DEFMETHOD("_do_print", function(output, kind){
         output.print(kind);
@@ -432,7 +605,11 @@ function OutputStream(options) {
             if (i) output.comma();
             def.print(output);
         });
-        if (!this.inline) output.semicolon();
+        var p = output.parent();
+        var in_for = p instanceof AST_For || p instanceof AST_ForIn;
+        var avoid_semicolon = in_for && p.init === this;
+        if (!avoid_semicolon)
+            output.semicolon();
     });
     DEFPRINT(AST_Var, function(self, output){
         self._do_print(output, "var");
@@ -449,16 +626,24 @@ function OutputStream(options) {
             self.value.print(output);
         }
     });
+
     /* -----[ other expressions ]----- */
     DEFPRINT(AST_Call, function(self, output){
         self.expression.print(output);
+        if (self instanceof AST_New && no_constructor_parens(self, output))
+            return;
         output.with_parens(function(){
-            self.args.forEach(function(arg, i){
+            self.args.forEach(function(expr, i){
                 if (i) output.comma();
-                arg.print(output);
+                expr.print(output);
             });
         });
     });
+    function no_constructor_parens(self, output) {
+        return (self.args.length == 0
+                // && !output.options("beautify")
+               );
+    };
     DEFPRINT(AST_New, function(self, output){
         output.print("new");
         output.space();
@@ -470,7 +655,12 @@ function OutputStream(options) {
         self.second.print(output);
     });
     DEFPRINT(AST_Dot, function(self, output){
-        self.expression.print(output);
+        var expr = self.expression;
+        expr.print(output);
+        if (expr instanceof AST_Number) {
+            if (!/[xa-f.]/i.test(output.last()))
+                output.print(".");
+        }
         output.print(".");
         output.print_name(self.property);
     });
@@ -488,43 +678,12 @@ function OutputStream(options) {
         self.expression.print(output);
         output.print(self.operator);
     });
-    AST_Binary.DEFMETHOD("_do_print", function(output){
-        this.left.print(output);
+    DEFPRINT(AST_Binary, function(self, output){
+        self.left.print(output);
         output.space();
-        output.print(this.operator);
+        output.print(self.operator);
         output.space();
-        this.right.print(output);
-    });
-    DEFPRINT(AST_Binary, function(self, output){
-        var p = output.parent();
-        if (p instanceof AST_Binary) {
-            var po = p.operator, pp = PRECEDENCE[po];
-            var so = self.operator, sp = PRECEDENCE[so];
-            if (pp > sp
-                || (pp == sp
-                    && self === p.right
-                    && !(so == po &&
-                         (so == "*" ||
-                          so == "&&" ||
-                          so == "||")))) {
-                output.with_parens(function(){
-                    self._do_print(output);
-                });
-                return;
-            }
-        }
-        self._do_print(output);
-    });
-    // XXX: this is quite similar as for AST_Binary, except for the parens.
-    DEFPRINT(AST_Assign, function(self, output){
-        var p = output.parent();
-        if (p instanceof AST_Binary) {
-            output.with_parens(function(){
-                self._do_print(output);
-            });
-            return;
-        }
-        self._do_print(output);
+        self.right.print(output);
     });
     DEFPRINT(AST_Conditional, function(self, output){
         self.condition.print(output);
@@ -532,9 +691,11 @@ function OutputStream(options) {
         output.print("?");
         output.space();
         self.consequent.print(output);
+        output.space();
         output.colon();
         self.alternative.print(output);
     });
+
     /* -----[ literals ]----- */
     DEFPRINT(AST_Array, function(self, output){
         output.with_square(function(){
@@ -547,24 +708,43 @@ function OutputStream(options) {
     DEFPRINT(AST_Object, function(self, output){
         if (self.properties.length > 0) output.with_block(function(){
             self.properties.forEach(function(prop, i){
-                if (i) output.comma();
+                if (i) {
+                    output.comma();
+                    output.newline();
+                }
                 output.indent();
                 prop.print(output);
-                output.newline();
             });
+            output.newline();
         });
         else output.print("{}");
     });
     DEFPRINT(AST_ObjectKeyVal, function(self, output){
-        output.print_name(self.key);
+        var key = self.key;
+        if (output.options("quote_keys")) {
+            output.print_string(key);
+        } else if ((typeof key == "number"
+                    || !output.options("beautify")
+                    && +key + "" == key)
+                   && parseFloat(key) >= 0) {
+            output.print(make_num(key));
+        } else if (!is_identifier(key)) {
+            output.print_string(key);
+        } else {
+            output.print_name(key);
+        }
         output.colon();
         self.value.print(output);
     });
     DEFPRINT(AST_ObjectSetter, function(self, output){
-        throw "not yet done";
+        output.print("set");
+        output.space();
+        self.func._do_print(output, true);
     });
     DEFPRINT(AST_ObjectGetter, function(self, output){
-        throw "not yet done";
+        output.print("get");
+        output.space();
+        self.func._do_print(output, true);
     });
     DEFPRINT(AST_Symbol, function(self, output){
         output.print_name(self.name);
@@ -581,16 +761,69 @@ function OutputStream(options) {
     DEFPRINT(AST_String, function(self, output){
         output.print_string(self.getValue());
     });
+    DEFPRINT(AST_Number, function(self, output){
+        output.print(make_num(self.getValue()));
+    });
     DEFPRINT(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){
-        stream.push_node(this);
-        generator(this, stream);
-        stream.pop_node();
-    });
-});
+
+    // return true if the node at the top of the stack (that means the
+    // innermost node in the current output) is lexically the first in
+    // a statement.
+    function first_in_statement(output) {
+        var a = output.stack(), i = a.length, node = a[--i], p = a[--i];
+        while (i > 0) {
+            if (p instanceof AST_Statement) return true;
+            if ((p instanceof AST_Seq && p.first === node) ||
+                (p instanceof AST_Call && p.expression === node) ||
+                (p instanceof AST_Dot && p.expression === node) ||
+                (p instanceof AST_Sub && p.expression === node) ||
+                (p instanceof AST_Conditional && p.condition === node) ||
+                (p instanceof AST_Binary && p.first === node) ||
+                (p instanceof AST_Assign && p.first === node) ||
+                (p instanceof AST_UnaryPostfix && p.expression === node))
+            {
+                node = p;
+                p = a[--i];
+            } else {
+                return false;
+            }
+        }
+    };
+
+    function best_of(a) {
+        var best = a[0], len = best.length;
+        for (var i = 1; i < a.length; ++i) {
+            if (a[i].length < len) {
+                best = a[i];
+                len = best.length;
+            }
+        }
+        return best;
+    };
+
+    function make_num(num) {
+        var str = num.toString(10), a = [ str.replace(/^0\./, ".").replace('e+', 'e') ], m;
+        if (Math.floor(num) === num) {
+            if (num >= 0) {
+                a.push("0x" + num.toString(16).toLowerCase(), // probably pointless
+                       "0" + num.toString(8)); // same.
+            } else {
+                a.push("-0x" + (-num).toString(16).toLowerCase(), // probably pointless
+                       "-0" + (-num).toString(8)); // same.
+            }
+            if ((m = /^(.*?)(0+)$/.exec(num))) {
+                a.push(m[1] + "e" + m[2].length);
+            }
+        } else if ((m = /^0?\.(0+)(.*)$/.exec(num))) {
+            a.push(m[2] + "e-" + (m[1].length + m[2].length),
+                   str.substr(str.indexOf(".")));
+        }
+        return best_of(a);
+    };
+
+})();
index 260c411..f5796b0 100644 (file)
@@ -166,6 +166,14 @@ function is_unicode_connector_punctuation(ch) {
     return UNICODE.connector_punctuation.test(ch);
 };
 
+function is_identifier(name) {
+    return /^[a-z_$][a-z0-9_$]*$/i.test(name)
+        && name != "this"
+        && !HOP(KEYWORDS_ATOM, name)
+        && !HOP(RESERVED_WORDS, name)
+        && !HOP(KEYWORDS, name);
+};
+
 function is_identifier_start(ch) {
     return ch == "$" || ch == "_" || is_letter(ch);
 };
@@ -695,11 +703,10 @@ function parse($TEXT, exigent_mode) {
     };
 
     function parenthesised() {
-        return new AST_Parenthesized({
-            start      : expect("("),
-            expression : expression(),
-            end        : expect(")")
-        });
+        expect("(");
+        var exp = expression();
+        expect(")");
+        return exp;
     };
 
     function embed_tokens(parser) {
@@ -834,7 +841,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 });
+        return new AST_LabeledStatement({ statement: stat, label: label });
     };
 
     function simple_statement() {
@@ -866,9 +873,6 @@ function parse($TEXT, exigent_mode) {
             init = is("keyword", "var")
                 ? (next(), var_(true))
                 : expression(true, true);
-            if (init instanceof AST_Var) {
-                init.inline = true;
-            }
             if (is("operator", "in")) {
                 if (init instanceof AST_Var && init.definitions.length > 1)
                     croak("Only one variable declaration allowed in for..in loop");
@@ -932,7 +936,7 @@ function parse($TEXT, exigent_mode) {
                 --S.in_function;
                 S.in_loop = loop;
                 S.labels = labels;
-                return new AST_Bracketed({ body: a });
+                return new AST_BlockStatement({ body: a });
             })()
         });
     };
@@ -997,7 +1001,7 @@ function parse($TEXT, exigent_mode) {
     }));
 
     function try_() {
-        var body = new AST_Bracketed({
+        var body = new AST_BlockStatement({
             start : S.token,
             body  : block_(),
             end   : prev()
@@ -1011,7 +1015,7 @@ function parse($TEXT, exigent_mode) {
             bcatch = new AST_Catch({
                 start   : start,
                 argname : name,
-                body    : new AST_Bracketed({
+                body    : new AST_BlockStatement({
                     start : S.token,
                     body  : block_(),
                     end   : prev()
@@ -1024,7 +1028,7 @@ function parse($TEXT, exigent_mode) {
             next();
             bfinally = new AST_Finally({
                 start : start,
-                body  : new AST_Bracketed({
+                body  : new AST_BlockStatement({
                     start : S.token,
                     body  : block_(),
                     end   : prev()
@@ -1191,7 +1195,7 @@ function parse($TEXT, exigent_mode) {
             var type = start.type;
             var name = as_property_name();
             if (type == "name" && !is("punc", ":")) {
-                if (name.name == "get") {
+                if (name == "get") {
                     a.push(new AST_ObjectGetter({
                         start : start,
                         name  : name,
@@ -1200,7 +1204,7 @@ function parse($TEXT, exigent_mode) {
                     }));
                     continue;
                 }
-                if (name.name == "set") {
+                if (name == "set") {
                     a.push(new AST_ObjectSetter({
                         start : start,
                         name  : name,