diff options
author | Mihai Bazon <mihai@bazon.net> | 2012-05-27 17:25:31 +0300 |
---|---|---|
committer | Mihai Bazon <mihai@bazon.net> | 2012-06-03 23:10:31 +0300 |
commit | 861e26a66639ca61eab2af53de45760370c4d534 (patch) | |
tree | c35c94fe6978dfdff6887a9e5ea03703f2e4bed4 /lib/parse.js | |
parent | 22bb5e8306687fb6324f094d208b564c9e874f77 (diff) | |
download | tracifyjs-861e26a66639ca61eab2af53de45760370c4d534.tar.gz tracifyjs-861e26a66639ca61eab2af53de45760370c4d534.zip |
WIP
Diffstat (limited to 'lib/parse.js')
-rw-r--r-- | lib/parse.js | 223 |
1 files changed, 148 insertions, 75 deletions
diff --git a/lib/parse.js b/lib/parse.js index 7164f3ee..9dcfa635 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -577,13 +577,13 @@ var UNARY_POSTFIX = array_to_hash([ "--", "++" ]); var ASSIGNMENT = (function(a, ret, i){ while (i < a.length) { - ret[a[i]] = a[i].substr(0, a[i].length - 1); + ret[a[i]] = a[i]; i++; } return ret; })( - ["+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&="], - { "=": true }, + [ "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=" ], + {}, 0 ); @@ -695,10 +695,11 @@ function parse($TEXT, exigent_mode) { }; function parenthesised() { - expect("("); - var ex = expression(); - expect(")"); - return ex; + return new AST_Parenthesized({ + start : expect("("), + expression : expression(), + end : expect(")") + }); }; function embed_tokens(parser) { @@ -828,8 +829,7 @@ function parse($TEXT, exigent_mode) { }); function labeled_statement() { - var label = S.token.value; - next(); + var label = as_symbol(true); expect(":"); S.labels.push(label); var start = S.token, stat = statement(); @@ -845,19 +845,21 @@ function parse($TEXT, exigent_mode) { }; function break_cont(type) { - var name = null; + var name = null, label = null; if (!can_insert_semicolon()) { name = is("name") ? S.token.value : null; } if (name != null) { next(); - if (!member(name, S.labels)) + label = find_if(function(l){ return l.name == name }, S.labels); + if (!label) croak("Label " + name + " without matching loop or statement"); + label = new AST_Label({ name: name, symbol: label }); } else if (S.in_loop == 0) croak(type.TYPE + " not inside a loop or switch"); semicolon(); - return new type({ label: name }); + return new type({ label: label }); }; function for_() { @@ -892,19 +894,19 @@ function parse($TEXT, exigent_mode) { }; function for_in(init) { - var lhs = init instanceof AST_Var ? init.definitions[0].name : init; + var lhs = init instanceof AST_Var ? init.definitions[0].name : null; var obj = expression(); expect(")"); return new AST_ForIn({ init : init, - lhs : lhs, + name : lhs, object : obj, body : in_loop(statement) }); }; var function_ = function(in_statement) { - var name = is("name") ? as_symbol() : null; + var name = is("name") ? as_symbol(true) : null; if (in_statement && !name) unexpected(); expect("("); @@ -914,7 +916,7 @@ function parse($TEXT, exigent_mode) { argnames: (function(first, a){ while (!is("punc", ")")) { if (first) first = false; else expect(","); - a.push(as_symbol()); + a.push(as_symbol(true)); } next(); return a; @@ -922,11 +924,14 @@ function parse($TEXT, exigent_mode) { body: embed_tokens(function(){ ++S.in_function; var loop = S.in_loop; + var labels = S.labels; S.in_directives = true; S.in_loop = 0; + S.labels = []; var a = block_(); --S.in_function; S.in_loop = loop; + S.labels = labels; return new AST_Bracketed({ body: a }); })() }); @@ -958,47 +963,70 @@ function parse($TEXT, exigent_mode) { var switch_block_ = embed_tokens(curry(in_loop, function(){ expect("{"); - var a = [], cur = null; + var a = [], cur = null, branch = null; while (!is("punc", "}")) { if (is("eof")) unexpected(); if (is("keyword", "case")) { - next(); + if (branch) branch.end = prev(); cur = []; - a.push(new AST_Case({ expression: expression(), body: cur })); + branch = new AST_Case({ + start : prog1(S.token, next), + expression : expression(), + body : cur + }); + a.push(branch); expect(":"); } else if (is("keyword", "default")) { - next(); - expect(":"); + if (branch) branch.end = prev(); cur = []; - a.push(new AST_Default({ body: cur })); + branch = new AST_Default({ + start : prog1(S.token, next, curry(expect, ":")), + body : cur + }) + a.push(branch); } else { if (!cur) unexpected(); cur.push(statement()); } } + if (branch) branch.end = prev(); next(); return new AST_SwitchBlock({ body: a }); })); function try_() { var body = new AST_Bracketed({ - body: block_() + start : S.token, + body : block_(), + end : prev() }), bcatch = null, bfinally = null; if (is("keyword", "catch")) { + var start = S.token; next(); expect("("); - var name = as_symbol(); + var name = as_symbol(true); expect(")"); bcatch = new AST_Catch({ + start : start, argname : name, - body : new AST_Bracketed({ body: block_() }) + body : new AST_Bracketed({ + start : S.token, + body : block_(), + end : prev() + }), + end : prev() }); } if (is("keyword", "finally")) { + var start = S.token; next(); - bfinally = new AST_Finally({ body: block_() }); + bfinally = new AST_Finally({ + start : start, + body : block_(), + end : prev() + }); } if (!bcatch && !bfinally) croak("Missing catch/finally blocks"); @@ -1014,7 +1042,7 @@ function parse($TEXT, exigent_mode) { for (;;) { a.push(new AST_VarDef({ start : S.token, - name : as_symbol(), + name : as_symbol(true), value : is("operator", "=") ? (next(), expression(false, no_in)) : null, end : prev() })); @@ -1025,19 +1053,25 @@ function parse($TEXT, exigent_mode) { return a; }; - var var_ = embed_tokens(function(no_in) { + var var_ = function(no_in) { return new AST_Var({ - definitions: vardefs(no_in) + start : prev(), + definitions : vardefs(no_in), + end : prev() }); - }); + }; - var const_ = embed_tokens(function() { + var const_ = function() { return new AST_Const({ - definitions: vardefs() + start : prev(), + definitions : vardefs(), + end : prev() }); - }); + }; - var new_ = embed_tokens(function() { + var new_ = function() { + var start = S.token; + expect_token("operator", "new"); var newexp = expr_atom(false), args; if (is("punc", "(")) { next(); @@ -1046,10 +1080,12 @@ function parse($TEXT, exigent_mode) { args = []; } return subscripts(new AST_New({ + start : start, expression : newexp, - args : args + args : args, + end : prev() }), true); - }); + }; function as_atom_node() { var tok = S.token, ret; @@ -1085,25 +1121,26 @@ function parse($TEXT, exigent_mode) { var expr_atom = function(allow_calls) { if (is("operator", "new")) { - next(); return new_(); } + var start = S.token; if (is("punc")) { - switch (S.token.value) { + switch (start.value) { case "(": next(); - return subscripts(prog1(expression, curry(expect, ")")), allow_calls); + var ex = expression(); + ex.start = start; + ex.end = S.token; + expect(")"); + return subscripts(ex, allow_calls); case "[": - next(); return subscripts(array_(), allow_calls); case "{": - next(); return subscripts(object_(), allow_calls); } unexpected(); } if (is("keyword", "function")) { - var start = S.token; next(); var func = function_(false); func.start = start; @@ -1131,13 +1168,15 @@ function parse($TEXT, exigent_mode) { return a; }; - function array_() { + var array_ = embed_tokens(function() { + expect("["); return new AST_Array({ elements: expr_list("]", !exigent_mode, true) }); - }; + }); var object_ = embed_tokens(function() { + expect("{"); var first = true, a = []; while (!is("punc", "}")) { if (first) first = false; else expect(","); @@ -1183,9 +1222,14 @@ function parse($TEXT, exigent_mode) { switch (S.token.type) { case "num": case "string": - return as_symbol(true); + case "name": + case "operator": + case "keyword": + case "atom": + return prog1(S.token.value, next); + default: + unexpected(); } - return as_name(); }; function as_name() { @@ -1194,15 +1238,16 @@ function parse($TEXT, exigent_mode) { case "operator": case "keyword": case "atom": - return as_symbol(true); + return prog1(S.token.value, next); default: unexpected(); } }; - function as_symbol(noerror) { - if (!noerror && !is("name")) croak("Name expected"); - var sym = new AST_Symbol({ + function as_symbol(def) { + if (!is("name")) croak("Name expected"); + var name = S.token.value; + var sym = new (name == "this" ? AST_This : def ? AST_Symbol : AST_SymbolRef)({ name : String(S.token.value), start : S.token, end : S.token @@ -1211,44 +1256,59 @@ function parse($TEXT, exigent_mode) { return sym; }; - var subscripts = embed_tokens(function(expr, allow_calls) { + var subscripts = function(expr, allow_calls) { + var start = expr.start; if (is("punc", ".")) { next(); return subscripts(new AST_Dot({ + start : start, expression : expr, - property : as_name() + property : as_name(), + end : prev() }), allow_calls); } if (is("punc", "[")) { next(); + var prop = expression(); + expect("]"); return subscripts(new AST_Sub({ + start : start, expression : expr, - property : prog1(expression, curry(expect, "]")) + property : prop, + end : prev() }), allow_calls); } if (allow_calls && is("punc", "(")) { next(); return subscripts(new AST_Call({ + start : start, expression : expr, - args : expr_list(")") + args : expr_list(")"), + end : prev() }), true); } return expr; - }); + }; - var maybe_unary = embed_tokens(function(allow_calls) { + var maybe_unary = function(allow_calls) { + var start = S.token; if (is("operator") && HOP(UNARY_PREFIX, S.token.value)) { - return make_unary(AST_UnaryPrefix, - prog1(S.token.value, next), - maybe_unary(allow_calls)); + var ex = make_unary(AST_UnaryPrefix, + prog1(S.token.value, next), + maybe_unary(allow_calls)); + ex.start = start; + ex.end = prev(); + return ex; } var val = expr_atom(allow_calls); while (is("operator") && HOP(UNARY_POSTFIX, S.token.value) && !S.token.nlb) { val = make_unary(AST_UnaryPostfix, S.token.value, val); + val.start = start; + val.end = S.token; next(); } return val; - }); + }; function make_unary(ctor, op, expr) { if ((op == "++" || op == "--") && !is_assignable(expr)) @@ -1256,7 +1316,7 @@ function parse($TEXT, exigent_mode) { return new ctor({ operator: op, expression: expr }); }; - var expr_op = embed_tokens(function(left, min_prec, no_in) { + var expr_op = function(left, min_prec, no_in) { var op = is("operator") ? S.token.value : null; if (op == "in" && no_in) op = null; var prec = op != null ? PRECEDENCE[op] : null; @@ -1264,32 +1324,37 @@ function parse($TEXT, exigent_mode) { next(); var right = expr_op(maybe_unary(true), prec, no_in); return expr_op(new AST_Binary({ + start : left.start, left : left, operator : op, - right : right + right : right, + end : right.end }), min_prec, no_in); } return left; - }); + }; function expr_ops(no_in) { return expr_op(maybe_unary(true), 0, no_in); }; - var maybe_conditional = embed_tokens(function(no_in) { + var maybe_conditional = function(no_in) { + var start = S.token; var expr = expr_ops(no_in); if (is("operator", "?")) { next(); var yes = expression(false); expect(":"); return new AST_Conditional({ - condition: expr, - consequent: yes, - alternative: expression(false, no_in) + start : start, + condition : expr, + consequent : yes, + alternative : expression(false, no_in), + end : peek() }); } return expr; - }); + }; function is_assignable(expr) { if (!exigent_mode) return true; @@ -1304,35 +1369,41 @@ function parse($TEXT, exigent_mode) { } }; - var maybe_assign = embed_tokens(function(no_in) { + var maybe_assign = function(no_in) { + var start = S.token; var left = maybe_conditional(no_in), val = S.token.value; if (is("operator") && HOP(ASSIGNMENT, val)) { if (is_assignable(left)) { next(); return new AST_Assign({ + start : start, left : left, operator : ASSIGNMENT[val], - right : maybe_assign(no_in) + right : maybe_assign(no_in), + end : peek() }); } croak("Invalid assignment"); } return left; - }); + }; - var expression = embed_tokens(function(commas, no_in) { + var expression = function(commas, no_in) { if (arguments.length == 0) commas = true; + var start = S.token; var expr = maybe_assign(no_in); if (commas && is("punc", ",")) { next(); return new AST_Seq({ + start : start, first : expr, - second : expression(true, no_in) + second : expression(true, no_in), + end : peek() }); } return expr; - }); + }; function in_loop(cont) { ++S.in_loop; @@ -1342,11 +1413,13 @@ function parse($TEXT, exigent_mode) { }; return new AST_Toplevel({ + start: S.token, body: (function(a){ while (!is("eof")) a.push(statement()); return a; - })([]) + })([]), + end: prev() }); }; |