Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

first

  • Loading branch information...
commit e4490a88ea02bc944b69f8b94deb29210a29143f 0 parents
@zaach zaach authored
0  .gitignore
No changes.
13 Makefile
@@ -0,0 +1,13 @@
+all: build test
+
+build: move
+ jison lib/grammar.y lib/lexer.l
+ mv grammar.js dist/parser.js
+
+move: lib
+ cp lib/*.js dist/
+
+
+test: move dist
+ node test/reflect-parse.js
+
14 README.md
@@ -0,0 +1,14 @@
+![Reflect.js](reflectjs.png "Reflect.js")
+
+Reflect.js is a pure JavaScript implementation of [Mozilla's Parser API](https://developer.mozilla.org/en/SpiderMonkey/Parser_API). It does not currently support Mozilla specific extentions such as `let`, generators, list comprehensions, patterns, E4X, etc. but may eventually support ones that are/become Harmony proposals. *I'll probably add patterns next because those are pretty sweet.*
+
+Builders are also supported.
+
+Parsing large files can be slow, for reasons [articulated](http://www.andychu.net/ecmascript/RegExp-Enhancements.html) by Andy Chu.
+
+Install
+=======
+
+ npm install reflect
+
+MIT X Licensed.
1  dist/.gitignore
@@ -0,0 +1 @@
+*.js
234 examples/jsonml-ast.js
@@ -0,0 +1,234 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var Reflect = require('reflect').Reflect;
+
+// A simple proof-of-concept that the builder API can be used to generate other
+// formats, such as JsonMLAst:
+//
+// http://code.google.com/p/es-lab/wiki/JsonMLASTFormat
+//
+// It's incomplete (e.g., it doesn't convert source-location information and
+// doesn't use all the direct-eval rules), but I think it proves the point.
+
+var JsonMLAst = (function() {
+function reject() {
+ throw new SyntaxError("node type not supported");
+}
+
+function isDirectEval(expr) {
+ // an approximation to the actual rules. you get the idea
+ return (expr[0] === "IdExpr" && expr[1].name === "eval");
+}
+
+function functionNode(type) {
+ return function(id, args, body, isGenerator, isExpression) {
+ if (isExpression)
+ body = ["ReturnStmt", {}, body];
+
+ if (!id)
+ id = ["Empty"];
+
+ // Patch up the argument node types: s/IdExpr/IdPatt/g
+ for (var i = 0; i < args.length; i++) {
+ args[i][0] = "IdPatt";
+ }
+
+ args.unshift("ParamDecl", {});
+
+ return [type, {}, id, args, body];
+ }
+}
+
+return {
+ program: function(stmts) {
+ stmts.unshift("Program", {});
+ return stmts;
+ },
+ identifier: function(name) {
+ return ["IdExpr", { name: name }];
+ },
+ literal: function(val) {
+ return ["LiteralExpr", { value: val }];
+ },
+ expressionStatement: function(expr) {return expr},
+ conditionalExpression: function(test, cons, alt) {
+ return ["ConditionalExpr", {}, test, cons, alt];
+ },
+ unaryExpression: function(op, expr) {
+ return ["UnaryExpr", {op: op}, expr];
+ },
+ binaryExpression: function(op, left, right) {
+ return ["BinaryExpr", {op: op}, left, right];
+ },
+ property: function(kind, key, val) {
+ return [kind === "init"
+ ? "DataProp"
+ : kind === "get"
+ ? "GetterProp"
+ : "SetterProp",
+ {name: key[1].name}, val];
+ },
+ functionDeclaration: functionNode("FunctionDecl"),
+ variableDeclaration: function(kind, elts) {
+ if (kind === "let" || kind === "const")
+ throw new SyntaxError("let and const not supported");
+ elts.unshift("VarDecl", {});
+ return elts;
+ },
+ variableDeclarator: function(id, init) {
+ id[0] = "IdPatt";
+ if (!init)
+ return id;
+ return ["InitPatt", {}, id, init];
+ },
+ sequenceExpression: function(exprs) {
+ var length = exprs.length;
+ var result = ["BinaryExpr", {op:","}, exprs[exprs.length - 2], exprs[exprs.length - 1]];
+ for (var i = exprs.length - 3; i >= 0; i--) {
+ result = ["BinaryExpr", {op:","}, exprs[i], result];
+ }
+ return result;
+ },
+ assignmentExpression: function(op, lhs, expr) {
+ return ["AssignExpr", {op: op}, lhs, expr];
+ },
+ logicalExpression: function(op, left, right) {
+ return [op === "&&" ? "LogicalAndExpr" : "LogicalOrExpr", {}, left, right];
+ },
+ updateExpression: function(expr, op, isPrefix) {
+ return ["CountExpr", {isPrefix:isPrefix, op:op}, expr];
+ },
+ newExpression: function(callee, args) {
+ args.unshift("NewExpr", {}, callee);
+ return args;
+ },
+ callExpression: function(callee, args) {
+ args.unshift(isDirectEval(callee) ? "EvalExpr" : "CallExpr", {}, callee);
+ return args;
+ },
+ memberExpression: function(isComputed, expr, member) {
+ return ["MemberExpr", {}, expr, isComputed ? member : ["LiteralExpr", {type: "string", value: member[1].name}]];
+ },
+ functionExpression: functionNode("FunctionExpr"),
+ arrayExpression: function(elts) {
+ for (var i = 0; i < elts.length; i++) {
+ if (!elts[i])
+ elts[i] = ["Empty"];
+ }
+ elts.unshift("ArrayExpr", {});
+ return elts;
+ },
+ objectExpression: function(props) {
+ props.unshift("ObjectExpr", {});
+ return props;
+ },
+ thisExpression: function() {
+ return ["ThisExpr", {}];
+ },
+
+ graphExpression: reject,
+ graphIndexExpression: reject,
+ comprehensionExpression: reject,
+ generatorExpression: reject,
+ yieldExpression: reject,
+ letExpression: reject,
+
+ emptyStatement: function() {return ["EmptyStmt", {}] },
+ blockStatement: function(stmts) {
+ stmts.unshift("BlockStmt", {});
+ return stmts;
+ },
+ labeledStatement: function(lab, stmt) {
+ return ["LabelledStmt", {label: lab}, stmt];
+ },
+ ifStatement: function(test, cons, alt) {
+ return ["IfStmt", {}, test, cons, alt || ["EmptyStmt", {}]];
+ },
+ switchStatement: function(test, clauses, isLexical) {
+ clauses.unshift("SwitchStmt", {}, test);
+ return clauses;
+ },
+ whileStatement: function(expr, stmt) {
+ return ["WhileStmt", {}, expr, stmt];
+ },
+ doWhileStatement: function(stmt, expr) {
+ return ["DoWhileStmt", {}, stmt, expr];
+ },
+ forStatement: function(init, test, update, body) {
+ return ["ForStmt", {}, init || ["Empty"], test || ["Empty"], update || ["Empty"], body];
+ },
+ forInStatement: function(lhs, rhs, body) {
+ return ["ForInStmt", {}, lhs, rhs, body];
+ },
+ breakStatement: function(lab) {
+ return lab ? ["BreakStmt", {}, lab] : ["BreakStmt", {}];
+ },
+ continueStatement: function(lab) {
+ return lab ? ["ContinueStmt", {}, lab] : ["ContinueStmt", {}];
+ },
+ withStatement: function(expr, stmt) {
+ return ["WithStmt", {}, expr, stmt];
+ },
+ returnStatement: function(expr) {
+ return expr ? ["ReturnStmt", {}, expr] : ["ReturnStmt", {}];
+ },
+ tryStatement: function(body, catches, fin) {
+ if (catches.length > 1)
+ throw new SyntaxError("multiple catch clauses not supported");
+ var node = ["TryStmt", body, catches[0] || ["Empty"]];
+ if (fin)
+ node.push(fin);
+ return node;
+ },
+ throwStatement: function(expr) {
+ return ["ThrowStmt", {}, expr];
+ },
+ debuggerStatement: function(){ return ["DebuggerStmt", {}] },
+ letStatement: reject,
+ switchCase: function(expr, stmts) {
+ if (expr)
+ stmts.unshift("SwitchCase", {}, expr);
+ else
+ stmts.unshift("DefaultCase", {});
+ return stmts;
+ },
+ catchClause: function(param, guard, body) {
+ if (guard)
+ throw new SyntaxError("catch guards not supported");
+ param[0] = "IdPatt";
+ return ["CatchClause", {}, param, body];
+ },
+ comprehensionBlock: reject,
+
+ arrayPattern: reject,
+ objectPattern: reject,
+ propertyPattern: reject,
+
+ xmlAnyName: reject,
+ xmlAttributeSelector: reject,
+ xmlEscape: reject,
+ xmlFilterExpression: reject,
+ xmlDefaultDeclaration: reject,
+ xmlQualifiedIdentifier: reject,
+ xmlFunctionQualifiedIdentifier: reject,
+ xmlElement: reject,
+ xmlText: reject,
+ xmlList: reject,
+ xmlStartTag: reject,
+ xmlEndTag: reject,
+ xmlPointTag: reject,
+ xmlName: reject,
+ xmlAttribute: reject,
+ xmlCdata: reject,
+ xmlComment: reject,
+ xmlProcessingInstruction: reject
+};
+})();
+
+console.log(JSON.stringify(Reflect.parse("2 + (-x * y)", {loc: false, builder: JsonMLAst}),null," "));
+
+
215 lib/codegen.js
@@ -0,0 +1,215 @@
+
+var indentChar = ' '; //4 spaces
+
+function idt (lvl) {
+ if (lvl < 0) lvl = 0;
+ return Array(lvl+1).join(indentChar);
+}
+
+var codegens = exports.nodes = {
+ 'EmptyStatement': function Empty_codegen () {
+ return '';
+ },
+ 'Literal': function LiteralExpr_codegen () {
+ switch (this.type) {
+ case 'null':
+ return 'null';
+ case 'string':
+ return '"'+this.value+'"';
+ default:
+ return this.value.toString();
+ }
+ },
+ 'ObjectExpression': function ObjectExpr_codegen (lvl) {
+ return this.children.length ? "{\n"+idt(lvl)+this.children.map(function(node){return node.toJS(lvl+1)}).join(',\n'+idt(lvl))+'\n'+idt(lvl-1)+'}':
+ '{}';
+ },
+ 'ThisExpression': function ThisExpression_codegen (lvl) {
+ return 'this';
+ },
+ 'ArrayExpression': function ObjectExpr_codegen (lvl) {
+ return "["+this.children.map(function(node){return node.toJS(lvl+1)}).join(', ')
+ +(this.children.length && this.children[this.children.length-1].nodeType === 'Empty' ? ',':'')
+ +"]";
+ },
+ 'MemberExpression': function MemberExpr_codegen (lvl) {
+ return this.children[0].toJS(lvl+1) +
+ (this.brackets ?
+ '['+this.children[1].toJS(lvl+1)+']' :
+ '.'+this.children[1].value);
+ },
+ 'NewExpression': function MemberExpr_codegen (lvl) {
+ return 'new '+this.children[0].toJS(lvl+1) +
+ (this.children.length > 1 ?
+ '('+this.children.slice(1).map(function(node){return node.toJS(lvl+1)}).join(', ')+')' : '()');
+ },
+ 'CallExpression': function CappExpr_codegen (lvl) {
+ return this.children[0].toJS(lvl+1) +
+ (this.children.length > 1 ?
+ '('+this.children.slice(1).map(function(node){return node.toJS(lvl+1)}).join(', ')+')' : '()');
+ },
+ 'UpdateExpression': function CountExpr_codegen (lvl) {
+ return this.isPrefix ?
+ this.op + this.children[0].toJS(lvl+1) :
+ this.children[0].toJS(lvl+1) + this.op;
+ },
+ //'DeleteExpression': function DeleteExpr_codegen (lvl) {
+ //return 'delete '+this.children[0].toJS(lvl+1);
+ //},
+ //'VoidExpression': function VoidExpr_codegen (lvl) {
+ //return 'void '+this.children[0].toJS(lvl+1);
+ //},
+ //'TypeofExpression': function TypeofExpr_codegen (lvl) {
+ //return 'typeof '+this.children[0].toJS(lvl+1);
+ //},
+ 'UnaryExpression': function UnaryPlusExpr_codegen (lvl) {
+ return '+ '+this.children[0].toJS(lvl+1);
+ },
+ 'ConditionalExpression': function ConditionalExpr_codegen (lvl) {
+ return this.children[0].toJS(lvl+1) + ' ? ' +
+ this.children[1].toJS(lvl+1) +' : '+
+ this.children[2].toJS(lvl+1);
+ },
+ 'AssignmentExpression': function AssignExpr_codegen (lvl) {
+ return this.children[0].toJS(lvl+1) +' '+ this.op +' '+
+ this.children[1].toJS(lvl+1);
+ },
+ 'BinaryExpression': function BinaryExpr_codegen (lvl) {
+ return this.children[0].toJS(lvl+1) + this.op +' '+ this.children[1].toJS(lvl+1);
+ },
+ 'BlockStatement': function BlockStmt_codegen (lvl) {
+ return this.body.length ? "{\n"+idt(lvl)+genStmts(this.body, lvl)+"\n"+idt(lvl-1)+"}" :
+ '{ }';
+ },
+ 'VariableDeclaration': function VariableDeclaration_codegen (lvl) {
+ return "var "+this.children.map(function(node){return node.toJS(lvl);}).join(',\n'+idt(lvl+1));
+ },
+ 'InitPatt': function InitPatt_codegen (lvl) {
+ return this.children[0].toJS(lvl) +' = '+
+ this.children[1].toJS(lvl);
+ },
+ 'IfStatement': function IfStmt_codegen (lvl) {
+ return 'if (' + this.children[0].toJS(lvl+1) +') '+ this.children[1].blockgen(lvl+1)
+ + (this.children[2].nodeType === 'EmptyStatement' ? '' :
+ ' else '+this.children[2].blockgen(lvl));
+ },
+ 'DoWhileStatement': function DoWhileStmt_codegen (lvl) {
+ return 'do '+this.children[0].blockgen(lvl+1) + ' while (' + this.children[1].toJS(lvl+1) +')';
+ },
+ 'WhileStatement': function WhileStmt_codegen (lvl) {
+ return 'while (' + this.children[0].toJS(lvl+1) +') '+ this.children[1].blockgen(lvl+1);
+ },
+ 'ForStatement': function ForStmt_codegen (lvl) {
+ return 'for (' + this.children[0].toJS(lvl+1) +
+ ';' + this.children[1].toJS(lvl+1) +
+ ';' + this.children[2].toJS(lvl+1) +') '+ this.children[3].blockgen(lvl+1);
+ },
+ 'ForInStatement': function ForStmt_codegen (lvl) {
+ return 'for (' + this.children[0].toJS(lvl+1) +
+ ' in ' + this.children[1].toJS(lvl+1) +') '+ this.children[2].blockgen(lvl+1);
+ },
+ 'ContinueStatement': function ContinueStmt_codegen (lvl) {
+ return 'continue' + (this.label ? ' '+this.label : '');
+ },
+ 'BreakStatement': function BreakStmt_codegen (lvl) {
+ return 'break' + (this.label ? ' '+this.label : '');
+ },
+ 'ReturnStatement': function ReturnStatement_codegen (lvl) {
+ return 'return' + (this.children.length ? ' '+this.children[0].toJS(lvl+1) : '');
+ },
+ 'WithStatement': function WithStmt_codegen (lvl) {
+ return 'with (' +this.children[0].toJS() + ') ' + this.children[1].blockgen(lvl);
+ },
+ 'SwitchStatement': function SwitchStmt_codegen (lvl) {
+ return 'switch ('+this.children[0].toJS()+') {\n'+idt(lvl)
+ +this.children.slice(1).map(function(node){return node.toJS(lvl+1)}).join('\n'+idt(lvl))
+ +'\n'+idt(lvl-1)+'}';
+ },
+ 'SwitchCase': function Case_codegen (lvl) {
+ return 'case ' +this.children[0].toJS()+':\n'+idt(lvl)+this.children.slice(1).map(function(node){return node.blockgen(lvl+1)}).join('\n'+idt(lvl));
+ },
+ 'LabeledStatement': function LabelledStmt_codegen (lvl) {
+ return 'labelled:\n'+idt(lvl) +this.children[0].blockgen(lvl+1);
+ },
+ 'ThrowStatement': function ThrowStmt_codegen (lvl) {
+ return 'throw ' + this.children[0].toJS(lvl+1);
+ },
+ 'TryStatement': function TryStmt_codegen (lvl) {
+ return 'try ' + this.children[0].toJS(lvl+1)
+ +this.children[1].toJS(lvl+1)
+ +(this.children.length === 3 ?
+ ' finally '+this.children[2].toJS(lvl+1) : '');
+ },
+ 'CatchClause': function CatchClause_codegen (lvl) {
+ return ' catch (' + this.children[0].toJS(lvl) +') '+this.children[1].toJS(lvl);
+ },
+ 'DebuggerStatement': function DebuggerStmt_codegen (lvl) {
+ return 'debugger';
+ },
+ 'FunctionDeclaration': function FunctionDeclaration_codegen (lvl) {
+ return 'function ' + this.children[0].toJS() +' ('
+ +this.children[1].toJS(lvl+1) + ') '
+ +'{\n'+idt(lvl)+genStmts(this.children.slice(2), lvl) +'\n'+idt(lvl-1)+'}';
+ },
+ 'FunctionExpression': function FunctionExpr_codegen (lvl) {
+ return 'function' + (this.children[0].nodeType === 'Empty' ? '' : ' '+this.children[0].toJS()) +' ('
+ +this.children[1].toJS() + ') '
+ +'{\n'+idt(lvl)+genStmts(this.children.slice(2), lvl) +'\n'+idt(lvl-1)+'}';
+ },
+ 'Program': function Program_codegen (lvl) {
+ return this.children.map(function(node){return node.blockgen(lvl+1)}).join('\n')+'\n';
+ }
+};
+
+function binaryExprCodegen (lvl) {
+ return this.children[0].toJS(lvl+1) +' '+ this.op +' '+ this.children[1].toJS(lvl+1);
+}
+
+// convenience function for generating a list of statements
+function genStmts (nodes, lvl) {
+ return nodes.map(function(node){return node.blockgen(lvl)}).join('\n'+idt(lvl));
+}
+
+// convenience function for generating code as a statement (default is expr)
+function blockgen () { return this.stmtWrap(this.toJS.apply(this, arguments)); }
+
+// Most statements end with a semicolon
+function defaultStmtWrap (source) { return source+';'; }
+
+// But some (like while-loop, if-then, etc) do not
+function idWrap (source) { return source; }
+
+// Wrap epressions in parens if they were in original source
+function curryParenWrap (fun) { return function (lvl) { return this.parens ? '('+fun.call(this, lvl)+')' : fun.call(this, lvl); }; }
+
+// Overwrite defaultStmtWrap for specific nodes
+var wraps = {
+ // objects must be wrapped with parens to disambiguate from blocks
+ 'ObjectExpression': function (source) { return defaultStmtWrap(this.parens ? source : '('+source+')'); },
+ 'BlockStatement': idWrap,
+ 'WhileStatement': idWrap,
+ 'WithStatement': idWrap,
+ 'ForStatement': idWrap,
+ 'ForInStatement': idWrap,
+ 'IfStatement': idWrap,
+ 'SwitchStatement': idWrap,
+ 'LabeledStatement': idWrap,
+ 'TryStatement': idWrap,
+ // func-expr must be wrapped with parens to disambiguate from func-decl
+ 'FunctionExpression': function (source) { return defaultStmtWrap(this.parens ? source : '('+source+')'); },
+ 'FunctionDeclaration': idWrap,
+ 'Program': idWrap
+};
+
+
+// Extend the node prototypes to include code generation functions
+exports.extend = function extend (protos) {
+ var type;
+ protos.base.stmtWrap = defaultStmtWrap;
+ protos.base.blockgen = blockgen;
+ for (type in codegens) {
+ protos[type].toJS = curryParenWrap(codegens[type]);
+ if (type in wraps) protos[type].stmtWrap = wraps[type];
+ }
+};
+
910 lib/grammar.y
@@ -0,0 +1,910 @@
+%start Program
+
+%nonassoc IF_WITHOUT_ELSE
+%nonassoc ELSE
+
+%%
+
+IdentifierName
+ : IDENT
+ | NULLTOKEN
+ | TRUETOKEN
+ | FALSETOKEN
+ | BREAK
+ | CASE
+ | CATCH
+ | CONTINUE
+ | DEBUGGER
+ | DEFAULT
+ | DELETETOKEN
+ | DO
+ | ELSE
+ | FINALLY
+ | FOR
+ | FUNCTION
+ | IF
+ | INTOKEN
+ | INSTANCEOF
+ | NEW
+ | RETURN
+ | SWITCH
+ | THIS
+ | THROW
+ | TRY
+ | TYPEOF
+ | VAR
+ | VOIDTOKEN
+ | WHILE
+ | WITH
+ ;
+
+Literal
+ : NULLTOKEN
+ { $$ = yy.Node('Literal', null, yy.loc(@1)); }
+ | TRUETOKEN
+ { $$ = yy.Node('Literal', true, yy.loc(@1)); }
+ | FALSETOKEN
+ { $$ = yy.Node('Literal', false, yy.loc(@1)); }
+ | NUMBER
+ { $$ = yy.Node('Literal', Number($1), yy.loc(@1)); }
+ | STRING
+ { $$ = yy.Node('Literal', yy.escapeString(String($1)), yy.loc(@1)); }
+ | RegularExpressionLiteralBegin REGEXP_BODY
+ {{
+ var body = yytext.slice(1,yytext.lastIndexOf('/'));
+ var flags = yytext.slice(yytext.lastIndexOf('/')+1);
+ $$ = yy.Node('Literal', new RegExp(body, flags), yy.loc(yy.locComb(@$,@2)));
+ //$$ = yy.Node('RegExpExpression', {body:body,flags:flags});
+ yy.inRegex = false;
+ }}
+ ;
+
+RegularExpressionLiteralBegin
+ : '/'
+ { yy.inRegex = true; yy.lexer.unput($1); $$ = $1; }
+ | DIVEQUAL
+ { yy.inRegex = true; yy.lexer.unput($1); $$ = $1; }
+ ;
+
+Property
+ : IdentifierName ':' AssignmentExpr
+ {{ yy.locComb(@$,@3);$$ = {key:yy.Node('Identifier', $1,yy.loc(@1)),value:$3,kind: "init"}; }}
+ | STRING ':' AssignmentExpr
+ {{ yy.locComb(@$,@3);$$ = {key:yy.Node('Literal', String($1),yy.loc(@1)),value:$3,kind: "init"}; }}
+ | NUMBER ':' AssignmentExpr
+ {{ yy.locComb(@$,@3);$$ = {key:yy.Node('Literal', Number($1),yy.loc(@1)),value:$3,kind: "init"}; }}
+ | IDENT IdentifierName '(' ')' Block
+ {{
+ if ($1 !== 'get' && $1 !== 'set') throw new Error('Parse error, invalid set/get.'); // TODO: use jison ABORT when supported
+ @$ = yy.locComb(@1,@5);
+ var fun = yy.Node('FunctionExpression',null,[],$Block, false, false, yy.loc(@$));
+ $$ = {key:yy.Node('Identifier', $2,yy.loc(@2)),value:fun,kind: $1};
+ }}
+ | IDENT IdentifierName '(' FormalParameterList ')' Block
+ {{
+ @$ = yy.locComb(@1,@6);
+ if ($1 !== 'get' && $1 !== 'set') throw new Error('Parse error, invalid set/get.'); // TODO: use jison ABORT when supported
+ var fun = yy.Node('FunctionExpression',null,$FormalParameterList,$Block,false,false,yy.loc(@$));
+ $$ = {key:yy.Node('Identifier', $2,yy.loc(@2)),value:fun,kind: $1};
+ }}
+ ;
+
+PropertyList
+ : Property
+ { $$ = [$1]; }
+ | PropertyList ',' Property
+ { $$ = $1; $$.push($3); }
+ ;
+
+PrimaryExpr
+ : PrimaryExprNoBrace
+ | OPENBRACE CLOSEBRACE
+ { $$ = yy.Node('ObjectExpression',[],yy.loc([@$,@2])); }
+ | OPENBRACE PropertyList CLOSEBRACE
+ { $$ = yy.Node('ObjectExpression',$2,yy.loc([@$,@3])); }
+ | OPENBRACE PropertyList ',' CLOSEBRACE
+ { $$ = yy.Node('ObjectExpression',$2,yy.loc([@$,@4])); }
+ ;
+
+PrimaryExprNoBrace
+ : THISTOKEN
+ { $$ = yy.Node('ThisExpression'); }
+ | Literal
+ | ArrayLiteral
+ | IDENT
+ { $$ = yy.Node('Identifier', String($1), yy.loc(@1)); }
+ | '(' Expr ')'
+ { $$ = $Expr; $$.parens = true; yy.locComb(@$,@3) }
+ ;
+
+ArrayLiteral
+ : '[' ']'
+ { $$ = yy.Node('ArrayExpression',[],yy.loc([@$,@2])); }
+ | '[' Elision ']'
+ { $$ = yy.Node('ArrayExpression',$2,yy.loc([@$,@3])); }
+ | '[' ElementList ']'
+ { $$ = yy.Node('ArrayExpression',$2,yy.loc([@$,@3])); }
+ | '[' ElementList ',' ElisionOpt ']'
+ { $$ = yy.Node('ArrayExpression',$2.concat($4),yy.loc([@$,@5]));}
+ ;
+
+ElementList
+ : AssignmentExpr
+ { $$ = [$1]; }
+ | Elision AssignmentExpr
+ { $$ = $1; $$.push($2); }
+ | ElementList ',' ElisionOpt AssignmentExpr
+ { $$ = $1.concat($3); $$.push($4); }
+ ;
+
+ElisionOpt
+ :
+ { $$ = []; }
+ | Elision
+ ;
+
+Elision
+ : ','
+ { $$ = [,]; }
+ | Elision ','
+ { $$ = $1; $$.length = $$.length+1; }
+ ;
+
+MemberExpr
+ : PrimaryExpr
+ | FunctionExpr
+ | MemberExpr '[' Expr ']'
+ { $$ = yy.Node('MemberExpression',$1,$3,true,yy.loc([@$,@4])); }
+ | MemberExpr '.' IdentifierName
+ { $$ = yy.Node('MemberExpression',$1,yy.Node('Identifier', String($3)),false,yy.loc([@$,@3])); }
+ | NEW MemberExpr Arguments
+ { $$ = yy.Node('NewExpression',$MemberExpr,$Arguments,yy.loc([@$,@3])); }
+ ;
+
+MemberExprNoBF
+ : PrimaryExprNoBrace
+ | MemberExprNoBF '[' Expr ']'
+ { $$ = yy.Node('MemberExpression',$1,$3,true,yy.loc([@$,@4])); }
+ | MemberExprNoBF '.' IdentifierName
+ { $$ = yy.Node('MemberExpression',$1,yy.Node('Identifier', String($3)),false,yy.loc([@$,@3])); }
+ | NEW MemberExpr Arguments
+ { $$ = yy.Node('NewExpression',$MemberExpr,$Arguments,yy.loc([@$,@3])); }
+ ;
+
+NewExpr
+ : MemberExpr
+ | NEW NewExpr
+ { $$ = yy.Node('NewExpression',$2,[],yy.loc([@$,@2])); }
+ ;
+
+NewExprNoBF
+ : MemberExprNoBF
+ | NEW NewExpr
+ { $$ = yy.Node('NewExpression',$2,[],yy.loc([@$,@2])); }
+ ;
+
+CallExpr
+ : MemberExpr Arguments
+ { $$ = yy.Node('CallExpression',$1,$2,yy.loc([@$,@2])); }
+ | CallExpr Arguments
+ { $$ = yy.Node('CallExpression',$1,$2,yy.loc([@$,@2])); }
+ | CallExpr '[' Expr ']'
+ { $$ = yy.Node('MemberExpression',$1,$3,true,yy.loc([@$,@4])); }
+ | CallExpr '.' IdentifierName
+ { $$ = yy.Node('MemberExpression',$1,yy.Node('Identifier', String($3)),false,yy.loc([@$,@3])); }
+ ;
+
+CallExprNoBF
+ : MemberExprNoBF Arguments
+ { $$ = yy.Node('CallExpression',$1,$2,yy.loc([@$,@2])); }
+ | CallExprNoBF Arguments
+ { $$ = yy.Node('CallExpression',$1,$2,yy.loc([@$,@2])); }
+ | CallExprNoBF '[' Expr ']'
+ { $$ = yy.Node('MemberExpression',$1,$3,true,yy.loc([@$,@4])); }
+ | CallExprNoBF '.' IdentifierName
+ { $$ = yy.Node('MemberExpression',$1,yy.Node('Identifier', String($3)),false,yy.loc([@$,@3])); }
+ ;
+
+Arguments
+ : '(' ')'
+ { $$ = []; }
+ | '(' ArgumentList ')'
+ { $$ = $ArgumentList; }
+ ;
+
+ArgumentList
+ : AssignmentExpr
+ { $$ = [$1]; }
+ | ArgumentList ',' AssignmentExpr
+ { $$ = $1; $$.push($3); }
+ ;
+
+LeftHandSideExpr
+ : NewExpr
+ | CallExpr
+ ;
+
+LeftHandSideExprNoBF
+ : NewExprNoBF
+ | CallExprNoBF
+ ;
+
+PostfixExpr
+ : LeftHandSideExpr
+ | LeftHandSideExpr PLUSPLUS
+ { $$ = yy.Node('UpdateExpression','++',$1,false,yy.loc([@$,@2])); }
+ | LeftHandSideExpr MINUSMINUS
+ { $$ = yy.Node('UpdateExpression','--',$1,false,yy.loc([@$,@2])); }
+ ;
+
+PostfixExprNoBF
+ : LeftHandSideExprNoBF
+ | LeftHandSideExprNoBF PLUSPLUS
+ { $$ = yy.Node('UpdateExpression','++',$1,false,yy.loc([@$,@2])); }
+ | LeftHandSideExprNoBF MINUSMINUS
+ { $$ = yy.Node('UpdateExpression','--',$1,false,yy.loc([@$,@2])); }
+ ;
+
+UnaryExprCommon
+ : DELETETOKEN UnaryExpr
+ { $$ = yy.Node('UnaryExpression','delete',$2,yy.loc([@$,@2])); }
+ | VOIDTOKEN UnaryExpr
+ { $$ = yy.Node('UnaryExpression','void',$2,yy.loc([@$,@2])); }
+ | TYPEOF UnaryExpr
+ { $$ = yy.Node('UnaryExpression','typeof',$2,yy.loc([@$,@2])); }
+ | PLUSPLUS UnaryExpr
+ { $$ = yy.Node('UpdateExpression','++',$2,true,yy.loc([@$,@2])); }
+ | MINUSMINUS UnaryExpr
+ { $$ = yy.Node('UpdateExpression','--',$2,true,yy.loc([@$,@2])); }
+ | '+' UnaryExpr
+ { $$ = yy.Node('UnaryExpression','+',$2,yy.loc([@$,@2])); }
+ | '-' UnaryExpr
+ { $$ = yy.Node('UnaryExpression','-',$2,yy.loc([@$,@2])); }
+ | '~' UnaryExpr
+ { $$ = yy.Node('UnaryExpression','~',$2,yy.loc([@$,@2])); }
+ | '!' UnaryExpr
+ { $$ = yy.Node('UnaryExpression','!',$2,yy.loc([@$,@2])); }
+ ;
+
+UnaryExpr
+ : PostfixExpr
+ | UnaryExprCommon
+ ;
+
+UnaryExprNoBF
+ : PostfixExprNoBF
+ | UnaryExprCommon
+ ;
+
+MultiplicativeExpr
+ : UnaryExpr
+ | MultiplicativeExpr '*' UnaryExpr
+ { $$ = yy.Node('BinaryExpression', '*', $1, $3, yy.loc([@$,@3])); }
+ | MultiplicativeExpr '/' UnaryExpr
+ { $$ = yy.Node('BinaryExpression', '/', $1, $3,yy.loc([@$,@3])); }
+ | MultiplicativeExpr '%' UnaryExpr
+ { $$ = yy.Node('BinaryExpression', '%', $1, $3,yy.loc([@$,@3])); }
+ ;
+
+MultiplicativeExprNoBF
+ : UnaryExprNoBF
+ | MultiplicativeExprNoBF '*' UnaryExpr
+ { $$ = yy.Node('BinaryExpression', '*', $1, $3,yy.loc([@$,@3])); }
+ | MultiplicativeExprNoBF '/' UnaryExpr
+ { $$ = yy.Node('BinaryExpression', '/', $1, $3,yy.loc([@$,@3])); }
+ | MultiplicativeExprNoBF '%' UnaryExpr
+ { $$ = yy.Node('BinaryExpression', '%', $1, $3,yy.loc([@$,@3])); }
+ ;
+
+AdditiveExpr
+ : MultiplicativeExpr
+ | AdditiveExpr '+' MultiplicativeExpr
+ { $$ = yy.Node('BinaryExpression', '+', $1, $3,yy.loc([@$,@3])); }
+ | AdditiveExpr '-' MultiplicativeExpr
+ { $$ = yy.Node('BinaryExpression', '-', $1, $3,yy.loc([@$,@3])); }
+ ;
+
+AdditiveExprNoBF
+ : MultiplicativeExprNoBF
+ | AdditiveExprNoBF '+' MultiplicativeExpr
+ { @$ = yy.locComb(@1,@3);
+ $$ = yy.Node('BinaryExpression', '+', $1, $3, yy.loc(@$)); }
+ | AdditiveExprNoBF '-' MultiplicativeExpr
+ { @$ = yy.locComb(@1,@3);
+ $$ = yy.Node('BinaryExpression', '-', $1, $3, yy.loc(@$)); }
+ ;
+
+ShiftExpr
+ : AdditiveExpr
+ | ShiftExpr LSHIFT AdditiveExpr
+ { $$ = yy.Node('BinaryExpression', '<<', $1, $3,yy.loc([@$,@3])); }
+ | ShiftExpr RSHIFT AdditiveExpr
+ { $$ = yy.Node('BinaryExpression', '>>', $1, $3,yy.loc([@$,@3])); }
+ | ShiftExpr URSHIFT AdditiveExpr
+ { $$ = yy.Node('BinaryExpression', '>>>', $1, $3,yy.loc([@$,@3])); }
+ ;
+
+ShiftExprNoBF
+ : AdditiveExprNoBF
+ | ShiftExprNoBF LSHIFT AdditiveExpr
+ { $$ = yy.Node('BinaryExpression', '<<', $1, $3,yy.loc([@$,@3])); }
+ | ShiftExprNoBF RSHIFT AdditiveExpr
+ { $$ = yy.Node('BinaryExpression', '>>', $1, $3,yy.loc([@$,@3])); }
+ | ShiftExprNoBF URSHIFT AdditiveExpr
+ { $$ = yy.Node('BinaryExpression', '>>>', $1, $3,yy.loc([@$,@3])); }
+ ;
+
+RelationalExpr
+ : ShiftExpr
+ | RelationalExpr '<' ShiftExpr
+ { $$ = yy.Node('BinaryExpression', '<', $1, $3,yy.loc([@$,@3])); }
+ | RelationalExpr '>' ShiftExpr
+ { $$ = yy.Node('BinaryExpression', '>', $1, $3,yy.loc([@$,@3])); }
+ | RelationalExpr LE ShiftExpr
+ { $$ = yy.Node('BinaryExpression', '<=', $1, $3,yy.loc([@$,@3])); }
+ | RelationalExpr GE ShiftExpr
+ { $$ = yy.Node('BinaryExpression', '>=', $1, $3,yy.loc([@$,@3])); }
+ | RelationalExpr INSTANCEOF ShiftExpr
+ { $$ = yy.Node('BinaryExpression', 'instanceof', $1, $3,yy.loc([@$,@3])); }
+ | RelationalExpr INTOKEN ShiftExpr
+ { $$ = yy.Node('BinaryExpression', 'in', $1, $3,yy.loc([@$,@3])); }
+ ;
+
+RelationalExprNoIn
+ : ShiftExpr
+ | RelationalExprNoIn '<' ShiftExpr
+ { $$ = yy.Node('BinaryExpression', '<', $1, $3,yy.loc([@$,@3])); }
+ | RelationalExprNoIn '>' ShiftExpr
+ { $$ = yy.Node('BinaryExpression', '>', $1, $3,yy.loc([@$,@3])); }
+ | RelationalExprNoIn LE ShiftExpr
+ { $$ = yy.Node('BinaryExpression', '<=', $1, $3,yy.loc([@$,@3])); }
+ | RelationalExprNoIn GE ShiftExpr
+ { $$ = yy.Node('BinaryExpression', '>=', $1, $3,yy.loc([@$,@3])); }
+ | RelationalExprNoIn INSTANCEOF ShiftExpr
+ { $$ = yy.Node('BinaryExpression', 'instanceof', $1, $3,yy.loc([@$,@3])); }
+ ;
+
+RelationalExprNoBF
+ : ShiftExprNoBF
+ | RelationalExprNoBF '<' ShiftExpr
+ { $$ = yy.Node('BinaryExpression', '<', $1, $3,yy.loc([@$,@3])); }
+ | RelationalExprNoBF '>' ShiftExpr
+ { $$ = yy.Node('BinaryExpression', '>', $1, $3,yy.loc([@$,@3])); }
+ | RelationalExprNoBF LE ShiftExpr
+ { $$ = yy.Node('BinaryExpression', '<=', $1, $3,yy.loc([@$,@3])); }
+ | RelationalExprNoBF GE ShiftExpr
+ { $$ = yy.Node('BinaryExpression', '>=', $1, $3,yy.loc([@$,@3])); }
+ | RelationalExprNoBF INSTANCEOF ShiftExpr
+ { $$ = yy.Node('BinaryExpression', 'instanceof', $1, $3,yy.loc([@$,@3])); }
+ | RelationalExprNoBF INTOKEN ShiftExpr
+ { $$ = yy.Node('BinaryExpression', 'in', $1, $3,yy.loc([@$,@3])); }
+ ;
+
+EqualityExpr
+ : RelationalExpr
+ | EqualityExpr EQEQ RelationalExpr
+ { $$ = yy.Node('BinaryExpression', '==', $1, $3,yy.loc([@$,@3])); }
+ | EqualityExpr NE RelationalExpr
+ { $$ = yy.Node('BinaryExpression', '!=', $1, $3,yy.loc([@$,@3])); }
+ | EqualityExpr STREQ RelationalExpr
+ { $$ = yy.Node('BinaryExpression', '===', $1, $3,yy.loc([@$,@3])); }
+ | EqualityExpr STRNEQ RelationalExpr
+ { $$ = yy.Node('BinaryExpression', '!==', $1, $3,yy.loc([@$,@3])); }
+ ;
+
+EqualityExprNoIn
+ : RelationalExprNoIn
+ | EqualityExprNoIn EQEQ RelationalExprNoIn
+ { $$ = yy.Node('BinaryExpression', '==', $1, $3,yy.loc([@$,@3])); }
+ | EqualityExprNoIn NE RelationalExprNoIn
+ { $$ = yy.Node('BinaryExpression', '!=', $1, $3,yy.loc([@$,@3])); }
+ | EqualityExprNoIn STREQ RelationalExprNoIn
+ { $$ = yy.Node('BinaryExpression', '===', $1, $3,yy.loc([@$,@3])); }
+ | EqualityExprNoIn STRNEQ RelationalExprNoIn
+ { $$ = yy.Node('BinaryExpression', '!==', $1, $3,yy.loc([@$,@3])); }
+ ;
+
+EqualityExprNoBF
+ : RelationalExprNoBF
+ | EqualityExprNoBF EQEQ RelationalExpr
+ { $$ = yy.Node('BinaryExpression', '==', $1, $3,yy.loc([@$,@3])); }
+ | EqualityExprNoBF NE RelationalExpr
+ { $$ = yy.Node('BinaryExpression', '!=', $1, $3,yy.loc([@$,@3])); }
+ | EqualityExprNoBF STREQ RelationalExpr
+ { $$ = yy.Node('BinaryExpression', '===', $1, $3,yy.loc([@$,@3])); }
+ | EqualityExprNoBF STRNEQ RelationalExpr
+ { $$ = yy.Node('BinaryExpression', '!==', $1, $3,yy.loc([@$,@3])); }
+ ;
+
+BitwiseANDExpr
+ : EqualityExpr
+ | BitwiseANDExpr '&' EqualityExpr
+ { $$ = yy.Node('BinaryExpression', '&', $1, $3,yy.loc([@$,@3])); }
+ ;
+
+BitwiseANDExprNoIn
+ : EqualityExprNoIn
+ | BitwiseANDExprNoIn '&' EqualityExprNoIn
+ { $$ = yy.Node('BinaryExpression', '&', $1, $3,yy.loc([@$,@3])); }
+ ;
+
+BitwiseANDExprNoBF
+ : EqualityExprNoBF
+ | BitwiseANDExprNoBF '&' EqualityExpr
+ { $$ = yy.Node('BinaryExpression', '&', $1, $3,yy.loc([@$,@3])); }
+ ;
+
+BitwiseXORExpr
+ : BitwiseANDExpr
+ | BitwiseXORExpr '^' BitwiseANDExpr
+ { $$ = yy.Node('BinaryExpression', '^', $1, $3,yy.loc([@$,@3])); }
+ ;
+
+BitwiseXORExprNoIn
+ : BitwiseANDExprNoIn
+ | BitwiseXORExprNoIn '^' BitwiseANDExprNoIn
+ { $$ = yy.Node('BinaryExpression', '^', $1, $3,yy.loc([@$,@3])); }
+ ;
+
+BitwiseXORExprNoBF
+ : BitwiseANDExprNoBF
+ | BitwiseXORExprNoBF '^' BitwiseANDExpr
+ { $$ = yy.Node('BinaryExpression', '^', $1, $3,yy.loc([@$,@3])); }
+ ;
+
+BitwiseORExpr
+ : BitwiseXORExpr
+ | BitwiseORExpr '|' BitwiseXORExpr
+ { $$ = yy.Node('BinaryExpression', '|', $1, $3,yy.loc([@$,@3])); }
+ ;
+
+BitwiseORExprNoIn
+ : BitwiseXORExprNoIn
+ | BitwiseORExprNoIn '|' BitwiseXORExprNoIn
+ { $$ = yy.Node('BinaryExpression', '|', $1, $3,yy.loc([@$,@3])); }
+ ;
+
+BitwiseORExprNoBF
+ : BitwiseXORExprNoBF
+ | BitwiseORExprNoBF '|' BitwiseXORExpr
+ { $$ = yy.Node('BinaryExpression', '|', $1, $3,yy.loc([@$,@3])); }
+ ;
+
+LogicalANDExpr
+ : BitwiseORExpr
+ | LogicalANDExpr AND BitwiseORExpr
+ { $$ = yy.Node('LogicalExpression', '&&', $1, $3,yy.loc([@$,@3])); }
+ ;
+
+LogicalANDExprNoIn
+ : BitwiseORExprNoIn
+ | LogicalANDExprNoIn AND BitwiseORExprNoIn
+ { $$ = yy.Node('LogicalExpression', '&&', $1, $3,yy.loc([@$,@3])); }
+ ;
+
+LogicalANDExprNoBF
+ : BitwiseORExprNoBF
+ | LogicalANDExprNoBF AND BitwiseORExpr
+ { $$ = yy.Node('LogicalExpression', '&&', $1, $3,yy.loc([@$,@3])); }
+ ;
+
+LogicalORExpr
+ : LogicalANDExpr
+ | LogicalORExpr OR LogicalANDExpr
+ { $$ = yy.Node('LogicalExpression', '||', $1, $3,yy.loc([@$,@3])); }
+ ;
+
+LogicalORExprNoIn
+ : LogicalANDExprNoIn
+ | LogicalORExprNoIn OR LogicalANDExprNoIn
+ { $$ = yy.Node('LogicalExpression', '||', $1, $3,yy.loc([@$,@3])); }
+ ;
+
+LogicalORExprNoBF
+ : LogicalANDExprNoBF
+ | LogicalORExprNoBF OR LogicalANDExpr
+ { $$ = yy.Node('LogicalExpression', '||', $1, $3,yy.loc([@$,@3])); }
+ ;
+
+ConditionalExpr
+ : LogicalORExpr
+ | LogicalORExpr '?' AssignmentExpr ':' AssignmentExpr
+ { $$ = yy.Node('ConditionalExpression', $1, $3, $5,yy.loc([@$,@5])); }
+ ;
+
+ConditionalExprNoIn
+ : LogicalORExprNoIn
+ | LogicalORExprNoIn '?' AssignmentExprNoIn ':' AssignmentExprNoIn
+ { $$ = yy.Node('ConditionalExpression', $1, $3, $5,yy.loc([@$,@5])); }
+ ;
+
+ConditionalExprNoBF
+ : LogicalORExprNoBF
+ | LogicalORExprNoBF '?' AssignmentExpr ':' AssignmentExpr
+ { $$ = yy.Node('ConditionalExpression', $1, $3, $5,yy.loc([@$,@5])); }
+ ;
+
+AssignmentExpr
+ : ConditionalExpr
+ | LeftHandSideExpr AssignmentOperator AssignmentExpr
+ { $$ = yy.Node('AssignmentExpression', $2, $1, $3,yy.loc([@$,@3])); }
+ ;
+
+AssignmentExprNoIn
+ : ConditionalExprNoIn
+ | LeftHandSideExpr AssignmentOperator AssignmentExprNoIn
+ { $$ = yy.Node('AssignmentExpression', $2, $1, $3,yy.loc([@$,@3])); }
+ ;
+
+AssignmentExprNoBF
+ : ConditionalExprNoBF
+ | LeftHandSideExprNoBF AssignmentOperator AssignmentExpr
+ { $$ = yy.Node('AssignmentExpression', $2, $1, $3,yy.loc([@$,@3])); }
+ ;
+
+AssignmentOperator
+ : '='
+ | PLUSEQUAL
+ | MINUSEQUAL
+ | MULTEQUAL
+ | DIVEQUAL
+ | LSHIFTEQUAL
+ | RSHIFTEQUAL
+ | URSHIFTEQUAL
+ | ANDEQUAL
+ | XOREQUAL
+ | OREQUAL
+ | MODEQUAL
+ ;
+
+Expr
+ : AssignmentExpr
+ | Expr ',' AssignmentExpr
+ {{
+ if ($1.type == 'SequenceExpression') {
+ $1.expressions.push($3);
+ $1.loc = yy.loc([@$,@3]);
+ $$ = $1;
+ } else
+ $$ = yy.Node('SequenceExpression',[$1, $3],yy.loc([@$,@3]));
+ }}
+ ;
+
+ExprNoIn
+ : AssignmentExprNoIn
+ | ExprNoIn ',' AssignmentExprNoIn
+ {{
+ if ($1.type == 'SequenceExpression') {
+ $1.expressions.push($3);
+ $1.loc = yy.loc([@$,@3]);
+ $$ = $1;
+ } else
+ $$ = yy.Node('SequenceExpression',[$1, $3],yy.loc([@$,@3]));
+ }}
+ ;
+
+ExprNoBF
+ : AssignmentExprNoBF
+ | ExprNoBF ',' AssignmentExpr
+ {{
+ if ($1.type == 'SequenceExpression') {
+ $1.expressions.push($3);
+ $1.loc = yy.loc([@$,@3]);
+ $$ = $1;
+ } else
+ $$ = yy.Node('SequenceExpression',[$1, $3],yy.loc([@$,@3]));
+ }}
+ ;
+
+Statement
+ : Block
+ | VariableStatement
+ | ConstStatement
+ | FunctionDeclarationaration
+ | EmptyStatement
+ | ExprStatement
+ | IfStatement
+ | IterationStatement
+ | ContinueStatement
+ | BreakStatement
+ | ReturnStatement
+ | WithStatement
+ | SwitchStatement
+ | LabeledStatement
+ | ThrowStatement
+ | TryStatement
+ | DebuggerStatement
+ ;
+
+Block
+ : OPENBRACE CLOSEBRACE
+ { $$ = yy.Node('BlockStatement',[],yy.loc([@$,@2])); }
+ | OPENBRACE SourceElements CLOSEBRACE
+ { $$ = yy.Node('BlockStatement',$2,yy.loc([@$,@3])); }
+ ;
+
+ConstStatement
+ : CONSTTOKEN ConstDecralarionList ';'
+ { $$ = yy.Node('VariableDeclaration',"const",$2,yy.loc([@$,@3])) }
+ | CONSTTOKEN ConstDecralarionList error
+ { $$ = yy.Node('VariableDeclaration',"const",$2,yy.loc([@$,@2])) }
+ ;
+
+ConstDecralarionList
+ : IDENT
+ { $$ = [yy.Node('InitPatt', yy.Node('Identifier', $1,yy.loc(@1)), null)]; }
+ | IDENT Initializer
+ { yy.locComb(@$,@2);$$ = [yy.Node('InitPatt', yy.Node('Identifier', $1,yy.loc(@1)), $2)]; }
+ | ConstDecralarionList ',' IDENT
+ { yy.locComb(@$,@3);$$ = $1; $1.push(yy.Node('InitPatt', yy.Node('Identifier', $3,yy.loc(@3)), null)); }
+ | ConstDecralarionList ',' IDENT Initializer
+ { yy.locComb(@$,@4);$$ = $1; $1.push(yy.Node('InitPatt', yy.Node('Identifier', $3,yy.loc(@3)), $4)); }
+ ;
+
+VariableStatement
+ : VAR VariableDeclarationList ';'
+ { $$ = yy.Node('VariableDeclaration',"var",$2,yy.loc([@$,@3])) }
+ | VAR VariableDeclarationList error
+ { $$ = yy.Node('VariableDeclaration',"var",$2,yy.loc([@$,@2])) }
+ ;
+
+VariableDeclarationList
+ : IDENT
+ { $$ = [yy.Node('InitPatt', yy.Node('Identifier', $1,yy.loc(@1)), null)]; }
+ | IDENT Initializer
+ { yy.locComb(@$,@2);$$ = [yy.Node('InitPatt', yy.Node('Identifier', $1,yy.loc(@1)), $2)]; }
+ | VariableDeclarationList ',' IDENT
+ { yy.locComb(@$,@3);$$ = $1; $1.push(yy.Node('InitPatt', yy.Node('Identifier', $3,yy.loc(@3)), null)); }
+ | VariableDeclarationList ',' IDENT Initializer
+ { yy.locComb(@$,@4);$$ = $1; $1.push(yy.Node('InitPatt', yy.Node('Identifier', $3,yy.loc(@3)), $4)); }
+ ;
+
+VariableDeclarationListNoIn
+ : IDENT
+ { $$ = [yy.Node('InitPatt', yy.Node('Identifier', $1,yy.loc(@1)), null)]; }
+ | IDENT InitializerNoIn
+ { yy.locComb(@$,@2);
+ $$ = [yy.Node('InitPatt', yy.Node('Identifier', $1,yy.loc(@1)), $2)]; }
+ | VariableDeclarationListNoIn ',' IDENT
+ { yy.locComb(@$,@3);
+ $$ = $1; $1.push(yy.Node('InitPatt', yy.Node('Identifier', $3,yy.loc(@3)), null)); }
+ | VariableDeclarationListNoIn ',' IDENT InitializerNoIn
+ { yy.locComb(@$,@4);
+ $$ = $1; $1.push(yy.Node('InitPatt', yy.Node('Identifier', $3,yy.loc(@3)), $4)); }
+ ;
+
+Initializer
+ : '=' AssignmentExpr
+ { $$ = $2; yy.locComb(@$,@2) }
+ ;
+
+InitializerNoIn
+ : '=' AssignmentExprNoIn
+ { $$ = $2; yy.locComb(@$,@2) }
+ ;
+
+EmptyStatement
+ : ';'
+ { $$ = yy.Node('EmptyStatement',yy.loc(@1)); }
+ ;
+
+ExprStatement
+ : ExprNoBF ';'
+ { $$ = yy.Node('ExpressionStatement', $1,yy.loc([@$,@2])); }
+ | ExprNoBF error
+ { $$ = yy.Node('ExpressionStatement', $1,yy.loc(@1)); }
+ ;
+
+IfStatement
+ : IF '(' Expr ')' Statement %prec IF_WITHOUT_ELSE
+ { $$ = yy.Node('IfStatement', $Expr, $Statement, null, yy.loc([@$,@5])); }
+ | IF '(' Expr ')' Statement ELSE Statement
+ { $$ = yy.Node('IfStatement', $Expr, $Statement1, $Statement2, yy.loc([@$,@7])); }
+ ;
+
+IterationStatement
+ : DO Statement WHILE '(' Expr ')' ';'
+ { $$ = yy.Node('DoWhileStatement', $Statement, $Expr,yy.loc([@$,@7])); }
+ | DO Statement WHILE '(' Expr ')' error
+ { $$ = yy.Node('DoWhileStatement', $Statement, $Expr,yy.loc([@$,@6])); }
+ | WHILE '(' Expr ')' Statement
+ { $$ = yy.Node('WhileStatement', $Expr, $Statement,yy.loc([@$,@5])); }
+ | FOR '(' ExprNoInOpt ';' ExprOpt ';' ExprOpt ')' Statement
+ { $$ = yy.Node('ForStatement', $ExprNoInOpt, $ExprOpt1, $ExprOpt2, $Statement,yy.loc([@$,@9])); }
+ | FOR '(' VAR VariableDeclarationListNoIn ';' ExprOpt ';' ExprOpt ')' Statement
+ { $$ = yy.Node('ForStatement',
+ yy.Node('VariableDeclaration',"var", $4, yy.loc([@3,@4])),
+ $ExprOpt1, $ExprOpt2, $Statement, yy.loc([@$,@10])); }
+ | FOR '(' LeftHandSideExpr INTOKEN Expr ')' Statement
+ { $$ = yy.Node('ForInStatement', $LeftHandSideExpr, $Expr, $Statement, false, yy.loc([@$,@7])); }
+ | FOR '(' VAR IDENT INTOKEN Expr ')' Statement
+ { $$ = yy.Node('ForInStatement',
+ yy.Node('VariableDeclaration',"var",
+ [yy.Node('InitPatt',yy.Node('Identifier', $4,yy.loc(@4)),null)],
+ yy.loc([@3,@4])),
+ $Expr, $Statement, false, yy.loc([@$,@8])); }
+ | FOR '(' VAR IDENT InitializerNoIn INTOKEN Expr ')' Statement
+ { $$ = yy.Node('ForInStatement',
+ yy.Node('VariableDeclaration',"var",
+ [yy.Node('InitPatt',yy.Node('Identifier', $4,yy.loc(@4)), $5)],
+ yy.loc([@3,@5])),
+ $Expr, $Statement, false, yy.loc([@$,@9])); }
+ ;
+
+ExprOpt
+ :
+ { $$ = null }
+ | Expr
+ ;
+
+ExprNoInOpt
+ :
+ { $$ = null }
+ | ExprNoIn
+ ;
+
+ContinueStatement
+ : CONTINUE ';'
+ { $$ = yy.Node('ContinueStatement',null,yy.loc([@$,@2])); }
+ | CONTINUE error
+ { $$ = yy.Node('ContinueStatement',null,yy.loc(@$)); }
+ | CONTINUE IDENT ';'
+ { $$ = yy.Node('ContinueStatement',yy.Node('Identifier', $2,yy.loc(@2)),yy.loc([@$,@3])); }
+ | CONTINUE IDENT error
+ { $$ = yy.Node('ContinueStatement',yy.Node('Identifier', $2,yy.loc(@2)),yy.loc([@$,@2])); }
+ ;
+
+BreakStatement
+ : BREAK ';'
+ { $$ = yy.Node('BreakStatement',null,yy.loc([@$,@2])); }
+ | BREAK error
+ { $$ = yy.Node('BreakStatement',null,yy.loc(@$)); }
+ | BREAK IDENT ';'
+ { $$ = yy.Node('BreakStatement',yy.Node('Identifier', $2,yy.loc(@$)),yy.loc([@$,@3])); }
+ | BREAK IDENT error
+ { $$ = yy.Node('BreakStatement',yy.Node('Identifier', $2,yy.loc(@$)),yy.loc([@$,@2])); }
+ ;
+
+ReturnStatement
+ : RETURN ';'
+ { $$ = yy.Node('ReturnStatement',null,yy.loc([@$,@3])); }
+ | RETURN error
+ { $$ = yy.Node('ReturnStatement',null,yy.loc([@$,@3])); }
+ | RETURN Expr ';'
+ { $$ = yy.Node('ReturnStatement',$2,yy.loc([@$,@3])); }
+ | RETURN Expr error
+ { $$ = yy.Node('ReturnStatement',$2,yy.loc([@$,@2])); }
+ ;
+
+WithStatement
+ : WITH '(' Expr ')' Statement
+ { $$ = yy.Node('WithStatement',$Expr,$Statement,yy.loc([@$,@5])); }
+ ;
+
+SwitchStatement
+ : SWITCH '(' Expr ')' CaseBlock
+ { $$ = yy.Node('SwitchStatement',$Expr,$CaseBlock,yy.loc([@$,@5])); }
+ ;
+
+CaseBlock
+ : OPENBRACE CaseClausesOpt CLOSEBRACE
+ { $$ = $2; yy.locComb(@$,@3) }
+ | OPENBRACE CaseClausesOpt DefaultClause CaseClausesOpt CLOSEBRACE
+ { $2.push($DefaultClause); $$ = $2.concat($CaseClausesOpt2); yy.locComb(@$,@5) }
+ ;
+
+CaseClausesOpt
+ :
+ { $$ = []; }
+ | CaseClauses
+ ;
+
+CaseClauses
+ : CaseClause
+ { $$ = [$1]; }
+ | CaseClauses CaseClause
+ { $1.push($2); $$ = $1; }
+ ;
+
+CaseClause
+ : CASE Expr ':'
+ { $$ = yy.Node('SwitchCase',$Expr,[], yy.loc([@$,@3])); }
+ | CASE Expr ':' SourceElements
+ { $$ = yy.Node('SwitchCase',$Expr,$4, yy.loc([@$,@4])); }
+ ;
+
+DefaultClause
+ : DEFAULT ':'
+ { $$ = yy.Node('SwitchCase',null,[], yy.loc([@$,@2])); }
+ | DEFAULT ':' SourceElements
+ { $$ = yy.Node('SwitchCase',null,$3, yy.loc([@$,@3])); }
+ ;
+
+LabeledStatement
+ : IDENT ':' Statement
+ { $$ = yy.Node('LabeledStatement',yy.Node('Identifier', $1,yy.loc(@1)),$3, yy.loc([@$,@3])); }
+ ;
+
+ThrowStatement
+ : THROW Expr ';'
+ { $$ = yy.Node('ThrowStatement', $Expr, @2, yy.loc([@$,@3])); }
+ | THROW Expr error
+ { $$ = yy.Node('ThrowStatement', $Expr, @2, yy.loc([@$,@2])); }
+ ;
+
+TryStatement
+ : TRY Block FINALLY Block
+ { $$ = yy.Node('TryStatement', $Block1, null, $Block2, yy.loc([@$,@4])); }
+ | TRY Block CATCH '(' IDENT ')' Block
+ { $$ = yy.Node('TryStatement', $Block1,
+ yy.Node('CatchClause',yy.Node('Identifier', $5,yy.loc(@5)),null, $Block2, yy.loc([@3,@7])), null, yy.loc([@$,@7])); }
+ | TRY Block CATCH '(' IDENT ')' Block FINALLY Block
+ { $$ = yy.Node('TryStatement', $Block1,
+ yy.Node('CatchClause',yy.Node('Identifier', $5,yy.loc(@5)),null, $Block2, yy.loc([@3,@7])),
+ $Block3, yy.loc([@$,@9])); }
+ ;
+
+DebuggerStatement
+ : DEBUGGER ';'
+ { $$ = yy.Node('DebuggerStatement', yy.loc([@$,@2])); }
+ | DEBUGGER error
+ { $$ = yy.Node('DebuggerStatement', yy.loc(@1)); }
+ ;
+
+FunctionDeclarationaration
+ : FUNCTION IDENT '(' ')' Block
+ { $$ = yy.Node('FunctionDeclaration',
+ yy.Node('Identifier', $2,yy.loc(@2)), [], $Block, false, false, yy.loc([@$,@5]))
+ }
+ | FUNCTION IDENT '(' FormalParameterList ')' Block
+ { $$ = yy.Node('FunctionDeclaration',
+ yy.Node('Identifier', $2,yy.loc(@2)),
+ $FormalParameterList, $Block, false, false, yy.loc([@$,@6]))
+ }
+ ;
+
+FunctionExpr
+ : FUNCTION '(' ')' Block
+ { $$ = yy.Node('FunctionExpression', null, [], $Block, false, false, yy.loc([@$,@4])); }
+ | FUNCTION '(' FormalParameterList ')' Block
+ { $$ = yy.Node('FunctionExpression', null,
+ $FormalParameterList, $Block, false, false, yy.loc([@$,@5])); }
+
+ | FUNCTION IDENT '(' ')' Block
+ { $$ = yy.Node('FunctionExpression',
+ yy.Node('Identifier', $2,yy.loc(@2)),
+ [], $Block, false, false, yy.loc([@$,@5])); }
+ | FUNCTION IDENT '(' FormalParameterList ')' Block
+ { $$ = yy.Node('FunctionExpression',
+ yy.Node('Identifier', $2,yy.loc(@2)),
+ $FormalParameterList, $Block, false, false, yy.loc([@$,@6])); }
+ ;
+
+FormalParameterList
+ : IDENT
+ { $$ = [yy.Node('Identifier', $1)]; }
+ | FormalParameterList ',' IDENT
+ { $$ = $1; $$.push(yy.Node('Identifier', $3,yy.loc(@3))); }
+ ;
+
+FunctionBody
+ :
+ { $$ = []; }
+ | SourceElements
+ ;
+
+Program
+ :
+ { return yy.Node('Program'); }
+ | SourceElements
+ { return yy.Node('Program',$1,yy.loc(@1)); }
+ ;
+
+SourceElements
+ : Statement
+ { $$ = [$1]; }
+ | SourceElements Statement
+ { yy.locComb(@$,@2);
+ $$ = $1;$1.push($2); }
+ ;
+
157 lib/lexer.l
@@ -0,0 +1,157 @@
+esc "\\"
+int ([0-9]|[1-9][0-9]+)
+exp ([eE][-+]?[0-9]+)
+frac ("."[0-9]+)
+IDS [a-zA-Z$_]|"\u"[a-fA-F0-9]{4}
+BSL "\\".
+RCLASS "["({BSL}|[^\\[])*"]"
+
+%%
+
+";"\s+/("++"|"--") {return ';'}
+\n(\s|\n)*/("++"|"--") {return ';'}
+\n(\s|\n)* %{ if(yy.ASI){ yy.ASI=false; return ';';} else yy.lineBreak = true; %}
+\s+ {yy.ASI=false; /* skip whitespace */}
+"//".* {/* skip comment */}
+"#".* {/* skip comment */}
+"/*"(.|\n)*?"*/" {yy.ASI=false; /* skip comment */}
+
+"0"[xX][a-fA-F0-9]+/([^a-zA-Z$_]{0,1}) {return 'NUMBER';}
+([1-9][0-9]+|[0-9]){frac}?{exp}?/([^a-zA-Z$_]{0,1}) {return 'NUMBER';}
+
+'"'("\\x"[a-fA-F0-9]{2}|"\\u"[a-fA-F0-9]{4}|"\\"[^xu]|[^"{esc}\n])*'"' {yytext = yytext.substr(1,yyleng-2); return 'STRING';}
+"'"("\\"['bfvnrt/{esc}]|"\\u"[a-fA-F0-9]{4}|[^'{esc}])*"'" {yytext = yytext.substr(1,yyleng-2); return 'STRING';}
+
+/("/="({BSL}|{RCLASS}|[^[\\\/])*"/"({IDS})*) %{
+ if(!!yy.inRegex) {
+ var s = yy.lexer.matches[1];
+ for(var i=0;i<s.length;++i) {
+ yy.lexer.input();
+ }
+ yytext = s;
+ return 'REGEXP_BODY';
+ } else {
+ yy.lexer.input(); yy.lexer.input();
+ return 'DIVEQUAL';
+ };
+%}
+
+/("/"({BSL}|{RCLASS}|[^[\\\/*])({BSL}|{RCLASS}|[^[\\\/])*"/"({IDS})*) %{
+ if(!!yy.inRegex) {
+ var s = yy.lexer.matches[1];
+ for(var i=0;i<s.length;++i) {
+ yy.lexer.input();
+ }
+ yytext = s;
+ return 'REGEXP_BODY';
+ } else {
+ yy.lexer.input();
+ return '/';
+ };
+%}
+
+
+"{" %{ return 'OPENBRACE' %}
+"}" %{ return 'CLOSEBRACE' %}
+"[" {return '['}
+"]" {return ']'}
+"(" {return '('}
+")" {return ')'}
+"," {return ','}
+"." {return '.'}
+";" {yy.ASI = false; return ';'}
+":" {return ':'}
+"+=" {return 'PLUSEQUAL'}
+"-=" {return 'MINUSEQUAL'}
+"*=" {return 'MULTEQUAL'}
+"%=" {return 'MODEQUAL'}
+"&=" {return 'ANDEQUAL'}
+"|=" {return 'OREQUAL'}
+"^=" {return 'XOREQUAL'}
+"<<=" {return 'LSHIFTEQUAL'}
+">>=" {return 'RSHIFTEQUAL'}
+">>>=" {return 'URSHIFTEQUAL'}
+"/=" {return 'DIVEQUAL'}
+"<=" {return 'LE'}
+">=" {return 'GE'}
+"===" {return 'STREQ'}
+"!==" {return 'STRNEQ'}
+"==" {return 'EQEQ'}
+"!=" {return 'NE'}
+"&&" {return 'AND'}
+"||" {return 'OR'}
+"++" {return 'PLUSPLUS'}
+"--" {return 'MINUSMINUS'}
+">>>" {return 'URSHIFT'}
+"<<" {return 'LSHIFT'}
+">>" {return 'RSHIFT'}
+"+" {return '+'}
+"-" {return '-'}
+"*" {return '*'}
+"%" {return '%'}
+"<" {return '<'}
+">" {return '>'}
+"&" {return '&'}
+"|" {return '|'}
+"^" {return '^'}
+"!" {return '!'}
+"~" {return '~'}
+"?" {return '?'}
+"/" {return '/'}
+"=" {return '='}
+
+"break" {yy.ASI = true; return 'BREAK'}
+"case" {return 'CASE'}
+"continue" {yy.ASI = true; return 'CONTINUE'}
+"debugger" {return 'DEBUGGER'}
+"default" {return 'DEFAULT'}
+"delete" {return 'DELETETOKEN'}
+"do" {return 'DO'}
+"else" {return 'ELSE'}
+"finally" {return 'FINALLY'}
+"for" {return 'FOR'}
+"function" {return 'FUNCTION'}
+"if" {return 'IF'}
+"in" {return 'INTOKEN'}
+"instanceof" {return 'INSTANCEOF'}
+"new" {return 'NEW'}
+"return" {yy.ASI = true; return 'RETURN'}
+"switch" {return 'SWITCH'}
+"try" {return 'TRY'}
+"catch" {return 'CATCH'}
+"throw" {yy.ASI = true; return 'THROW'}
+"typeof" {return 'TYPEOF'}
+"var" {return 'VAR'}
+"void" {return 'VOIDTOKEN'}
+"while" {return 'WHILE'}
+"with" {return 'WITH'}
+
+"class" {return 'WITH'}
+"const" {return 'CONSTTOKEN'}
+"enum" {return 'ENUM'}
+"export" {return 'EXPORT'}
+"extends" {return 'EXTENDS'}
+"import" {return 'IMPORT'}
+"super" {return 'SUPERTOKEN'}
+
+"implements" {return 'IMPLEMENTS'}
+"interface" {return 'INTERFACE'}
+"let" {return 'LET'}
+"package" {return 'PACKAGE'}
+"private" {return 'PRIVATE'}
+"protected" {return 'PROTECTED'}
+"public" {return 'PUBLIC'}
+"static" {return 'STATIC'}
+"yield" {return 'YIELD'}
+
+"this" {return 'THISTOKEN'}
+"true" {return 'TRUETOKEN'}
+"false" {return 'FALSETOKEN'}
+"null" {return 'NULLTOKEN'}
+
+({IDS})+({IDS}|[0-9])* {return 'IDENT';}
+
+. {return 'INVALID'}
+
+%%
+
268 lib/nodes.js
@@ -0,0 +1,268 @@
+
+exports.defineNodes = function (builder) {
+
+var defaultIni = function (loc) {
+ this.loc = loc;
+ return this;
+}
+
+var def = function def(name, ini) {
+ builder[name[0].toLowerCase()+name.slice(1)] = function (a,b,c,d,e,f,g,h) {
+ var obj = {};
+ ini.call(obj,a,b,c,d,e,f,g,h);
+ obj.type = name;
+ return obj;
+ };
+};
+
+/* Nodes
+*/
+
+// Program node
+def('Program', function (elements,loc) {
+ this.body = elements;
+ this.loc = loc;
+});
+
+def('ExpressionStatement', function (expression, loc) {
+ this.expression = expression;
+ this.loc = loc;
+});
+
+def('BlockStatement', function (body, loc) {
+ this.body = body;
+ this.loc = loc;
+});
+
+def('EmptyStatement', defaultIni);
+
+
+// Identifier node
+def('Identifier', function (name,loc) {
+ this.name = name;
+ this.loc = loc;
+});
+
+// Literal expression node
+def('Literal', function (val, loc) {
+ this.value = val;
+ this.loc = loc;
+});
+
+// "this" expression node
+def('ThisExpression', defaultIni);
+
+// Var statement node
+def('VariableDeclaration', function (kind, declarations, loc) {
+ this.kind = kind;
+ this.declarations = declarations;
+ this.loc = loc;
+});
+
+// Init pattern node
+def('InitPatt', function (id, init, loc) {
+ this.id = id;
+ this.init = init;
+ this.loc = loc;
+});
+
+def('ArrayExpression', function (elements, loc) {
+ this.elements = elements;
+ this.loc = loc;
+});
+
+def('ObjectExpression', function (properties, loc) {
+ this.properties = properties;
+ this.loc = loc;
+});
+
+// Function declaration node
+var funIni = function (ident, params, body, isGen, isExp, loc) {
+ this.id = ident;
+ this.params = params;
+ this.body = body;
+ this.generator = isGen;
+ this.expression = isExp;
+ this.loc = loc;
+};
+
+def('FunctionDeclaration', funIni);
+
+def('FunctionExpression', funIni);
+
+// return statement node
+def('ReturnStatement', function (argument, loc) {
+ this.argument = argument;
+ this.loc = loc;
+});
+
+def('TryStatement', function (block, handler, finalizer, loc) {
+ this.block = block;
+ this.handler = handler;
+ this.finalizer = finalizer;
+ this.loc = loc;
+});
+
+def('CatchClause', function (param, guard, body, loc) {
+ this.param = param;
+ this.guard = guard;
+ this.body = body;
+ this.loc = loc;
+});
+
+def('ThrowStatement', function (argument, loc) {
+ this.argument = argument;
+ this.loc = loc;
+});
+
+def('LabeledStatement', function (label, body, loc) {
+ this.label = label;
+ this.body = body;
+ this.loc = loc;
+});
+
+def('BreakStatement', function (label, loc) {
+ this.label = label;
+ this.loc = loc;
+});
+
+def('ContinueStatement', function (label, loc) {
+ this.label = label;
+ this.loc = loc;
+});
+
+def('SwitchStatement', function (discriminant, cases, lexical, loc) {
+ this.discriminant = discriminant;
+ this.cases = cases;
+ this.lexical = !!lexical;
+ this.loc = loc;
+});
+
+def('SwitchCase', function (test, consequent, loc) {
+ this.test = test;
+ this.consequent = consequent;
+ this.loc = loc;
+});
+
+def('WithStatement', function (object, body, loc) {
+ this.object = object;
+ this.body = body;
+ this.loc = loc;
+});
+
+
+// operators
+def('ConditionalExpression', function (test, consequent, alternate, loc) {
+ this.test = test;
+ this.alternate = alternate;
+ this.consequent = consequent;
+ this.loc = loc;
+});
+
+def('SequenceExpression', function (expressions, loc) {
+ this.expressions = expressions;
+ this.loc = loc;
+});
+
+def('BinaryExpression', function (op, left, right, loc) {
+ this.operator = op;
+ this.left = left;
+ this.right = right;
+ this.loc = loc;
+});
+
+def('AssignmentExpression', function (op, left, right, loc) {
+ this.operator = op;
+ this.left = left;
+ this.right = right;
+ this.loc = loc;
+});
+
+def('LogicalExpression', function (op, left, right, loc) {
+ this.operator = op;
+ this.left = left;
+ this.right = right;
+ this.loc = loc;
+});
+
+def('UnaryExpression', function (operator, argument, loc) {
+ this.operator = operator;
+ this.argument = argument;
+ this.loc = loc;
+});
+
+
+def('UpdateExpression', function (operator, argument, prefix, loc) {
+ this.operator = operator;
+ this.argument = argument;
+ this.prefix = prefix;
+ this.loc = loc;
+});
+
+def('CallExpression', function (callee, args, loc) {
+ this.callee = callee;
+ this["arguments"] = args;
+ this.loc = loc;
+});
+
+
+def('NewExpression', function (callee, args, loc) {
+ this.callee = callee;
+ this["arguments"] = args;
+ this.loc = loc;
+});
+
+
+def('MemberExpression', function (object, property, computed, loc) {
+ this.object = object;
+ this.property = property;
+ this.computed = computed;
+ this.loc = loc;
+});
+
+// debugger node
+def('DebuggerStatement', defaultIni);
+
+// empty node
+def('Empty', defaultIni);
+
+// control structs
+
+def('WhileStatement', function (test, body, loc) {
+ this.test = test;
+ this.body = body;
+ this.loc = loc;
+});
+
+def('DoWhileStatement', function (body, test, loc) {
+ this.test = test;
+ this.body = body;
+ this.loc = loc;
+});
+
+def('ForStatement', function (init, test, update, body, loc) {
+ this.init = init;
+ this.test = test;
+ this.update = update;
+ this.body = body;
+ this.loc = loc;
+});
+
+def('ForInStatement', function (left, right, body, each, loc) {
+ this.left = left;
+ this.right = right;
+ this.body = body;
+ this.each = !!each;
+ this.loc = loc;
+});
+
+def('IfStatement', function (test, consequent, alternate, loc) {
+ this.test = test;
+ this.alternate = alternate;
+ this.consequent = consequent;
+ this.loc = loc;
+});
+
+return def;
+}
+
105 lib/reflect.js
@@ -0,0 +1,105 @@
+
+var parser = require("./parser").parser,
+ nodes = require("./nodes");
+
+function JSParser (options) {
+ // Create a parser constructor and an instance
+ this.parser = new Parser(options||{});
+}
+
+JSParser.prototype = {
+ parse: function (source) {
+ return this.parser.parse(source);
+ }
+};
+
+var builder = {};
+
+// Define AST nodes
+nodes.defineNodes(builder);
+
+function Parser (options) {
+ this.yy.source = options.source||null;
+ this.yy.startLine = options.line || 1;
+ this.yy.noloc = options.loc === false;
+ this.yy.builder = options.builder||null;
+}
+
+Parser.prototype = parser;
+
+// allow yy.NodeType calls in parser
+for (var con in builder) {
+ if (builder.hasOwnProperty(con)) {
+ parser.yy[con] = function (name){
+ return function (a,b,c,d,e,f,g,h) {
+ return builder[name](a,b,c,d,e,f,g,h);
+ }
+ }(con);
+ }
+}
+
+// used named arguments to avoid arguments array
+parser.yy.Node = function Node (type, a,b,c,d,e,f,g,h) {
+ var buildName = type[0].toLowerCase()+type.slice(1);
+ if (this.builder && this.builder[buildName]) {
+ return this.builder[buildName](a,b,c,d,e,f,g,h);
+ } else if (builder[buildName]) {
+ return builder[buildName](a,b,c,d,e,f,g,h);
+ } else {
+ throw 'no such node type: '+type;
+ }
+};
+
+parser.yy.locComb = function (start, end) {
+ start.last_line = end.last_line;
+ start.last_column = end.last_column;
+ return start;
+};
+
+parser.yy.loc = function (loc) {
+ if (this.noloc) return null;
+ if ("length" in loc) loc = this.locComb(loc[0],loc[1]);
+
+ return { source: this.source
+ , start: { line: this.startLine+loc.first_line - 1
+ , column: loc.first_column }
+ , end: { line: this.startLine+loc.last_line - 1
+ , column: loc.last_column }
+ };
+};
+
+// Handle parse errors and recover from ASI
+parser.yy.parseError = function (err, hash) {
+ // don't print error for missing semicolon
+ if (!(hash.expected.indexOf("';'") >= 0 && (hash.token === 'CLOSEBRACE' || parser.yy.lineBreak || parser.yy.lastLineBreak || hash.token === 1))) {
+ throw new Error(err);
+ }
+};
+
+// used to check if last match was a line break (for ; insertion)
+var realLex = parser.lexer.lex;
+parser.lexer.lex = function () {
+ parser.yy.lastLineBreak = parser.yy.lineBreak;
+ parser.yy.lineBreak = false;
+ return realLex.call(parser.lexer);
+};
+
+parser.yy.escapeString = function (s) {
+ return s.replace(/\\\n/,'').replace(/\\([^xubfnvrt0\\])/g, '$1');
+};
+
+var oldParse = parser.parse;
+parser.parse = function (source) {
+ parser.yy.lineBreak = false;
+ parser.yy.inRegex = false;
+ parser.yy.ASI = false;
+ return oldParse.call(this,source);
+};
+
+exports.Reflect = {
+ parse: function (src, options) {
+ return new JSParser(options).parse(src);
+ }
+};
+
+exports.parse = exports.Reflect.parse;
30 package.json
@@ -0,0 +1,30 @@
+{
+ "name": "reflect",
+ "version": "0.0.1",
+ "author": "Zach Carter",
+ "email": "zach@carter.name",
+ "keywords": [
+ "parser",
+ "reflect",
+ "javascript"
+ ],
+ "githubName": "reflect.js",
+ "type": "zip",
+ "location": "http://github.com/zaach/reflect.js/zipball/master",
+ "main": "dist/reflect",
+ "engines": {
+ "node": ">=0.1.90"
+ },
+ "directories": {
+ "lib": "lib",
+ "bin": "./bin"
+ },
+ "modules": {
+ "reflect.js": "dist/reflect.js",
+ "parser.js": "dist/parser.js",
+ "nodes.js": "dist/nodes.js"
+ },
+ "files": [
+ ""
+ ]
+}
BIN  reflectjs.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
183 test/match.js
@@ -0,0 +1,183 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+
+// The Worker constructor can take a relative URL, but different test runners
+// run in different enough environments that it doesn't all just automatically
+// work. For the shell, we use just a filename; for the browser, see browser.js.
+var workerDir = '';
+
+// explicitly turn on js185
+// XXX: The browser currently only supports up to version 1.8
+if (typeof version != 'undefined')
+{
+ version(185);
+}
+
+String.prototype.quote = function () { return JSON.stringify(this); };
+
+// A little pattern-matching library.
+var Match =
+
+(function() {
+
+ function Pattern(template) {
+ // act like a constructor even as a function
+ if (!(this instanceof Pattern))
+ return new Pattern(template);
+
+ this.template = template;
+ }
+
+ Pattern.prototype = {
+ match: function(act) {
+ return match(act, this.template);
+ },
+
+ matches: function(act) {
+ try {
+ return this.match(act);
+ }
+ catch (e) {
+ if (e instanceof MatchError) {
+ return false;
+ } else throw e;
+ }
+ },
+
+ assert: function(act, message) {
+ try {
+ return this.match(act);
+ }
+ catch (e) {
+ console.log(act);
+ console.log(this.template);
+ if (e instanceof MatchError)
+ throw new Error((message || "failed match") + ": " + e.message);
+ else throw e;
+ }
+ },
+
+ toString: function() { return "[object Pattern]"; }
+ };
+
+ Pattern.ANY = new Pattern;
+ Pattern.ANY.template = Pattern.ANY;
+
+ var quote = JSON.stringify;
+
+ function MatchError(msg) {
+ this.message = msg;
+ }
+
+ MatchError.prototype = {
+ toString: function() {
+ return "match error: " + this.message;
+ }
+ };
+
+ function isAtom(x) {
+ return (typeof x === "number") ||
+ (typeof x === "string") ||
+ (typeof x === "boolean") ||
+ (x === null) ||
+ ((typeof x === "function" || typeof x === "object")
+ && x instanceof RegExp);
+ }
+
+ function isObject(x) {
+ return (x !== null) && (typeof x === "object");
+ }
+
+ function isArrayLike(x) {
+ return isObject(x) && ("length" in x);
+ }
+
+ function matchAtom(act, exp) {
+ if ((typeof exp) === "number" && isNaN(exp)) {
+ if ((typeof act) !== "number" || !isNaN(act))
+ throw new MatchError("expected NaN, got: " + quote(act));
+ return true;
+ }
+
+ if (exp === null) {
+ if (act !== null)
+ throw new MatchError("expected null, got: " + quote(act));
+ return true;
+ }
+
+ if (exp instanceof RegExp) {
+ if (!(act instanceof RegExp) || exp.source !== act.source)
+ throw new MatchError("expected " + quote(exp) + ", got: " + quote(act));
+ return true;
+ }
+
+ switch (typeof exp) {
+ case "string":
+ if (act !== exp)
+ throw new MatchError("expected " + exp.quote() + ", got " + quote(act));
+ return true;
+ case "boolean":
+ case "number":
+ if (exp !== act)
+ throw new MatchError("expected " + exp + ", got " + quote(act));
+ return true;
+ }
+
+ throw new Error("bad pattern: " + exp.toSource());
+ }
+
+ function matchObject(act, exp) {
+ if (!isObject(act))
+ throw new MatchError("expected object, got " + quote(act));
+
+ for (var key in exp) {
+ if (!(key in act))
+ throw new MatchError("expected property " + key.quote() + " not found in " + quote(act));
+ match(act[key], exp[key]);
+ }
+
+ return true;
+ }
+
+ function matchArray(act, exp) {
+ if (!isObject(act) || !("length" in act))
+ throw new MatchError("expected array-like object, got " + quote(act));
+
+ var length = exp.length;
+ for (var i = 0; i < length; i++) {
+ if (i in exp) {
+ if (!(i in act))
+ throw new MatchError("expected array property " + i + " not found in " + quote(act));
+ match(act[i], exp[i]);
+ }
+ }
+
+ return true;
+ }
+
+ function match(act, exp) {
+ if (exp === Pattern.ANY)
+ return true;
+
+ if (exp instanceof Pattern)
+ return exp.match(act);
+
+ if (isAtom(exp))
+ return matchAtom(act, exp);
+
+ if (isArrayLike(exp))
+ return matchArray(act, exp);
+
+ return matchObject(act, exp);
+ }
+
+ return { Pattern: Pattern,
+ MatchError: MatchError };
+
+})();
+
+exports.Match = Match;
1,643 test/reflect-parse.js
@@ -0,0 +1,1643 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var Reflect = require('../dist/reflect').Reflect;
+var Match = require('./match').Match;
+
+var Pattern = Match.Pattern;
+var MatchError = Match.MatchError;
+
+var _ = Pattern.ANY;
+
+
+function program (elts) {
+ return Pattern({
+ "type": "Program",
+ "body": elts
+ });
+}
+function exprStmt (expr) {
+ return Pattern({
+ "type": "ExpressionStatement",
+ "expression": expr
+ });
+}
+function throwStmt (expr) {
+ return Pattern({
+ "type": "ThrowStatement",
+ "argument": expr
+ });
+}
+function returnStmt (expr) {
+ return Pattern({
+ "type": "ReturnStatement",
+ "argument": expr
+ });
+}
+function yieldExpr (expr) {
+ return Pattern({
+ "type": "YieldExpression",
+ "argument": expr
+ });
+}
+function lit (val) {
+ return Pattern({
+ "type": "Literal",
+ "value": val
+ });
+}
+var thisExpr = Pattern({
+ "type": "ThisExpression"
+ });
+function funDecl (id, params, body) {
+ return Pattern({
+ "type": "FunctionDeclaration",
+ "id": id,
+ "params": params,
+ "body": body,
+ "generator": false
+ });
+}
+function genFunDecl (id, params, body) {
+ return Pattern({
+ "type": "FunctionDeclaration",
+ "id": id,
+ "params": params,
+ "body": body,
+ "generator": true
+ });
+}
+function varDecl (decls) {
+ return Pattern({
+ "type": "VariableDeclaration",
+ "declarations": decls,
+ "kind": "var"
+ });
+}
+function letDecl (decls) {
+ return Pattern({
+ "type": "VariableDeclaration",
+ "declarations": decls,
+ "kind": "let"
+ });
+}
+function constDecl (decls) {
+ return Pattern({
+ "type": "VariableDeclaration",
+ "declarations": decls,
+ "kind": "const"
+ });
+}
+function ident (name) {
+ return Pattern({
+ "type": "Identifier",
+ "name": name
+ });
+}
+function dotExpr (obj, id) {
+ return Pattern({
+ "type": "MemberExpression",
+ "computed": false,
+ "object": obj,
+ "property": id
+ });
+}
+function memExpr (obj, id) {
+ return Pattern({
+ "type": "MemberExpression",
+ "computed": true,
+ "object": obj,
+ "property": id
+ });
+}
+function forStmt (init, test, update, body) {
+ return Pattern({
+ "type": "ForStatement",
+ "init": init,
+ "test": test,
+ "update": update,
+ "body": body
+ });
+}
+function forInStmt (lhs, rhs, body) {
+ return Pattern({
+ "type": "ForInStatement",
+ "left": lhs,
+ "right": rhs,
+ "body": body,
+ "each": false
+ });
+}
+function forEachInStmt (lhs, rhs, body) {
+ return Pattern({
+ "type": "ForInStatement",
+ "left": lhs,
+ "right": rhs,
+ "body": body,
+ "each": true
+ });
+}
+function breakStmt (lab) {
+ return Pattern({
+ "type": "BreakStatement",
+ "label": lab
+ });
+}
+function continueStmt (lab) {
+ return Pattern({
+ "type": "ContinueStatement",
+ "label": lab
+ });
+}
+function blockStmt (body) {
+ return Pattern({
+ "type": "BlockStatement",
+ "body": body
+ });
+}
+var emptyStmt = Pattern({
+ "type": "EmptyStatement"
+ });
+function ifStmt (test, cons, alt) {
+ return Pattern({
+ "type": "IfStatement",
+ "test": test,
+ "alternate": alt,
+ "consequent": cons
+ });
+}
+function labStmt (lab, stmt) {
+ return Pattern({
+ "type": "LabeledStatement",
+ "label": lab,
+ "body": stmt
+ });
+}
+function withStmt (obj, stmt) {
+ return Pattern({
+ "type": "WithStatement",
+ "object": obj,
+ "body": stmt
+ });
+}
+function whileStmt (test, stmt) {
+ return Pattern({
+ "type": "WhileStatement",
+ "test": test,
+ "body": stmt
+ });
+}
+function doStmt (stmt, test) {
+ return Pattern({
+ "type": "DoWhileStatement",
+ "test": test,
+ "body": stmt
+ });
+}
+function switchStmt (disc, cases) {
+ return Pattern({
+ "type": "SwitchStatement",
+ "discriminant": disc,
+ "cases": cases
+ });
+}
+function caseClause (test, stmts) {
+ return Pattern({
+ "type": "SwitchCase",
+ "test": test,
+ "consequent": stmts
+ });
+}
+function defaultClause (stmts) {
+ return Pattern({
+ "type": "SwitchCase",
+ "test": null,
+ "consequent": stmts
+ });
+}
+function catchClause (id, guard, body) {
+ return Pattern({
+ "type": "CatchClause",
+ "param": id,
+ "guard": guard,
+ "body": body
+ });
+}
+function tryStmt (body, catches, fin) {
+ return Pattern({
+ "type": "TryStatement",
+ "block": body,
+ "handler": catches,
+ "finalizer": fin
+ });
+}
+function letStmt (head, body) {
+ return Pattern({
+ "type": "LetStatement",
+ "head": head,
+ "body": body
+ });
+}
+function funExpr (id, args, body, gen) {
+ return Pattern({
+ "type": "FunctionExpression",
+ "id": id,
+ "params": args,
+ "body": body,
+ "generator": false
+ });
+}
+function genFunExpr (id, args, body) {
+ return Pattern({
+ "type": "FunctionExpression",
+ "id": id,
+ "params": args,
+ "body": body,
+ "generator": true
+ });
+}
+function unExpr (op, arg) {
+ return Pattern({
+ "type": "UnaryExpression",
+ "operator": op,
+ "argument": arg
+ });
+}
+function binExpr (op, left, right) {
+ return Pattern({
+ "type": "BinaryExpression",
+ "operator": op,
+ "left": left,
+ "right": right
+ });
+}
+function aExpr (op, left, right) {
+ return Pattern({
+ "type": "AssignmentExpression",
+ "operator": op,
+ "left": left,
+ "right": right
+ });
+}
+function updExpr (op, arg, prefix) {
+ return Pattern({
+ "type": "UpdateExpression",
+ "operator": op,
+ "argument": arg,
+ "prefix": prefix
+ });
+}
+function logExpr (op, left, right) {
+ return Pattern({
+ "type": "LogicalExpression",
+ "operator": op,
+ "left": left,
+ "right": right
+ });
+}
+function condExpr (test, cons, alt) {
+ return Pattern({
+ "type": "ConditionalExpression",
+ "test": test,
+ "consequent": cons,
+ "alternate": alt
+ });
+}
+function seqExpr (exprs) {
+ return Pattern({
+ "type": "SequenceExpression",
+ "expressions": exprs
+ });
+}
+function newExpr (callee, args) {
+ return Pattern({
+ "type": "NewExpression",
+ "callee": callee,
+ "arguments": args
+ });
+}
+function callExpr (callee, args) {
+ return Pattern({
+ "type": "CallExpression",
+ "callee": callee,
+ "arguments": args
+ });
+}
+function arrExpr (elts) {
+ return Pattern({
+ "type": "ArrayExpression",
+ "elements": elts
+ });
+}
+function objExpr (elts) {
+ return Pattern({
+ "type": "ObjectExpression",
+ "properties": elts
+ });
+}
+function compExpr (body, blocks, filter) {
+ return Pattern({
+ "type": "ComprehensionExpression",
+ "body": body,