WIP
authorMihai Bazon <mihai@bazon.net>
Wed, 15 Aug 2012 10:32:37 +0000 (13:32 +0300)
committerMihai Bazon <mihai@bazon.net>
Wed, 15 Aug 2012 11:50:27 +0000 (14:50 +0300)
lib/ast.js
lib/node.js
lib/output.js
lib/parse.js
lib/test.js

index 683f602..b8b6b07 100644 (file)
@@ -8,6 +8,8 @@ function DEFNODE(type, props, methods, base) {
     for (var i = props.length; --i >= 0;) {
         code += "this." + props[i] + " = props." + props[i] + ";";
     }
+    if (methods && methods.initialize)
+        code += "this.initialize();"
     code += " } }";
     var ctor = new Function(code)();
     if (base) {
@@ -29,11 +31,15 @@ var AST_Token = DEFNODE("Token", "type value line col pos endpos nlb comments_be
 }, null);
 
 var AST_Node = DEFNODE("Node", "start end", {
-    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);
+    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);
     }
@@ -75,41 +81,50 @@ Used for bodies of FUNCTION/TRY/CATCH/THROW/SWITCH.",
 
 /* -----[ loops ]----- */
 
-var AST_LabeledStatement = DEFNODE("LabeledStatement", "label body", {
+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 Array)
-                    AST_Bracketed.prototype.walk.call(this, w);
-                else
+                if (this.body instanceof AST_Node)
                     this.body.walk(w);
+                else
+                    this.walk_array(w);
             }
         });
-    }
+    },
+    walk_array: AST_Bracketed.prototype.walk
 });
 
-var AST_Statement = DEFNODE("Statement", null, {
+var AST_SimpleStatement = DEFNODE("SimpleStatement", null, {
+
+}, AST_Statement);
 
-}, AST_LabeledStatement);
+var AST_BlockStatement = DEFNODE("BlockStatement", null, {
+
+}, AST_Statement);
+
+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_LabeledStatement.prototype.walk.call(this, w);
+            AST_Statement.prototype.walk.call(this, w);
         });
     }
-}, AST_LabeledStatement);
+}, AST_Statement);
 
 var AST_While = DEFNODE("While", "condition", {
     walk: function(w) {
         w._visit(this, function(){
             this.condition.walk(w);
-            AST_LabeledStatement.prototype.walk.call(this, w);
+            AST_Statement.prototype.walk.call(this, w);
         });
     }
-}, AST_LabeledStatement);
+}, AST_Statement);
 
 var AST_For = DEFNODE("For", "init condition step", {
     walk: function(w) {
@@ -117,42 +132,42 @@ var AST_For = DEFNODE("For", "init condition step", {
             if (this.init) this.init.walk(w);
             if (this.condition) this.condition.walk(w);
             if (this.step) this.step.walk(w);
-            AST_LabeledStatement.prototype.walk.call(this, w);
+            AST_Statement.prototype.walk.call(this, w);
         });
     }
-}, AST_LabeledStatement);
+}, 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_LabeledStatement.prototype.walk.call(this, w);
+            AST_Statement.prototype.walk.call(this, w);
         });
     }
-}, AST_LabeledStatement);
+}, AST_Statement);
 
-var AST_With = DEFNODE("With", "expression body", {
+var AST_With = DEFNODE("With", "expression", {
     walk: function(w) {
         w._visit(this, function(){
             this.expression.walk(w);
-            AST_LabeledStatement.prototype.walk.call(this, w);
+            AST_Statement.prototype.walk.call(this, w);
         });
     }
-});
+}, AST_Statement);
 
 /* -----[ functions ]----- */
 
-var AST_Scope = DEFNODE("Scope", "identifiers body", {
+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_LabeledStatement.prototype.walk.call(this, w);
+            AST_Statement.prototype.walk.call(this, w);
         });
     }
-});
+}, AST_Statement);
 
 var AST_Toplevel = DEFNODE("Toplevel", null, {
 
@@ -234,25 +249,23 @@ var AST_Switch = DEFNODE("Switch", "expression", {
     walk: function(w) {
         w._visit(this, function(){
             this.expression.walk(w);
-            AST_LabeledStatement.prototype.walk.call(this, w);
+            AST_Statement.prototype.walk.call(this, w);
         });
     }
-}, AST_LabeledStatement);
+}, 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, {
-    walk: function(w) {
-        w._visit(this, function(){
-            AST_Statement.prototype.walk.call(this, w);
-        });
-    }
+
 }, AST_SwitchBranch);
 
 var AST_Case = DEFNODE("Case", "expression", {
@@ -482,10 +495,8 @@ var AST_Number = DEFNODE("Number", "value", {
 }, AST_Constant);
 
 var AST_RegExp = DEFNODE("Regexp", "pattern mods", {
-    getValue: function() {
-        return this._regexp || (
-            this._regexp = new RegExp(this.pattern, this.mods)
-        );
+    initialize: function() {
+        this.value = new RegExp(this.pattern, this.mods);
     }
 }, AST_Constant);
 
@@ -494,17 +505,45 @@ var AST_Atom = DEFNODE("Atom", null, {
 }, AST_Constant);
 
 var AST_Null = DEFNODE("Null", null, {
-    getValue: function() { return null }
+    value: null
 }, AST_Atom);
 
 var AST_Undefined = DEFNODE("Undefined", null, {
-    getValue: function() { return (function(){}()) }
+    value: (function(){}())
 }, AST_Atom);
 
 var AST_False = DEFNODE("False", null, {
-    getValue: function() { return false }
+    value: false
 }, AST_Atom);
 
 var AST_True = DEFNODE("True", null, {
-    getValue: function() { return true }
+    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 9089a5f..571cdd8 100755 (executable)
     console.timeEnd("parse");
 
     console.time("walk");
-    ast.walk({
-        _visit: function(node, descend) {
-            //console.log(node);
-            if (descend) descend.call(node);
-        }
+    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");
 
 })();
index 2c4c6fd..cf17c3e 100644 (file)
@@ -4,7 +4,6 @@ function OutputStream(options) {
         indent_level  : 4,
         quote_keys    : false,
         space_colon   : false,
-        beautify      : true,
         ascii_only    : false,
         inline_script : false,
         width         : 80
@@ -12,6 +11,7 @@ function OutputStream(options) {
 
     var indentation = 0;
     var current_col = 0;
+    var current_line = 0;
     var OUTPUT = "";
 
     function to_ascii(str) {
@@ -45,12 +45,9 @@ function OutputStream(options) {
     };
 
     function print(str) {
-        var nl = str.lastIndexOf("\n");
-        if (nl >= 0) {
-            current_col = nl;
-        } else {
-            current_col += str.length;
-        }
+        var a = str.split(/\r?\n/), n = a.length;
+        current_line += n;
+        current_col += a[n - 1].length;
         OUTPUT += str;
     };
 
@@ -71,12 +68,12 @@ function OutputStream(options) {
     function make_indent(line) {
         if (line == null)
             line = "";
-        if (beautify)
-            line = repeat_string(" ", options.indent_start + indentation) + line;
+        line = repeat_string(" ", options.indent_start + indentation) + line;
         return line;
     };
 
     function with_indent(col, cont) {
+        if (col === true) col = next_indent();
         var save_indentation = indentation;
         indentation = col;
         var ret = cont();
@@ -85,34 +82,31 @@ function OutputStream(options) {
     };
 
     function indent() {
-        if (options.beautify) print(make_indent());
+        print(make_indent());
     };
 
     function newline() {
-        if (options.beautify) {
-            print("\n");
-            print(make_indent());
-        }
+        print("\n");
     };
 
     function next_indent() {
         return indentation + options.indent_level;
     };
 
-    function with_block(cont) {
+    function with_block(cont, beautify) {
         var ret;
         print("{");
         with_indent(next_indent(), function(){
-            newline();
+            if (beautify) newline();
             ret = cont();
-            newline();
+            if (beautify) newline();
         });
-        indent();
+        if (beautify) indent();
         print("}");
         return ret;
     };
 
-    function with_parens(cont) {
+    function with_parens(cont, beautify) {
         print("(");
         var ret = with_indent(current_col, cont);
         print(")");
@@ -128,7 +122,10 @@ function OutputStream(options) {
         with_indent : with_indent,
         with_block  : with_block,
         with_parens : with_parens,
-        options     : function() { return options }
+        options     : function() { return options },
+        line        : function() { return current_line },
+        col         : function() { return current_col },
+        pos         : function() { return OUTPUT.length }
     };
 
 };
index 9dcfa63..2cd160f 100644 (file)
@@ -739,13 +739,13 @@ function parse($TEXT, exigent_mode) {
           case "punc":
             switch (S.token.value) {
               case "{":
-                return new AST_Statement({ body: block_() });
+                return new AST_BlockStatement({ body: block_() });
               case "[":
               case "(":
                 return simple_statement();
               case ";":
                 next();
-                return new AST_Statement();
+                return new AST_EmptyStatement();
               default:
                 unexpected();
             }
index b765132..7972d34 100644 (file)
@@ -1,346 +1,5 @@
-var func = function tokenizer($TEXT) {
-
-    var S = {
-        text            : $TEXT.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, ''),
-        pos             : 0,
-        tokpos          : 0,
-        line            : 0,
-        tokline         : 0,
-        col             : 0,
-        tokcol          : 0,
-        newline_before  : false,
-        regex_allowed   : false,
-        comments_before : []
-    };
-
-    function peek() { return S.text.charAt(S.pos); };
-
-    function next(signal_eof, in_string) {
-        var ch = S.text.charAt(S.pos++);
-        if (signal_eof && !ch)
-            throw EX_EOF;
-        if (ch == "\n") {
-            S.newline_before = S.newline_before || !in_string;
-            ++S.line;
-            S.col = 0;
-        } else {
-            ++S.col;
-        }
-        return ch;
-    };
-
-    function eof() {
-        return !S.peek();
-    };
-
-    function find(what, signal_eof) {
-        var pos = S.text.indexOf(what, S.pos);
-        if (signal_eof && pos == -1) throw EX_EOF;
-        return pos;
-    };
-
-    function start_token() {
-        S.tokline = S.line;
-        S.tokcol = S.col;
-        S.tokpos = S.pos;
-    };
-
-    function token(type, value, is_comment) {
-        S.regex_allowed = ((type == "operator" && !HOP(UNARY_POSTFIX, value)) ||
-                           (type == "keyword" && HOP(KEYWORDS_BEFORE_EXPRESSION, value)) ||
-                           (type == "punc" && HOP(PUNC_BEFORE_EXPRESSION, value)));
-        var ret = {
-            type   : type,
-            value  : value,
-            line   : S.tokline,
-            col    : S.tokcol,
-            pos    : S.tokpos,
-            endpos : S.pos,
-            nlb    : S.newline_before
-        };
-        if (!is_comment) {
-            ret.comments_before = S.comments_before;
-            S.comments_before = [];
-            // make note of any newlines in the comments that came before
-            for (var i = 0, len = ret.comments_before.length; i < len; i++) {
-                ret.nlb = ret.nlb || ret.comments_before[i].nlb;
-            }
-        }
-        S.newline_before = false;
-        return new AST_Token(ret);
-    };
-
-    function skip_whitespace() {
-        while (HOP(WHITESPACE_CHARS, peek()))
-            next();
-    };
-
-    function read_while(pred) {
-        var ret = "", ch = peek(), i = 0;
-        while (ch && pred(ch, i++)) {
-            ret += next();
-            ch = peek();
-        }
-        return ret;
-    };
-
-    function parse_error(err) {
-        js_error(err, S.tokline, S.tokcol, S.tokpos);
-    };
-
-    function read_num(prefix) {
-        var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".";
-        var num = read_while(function(ch, i){
-            if (ch == "x" || ch == "X") {
-                if (has_x) return false;
-                return has_x = true;
-            }
-            if (!has_x && (ch == "E" || ch == "e")) {
-                if (has_e) return false;
-                return has_e = after_e = true;
-            }
-            if (ch == "-") {
-                if (after_e || (i == 0 && !prefix)) return true;
-                return false;
-            }
-            if (ch == "+") return after_e;
-            after_e = false;
-            if (ch == ".") {
-                if (!has_dot && !has_x && !has_e)
-                    return has_dot = true;
-                return false;
-            }
-            return is_alphanumeric_char(ch);
-        });
-        if (prefix)
-            num = prefix + num;
-        var valid = parse_js_number(num);
-        if (!isNaN(valid)) {
-            return token("num", valid);
-        } else {
-            parse_error("Invalid syntax: " + num);
-        }
-    };
-
-    function read_escaped_char(in_string) {
-        var ch = next(true, in_string);
-        switch (ch) {
-          case "n" : return "\n";
-          case "r" : return "\r";
-          case "t" : return "\t";
-          case "b" : return "\b";
-          case "v" : return "\u000b";
-          case "f" : return "\f";
-          case "0" : return "\0";
-          case "x" : return String.fromCharCode(hex_bytes(2));
-          case "u" : return String.fromCharCode(hex_bytes(4));
-          case "\n": return "";
-          default  : return ch;
-        }
-    };
-
-    function hex_bytes(n) {
-        var num = 0;
-        for (; n > 0; --n) {
-            var digit = parseInt(next(true), 16);
-            if (isNaN(digit))
-                parse_error("Invalid hex-character pattern in string");
-            num = (num << 4) | digit;
-        }
-        return num;
-    };
-
-    function read_string() {
-        return with_eof_error("Unterminated string constant", function(){
-            var quote = next(), ret = "";
-            for (;;) {
-                var ch = next(true);
-                if (ch == "\\") {
-                    // read OctalEscapeSequence (XXX: deprecated if "strict mode")
-                    // https://github.com/mishoo/UglifyJS/issues/178
-                    var octal_len = 0, first = null;
-                    ch = read_while(function(ch){
-                        if (ch >= "0" && ch <= "7") {
-                            if (!first) {
-                                first = ch;
-                                return ++octal_len;
-                            }
-                            else if (first <= "3" && octal_len <= 2) return ++octal_len;
-                            else if (first >= "4" && octal_len <= 1) return ++octal_len;
-                        }
-                        return false;
-                    });
-                    if (octal_len > 0) ch = String.fromCharCode(parseInt(ch, 8));
-                    else ch = read_escaped_char(true);
-                }
-                else if (ch == quote) break;
-                ret += ch;
-            }
-            return token("string", ret);
-        });
-    };
-
-    function read_line_comment() {
-        next();
-        var i = find("\n"), ret;
-        if (i == -1) {
-            ret = S.text.substr(S.pos);
-            S.pos = S.text.length;
-        } else {
-            ret = S.text.substring(S.pos, i);
-            S.pos = i;
-        }
-        return token("comment1", ret, true);
-    };
-
-    function read_multiline_comment() {
-        next();
-        return with_eof_error("Unterminated multiline comment", function(){
-            var i = find("*/", true),
-            text = S.text.substring(S.pos, i);
-            S.pos = i + 2;
-            S.line += text.split("\n").length - 1;
-            S.newline_before = S.newline_before || text.indexOf("\n") >= 0;
-
-            // https://github.com/mishoo/UglifyJS/issues/#issue/100
-            if (/^@cc_on/i.test(text)) {
-                warn("WARNING: at line " + S.line);
-                warn("*** Found \"conditional comment\": " + text);
-                warn("*** UglifyJS DISCARDS ALL COMMENTS.  This means your code might no longer work properly in Internet Explorer.");
-            }
-
-            return token("comment2", text, true);
-        });
-    };
-
-    function read_name() {
-        var backslash = false, name = "", ch, escaped = false, hex;
-        while ((ch = peek()) != null) {
-            if (!backslash) {
-                if (ch == "\\") escaped = backslash = true, next();
-                else if (is_identifier_char(ch)) name += next();
-                else break;
-            }
-            else {
-                if (ch != "u") parse_error("Expecting UnicodeEscapeSequence -- uXXXX");
-                ch = read_escaped_char();
-                if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier");
-                name += ch;
-                backslash = false;
-            }
-        }
-        if (HOP(KEYWORDS, name) && escaped) {
-            hex = name.charCodeAt(0).toString(16).toUpperCase();
-            name = "\\u" + "0000".substr(hex.length) + hex + name.slice(1);
-        }
-        return name;
-    };
-
-    function read_regexp(regexp) {
-        return with_eof_error("Unterminated regular expression", function(){
-            var prev_backslash = false, ch, in_class = false;
-            while ((ch = next(true))) if (prev_backslash) {
-                regexp += "\\" + ch;
-                prev_backslash = false;
-            } else if (ch == "[") {
-                in_class = true;
-                regexp += ch;
-            } else if (ch == "]" && in_class) {
-                in_class = false;
-                regexp += ch;
-            } else if (ch == "/" && !in_class) {
-                break;
-            } else if (ch == "\\") {
-                prev_backslash = true;
-            } else {
-                regexp += ch;
-            }
-            var mods = read_name();
-            return token("regexp", [ regexp, mods ]);
-        });
-    };
-
-    function read_operator(prefix) {
-        function grow(op) {
-            if (!peek()) return op;
-            var bigger = op + peek();
-            if (HOP(OPERATORS, bigger)) {
-                next();
-                return grow(bigger);
-            } else {
-                return op;
-            }
-        };
-        return token("operator", grow(prefix || next()));
-    };
-
-    function handle_slash() {
-        next();
-        var regex_allowed = S.regex_allowed;
-        switch (peek()) {
-          case "/":
-            S.comments_before.push(read_line_comment());
-            S.regex_allowed = regex_allowed;
-            return next_token();
-          case "*":
-            S.comments_before.push(read_multiline_comment());
-            S.regex_allowed = regex_allowed;
-            return next_token();
-        }
-        return S.regex_allowed ? read_regexp("") : read_operator("/");
-    };
-
-    function handle_dot() {
-        next();
-        return is_digit(peek())
-            ? read_num(".")
-            : token("punc", ".");
-    };
-
-    function read_word() {
-        var word = read_name();
-        return HOP(KEYWORDS_ATOM, word)
-            ? token("atom", word)
-            : !HOP(KEYWORDS, word)
-            ? token("name", word)
-            : HOP(OPERATORS, word)
-            ? token("operator", word)
-            : token("keyword", word);
-    };
-
-    function with_eof_error(eof_error, cont) {
-        try {
-            return cont();
-        } catch(ex) {
-            if (ex === EX_EOF) parse_error(eof_error);
-            else throw ex;
-        }
-    };
-
-    function next_token(force_regexp) {
-        if (force_regexp != null)
-            return read_regexp(force_regexp);
-        skip_whitespace();
-        start_token();
-        var ch = peek();
-        if (!ch) return token("eof");
-        if (is_digit(ch)) return read_num();
-        if (ch == '"' || ch == "'") return read_string();
-        if (HOP(PUNC_CHARS, ch)) return token("punc", next());
-        if (ch == ".") return handle_dot();
-        if (ch == "/") return handle_slash();
-        if (HOP(OPERATOR_CHARS, ch)) return read_operator();
-        if (ch == "\\" || is_identifier_start(ch)) return read_word();
-        parse_error("Unexpected character '" + ch + "'");
-    };
-
-    next_token.context = function(nc) {
-        if (nc) S = nc;
-        return S;
-    };
-
-    return next_token;
-
+var func = function TEST () {
+    
 };
 
 console.time("parse");