From e4490a88ea02bc944b69f8b94deb29210a29143f Mon Sep 17 00:00:00 2001 From: Zach Carter Date: Mon, 11 Apr 2011 19:04:52 -0400 Subject: [PATCH] first --- .gitignore | 0 Makefile | 13 + README.md | 14 + dist/.gitignore | 1 + examples/jsonml-ast.js | 234 ++++++ lib/codegen.js | 215 ++++++ lib/grammar.y | 910 ++++++++++++++++++++++ lib/lexer.l | 157 ++++ lib/nodes.js | 268 +++++++ lib/reflect.js | 105 +++ package.json | 30 + reflectjs.png | Bin 0 -> 25065 bytes test/match.js | 183 +++++ test/reflect-parse.js | 1643 ++++++++++++++++++++++++++++++++++++++++ 14 files changed, 3773 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 dist/.gitignore create mode 100644 examples/jsonml-ast.js create mode 100644 lib/codegen.js create mode 100644 lib/grammar.y create mode 100644 lib/lexer.l create mode 100644 lib/nodes.js create mode 100644 lib/reflect.js create mode 100644 package.json create mode 100644 reflectjs.png create mode 100644 test/match.js create mode 100644 test/reflect-parse.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4fb824e --- /dev/null +++ b/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 + diff --git a/README.md b/README.md new file mode 100644 index 0000000..12fc652 --- /dev/null +++ b/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. diff --git a/dist/.gitignore b/dist/.gitignore new file mode 100644 index 0000000..a6c7c28 --- /dev/null +++ b/dist/.gitignore @@ -0,0 +1 @@ +*.js diff --git a/examples/jsonml-ast.js b/examples/jsonml-ast.js new file mode 100644 index 0000000..4298c88 --- /dev/null +++ b/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," ")); + + diff --git a/lib/codegen.js b/lib/codegen.js new file mode 100644 index 0000000..590c815 --- /dev/null +++ b/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]; + } +}; + diff --git a/lib/grammar.y b/lib/grammar.y new file mode 100644 index 0000000..656d320 --- /dev/null +++ b/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); } + ; + diff --git a/lib/lexer.l b/lib/lexer.l new file mode 100644 index 0000000..ee36d7b --- /dev/null +++ b/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>=" {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'} + +%% + diff --git a/lib/nodes.js b/lib/nodes.js new file mode 100644 index 0000000..98e2454 --- /dev/null +++ b/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; +} + diff --git a/lib/reflect.js b/lib/reflect.js new file mode 100644 index 0000000..5eca282 --- /dev/null +++ b/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; diff --git a/package.json b/package.json new file mode 100644 index 0000000..76e6c0e --- /dev/null +++ b/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": [ + "" + ] +} diff --git a/reflectjs.png b/reflectjs.png new file mode 100644 index 0000000000000000000000000000000000000000..a5fa739f1ae2a05ad64dcf55428ffe14c7b6e7ca GIT binary patch literal 25065 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 y)", binExpr(">", ident("x"), ident("y"))); +assertExpr("(x >= y)", binExpr(">=", ident("x"), ident("y"))); +assertExpr("(x << y)", binExpr("<<", ident("x"), ident("y"))); +assertExpr("(x >> y)", binExpr(">>", ident("x"), ident("y"))); +assertExpr("(x >>> y)", binExpr(">>>", ident("x"), ident("y"))); +assertExpr("(x + y)", binExpr("+", ident("x"), ident("y"))); +assertExpr("(w + x + y + z)", binExpr("+", binExpr("+", binExpr("+", ident("w"), ident("x")), ident("y")), ident("z"))); +assertExpr("(x - y)", binExpr("-", ident("x"), ident("y"))); +assertExpr("(w - x - y - z)", binExpr("-", binExpr("-", binExpr("-", ident("w"), ident("x")), ident("y")), ident("z"))); +assertExpr("(x * y)", binExpr("*", ident("x"), ident("y"))); +assertExpr("(x / y)", binExpr("/", ident("x"), ident("y"))); +assertExpr("(x % y)", binExpr("%", ident("x"), ident("y"))); +assertExpr("(x | y)", binExpr("|", ident("x"), ident("y"))); +assertExpr("(x ^ y)", binExpr("^", ident("x"), ident("y"))); +assertExpr("(x & y)", binExpr("&", ident("x"), ident("y"))); +assertExpr("(x in y)", binExpr("in", ident("x"), ident("y"))); +assertExpr("(x instanceof y)", binExpr("instanceof", ident("x"), ident("y"))); +assertExpr("(x = y)", aExpr("=", ident("x"), ident("y"))); +assertExpr("(x += y)", aExpr("+=", ident("x"), ident("y"))); +assertExpr("(x -= y)", aExpr("-=", ident("x"), ident("y"))); +assertExpr("(x *= y)", aExpr("*=", ident("x"), ident("y"))); +assertExpr("(x /= y)", aExpr("/=", ident("x"), ident("y"))); +assertExpr("(x %= y)", aExpr("%=", ident("x"), ident("y"))); +assertExpr("(x <<= y)", aExpr("<<=", ident("x"), ident("y"))); +assertExpr("(x >>= y)", aExpr(">>=", ident("x"), ident("y"))); +assertExpr("(x >>>= y)", aExpr(">>>=", ident("x"), ident("y"))); +assertExpr("(x |= y)", aExpr("|=", ident("x"), ident("y"))); +assertExpr("(x ^= y)", aExpr("^=", ident("x"), ident("y"))); +assertExpr("(x &= y)", aExpr("&=", ident("x"), ident("y"))); +assertExpr("(x || y)", logExpr("||", ident("x"), ident("y"))); +assertExpr("(x && y)", logExpr("&&", ident("x"), ident("y"))); +assertExpr("(w || x || y || z)", logExpr("||", logExpr("||", logExpr("||", ident("w"), ident("x")), ident("y")), ident("z"))) +assertExpr("(x ? y : z)", condExpr(ident("x"), ident("y"), ident("z"))); +assertExpr("(x,y)", seqExpr([ident("x"),ident("y")])) +assertExpr("(x,y,z)", seqExpr([ident("x"),ident("y"),ident("z")])) +assertExpr("(a,b,c,d,e,f,g)", seqExpr([ident("a"),ident("b"),ident("c"),ident("d"),ident("e"),ident("f"),ident("g")])); +assertExpr("(new Object)", newExpr(ident("Object"), [])); +assertExpr("(new Object())", newExpr(ident("Object"), [])); +assertExpr("(new Object(42))", newExpr(ident("Object"), [lit(42)])); +assertExpr("(new Object(1,2,3))", newExpr(ident("Object"), [lit(1),lit(2),lit(3)])); +assertExpr("(String())", callExpr(ident("String"), [])); +assertExpr("(String(42))", callExpr(ident("String"), [lit(42)])); +assertExpr("(String(1,2,3))", callExpr(ident("String"), [lit(1),lit(2),lit(3)])); +assertExpr("[]", arrExpr([])); +assertExpr("[1]", arrExpr([lit(1)])); +assertExpr("[1,2]", arrExpr([lit(1),lit(2)])); +assertExpr("[1,2,3]", arrExpr([lit(1),lit(2),lit(3)])); +assertExpr("[1,,2,3]", arrExpr([lit(1),,lit(2),lit(3)])); +assertExpr("[1,,,2,3]", arrExpr([lit(1),,,lit(2),lit(3)])); +assertExpr("[1,,,2,,3]", arrExpr([lit(1),,,lit(2),,lit(3)])); +assertExpr("[1,,,2,,,3]", arrExpr([lit(1),,,lit(2),,,lit(3)])); +assertExpr("[,1,2,3]", arrExpr([,lit(1),lit(2),lit(3)])); +assertExpr("[,,1,2,3]", arrExpr([,,lit(1),lit(2),lit(3)])); +assertExpr("[,,,1,2,3]", arrExpr([,,,lit(1),lit(2),lit(3)])); +assertExpr("[,,,1,2,3,]", arrExpr([,,,lit(1),lit(2),lit(3)])); +assertExpr("[,,,1,2,3,,]", arrExpr([,,,lit(1),lit(2),lit(3),])); +assertExpr("[,,,1,2,3,,,]", arrExpr([,,,lit(1),lit(2),lit(3),,])); +assertExpr("[,,,,,]", arrExpr([,,,,])); +assertExpr("({})", objExpr([])); +assertExpr("({x:1})", objExpr([{ key: ident("x"), value: lit(1) }])); +assertExpr("({x:1, y:2})", objExpr([{ key: ident("x"), value: lit(1) }, + { key: ident("y"), value: lit(2) } ])); +assertExpr("({x:1, y:2, z:3})", objExpr([{ key: ident("x"), value: lit(1) }, + { key: ident("y"), value: lit(2) }, + { key: ident("z"), value: lit(3) } ])); +assertExpr("({x:1, 'y':2, z:3})", objExpr([{ key: ident("x"), value: lit(1) }, + { key: lit("y"), value: lit(2) }, + { key: ident("z"), value: lit(3) } ])); +assertExpr("({'x':1, 'y':2, z:3})", objExpr([{ key: lit("x"), value: lit(1) }, + { key: lit("y"), value: lit(2) }, + { key: ident("z"), value: lit(3) } ])); +assertExpr("({'x':1, 'y':2, 3:3})", objExpr([{ key: lit("x"), value: lit(1) }, + { key: lit("y"), value: lit(2) }, + { key: lit(3), value: lit(3) } ])); + +// statements + +assertStmt("throw 42", throwStmt(lit(42))); +assertStmt("for (;;) break", forStmt(null, null, null, breakStmt(null))); +assertStmt("for (x; y; z) break", forStmt(ident("x"), ident("y"), ident("z"), breakStmt(null))); +assertStmt("for (var x; y; z) break", forStmt(varDecl([{ id: ident("x"), init: null }]), ident("y"), ident("z"))); +assertStmt("for (var x = 42; y; z) break", forStmt(varDecl([{ id: ident("x"), init: lit(42) }]), ident("y"), ident("z"))); +assertStmt("for (x; ; z) break", forStmt(ident("x"), null, ident("z"), breakStmt(null))); +assertStmt("for (var x; ; z) break", forStmt(varDecl([{ id: ident("x"), init: null }]), null, ident("z"))); +assertStmt("for (var x = 42; ; z) break", forStmt(varDecl([{ id: ident("x"), init: lit(42) }]), null, ident("z"))); +assertStmt("for (x; y; ) break", forStmt(ident("x"), ident("y"), null, breakStmt(null))); +assertStmt("for (var x; y; ) break", forStmt(varDecl([{ id: ident("x"), init: null }]), ident("y"), null, breakStmt(null))); +assertStmt("for (var x = 42; y; ) break", forStmt(varDecl([{ id: ident("x"), init: lit(42) }]), ident("y"), null, breakStmt(null))); +assertStmt("for (var x in y) break", forInStmt(varDecl([{ id: ident("x"), init: null }]), ident("y"), breakStmt(null))); +assertStmt("for (x in y) break", forInStmt(ident("x"), ident("y"), breakStmt(null))); +assertStmt("{ }", blockStmt([])); +assertStmt("{ throw 1; throw 2; throw 3; }", blockStmt([ throwStmt(lit(1)), throwStmt(lit(2)), throwStmt(lit(3))])); +assertStmt(";", emptyStmt); +assertStmt("if (foo) throw 42;", ifStmt(ident("foo"), throwStmt(lit(42)), null)); +assertStmt("if (foo) throw 42; else true;", ifStmt(ident("foo"), throwStmt(lit(42)), exprStmt(lit(true)))); +assertStmt("if (foo) { throw 1; throw 2; throw 3; }", + ifStmt(ident("foo"), + blockStmt([throwStmt(lit(1)), throwStmt(lit(2)), throwStmt(lit(3))]), + null)); +assertStmt("if (foo) { throw 1; throw 2; throw 3; } else true;", + ifStmt(ident("foo"), + blockStmt([throwStmt(lit(1)), throwStmt(lit(2)), throwStmt(lit(3))]), + exprStmt(lit(true)))); +assertStmt("foo: for(;;) break foo;", labStmt(ident("foo"), forStmt(null, null, null, breakStmt(ident("foo"))))); +assertStmt("foo: for(;;) continue foo;", labStmt(ident("foo"), forStmt(null, null, null, continueStmt(ident("foo"))))); +assertStmt("with (obj) { }", withStmt(ident("obj"), blockStmt([]))); +assertStmt("with (obj) { obj; }", withStmt(ident("obj"), blockStmt([exprStmt(ident("obj"))]))); +assertStmt("while (foo) { }", whileStmt(ident("foo"), blockStmt([]))); +assertStmt("while (foo) { foo; }", whileStmt(ident("foo"), blockStmt([exprStmt(ident("foo"))]))); +assertStmt("do { } while (foo);", doStmt(blockStmt([]), ident("foo"))); +assertStmt("do { foo; } while (foo)", doStmt(blockStmt([exprStmt(ident("foo"))]), ident("foo"))); +assertStmt("switch (foo) { case 1: 1; break; case 2: 2; break; default: 3; }", + switchStmt(ident("foo"), + [ caseClause(lit(1), [ exprStmt(lit(1)), breakStmt(null) ]), + caseClause(lit(2), [ exprStmt(lit(2)), breakStmt(null) ]), + defaultClause([ exprStmt(lit(3)) ]) ])); +assertStmt("switch (foo) { case 1: 1; break; case 2: 2; break; default: 3; case 42: 42; }", + switchStmt(ident("foo"), + [ caseClause(lit(1), [ exprStmt(lit(1)), breakStmt(null) ]), + caseClause(lit(2), [ exprStmt(lit(2)), breakStmt(null) ]), + defaultClause([ exprStmt(lit(3)) ]), + caseClause(lit(42), [ exprStmt(lit(42)) ]) ])); +assertStmt("try { } catch (e) { }", + tryStmt(blockStmt([]), + catchClause(ident("e"), null, blockStmt([])), + null)); +assertStmt("try { } catch (e) { } finally { }", + tryStmt(blockStmt([]), + catchClause(ident("e"), null, blockStmt([])), + blockStmt([]))); +assertStmt("try { } finally { }", + tryStmt(blockStmt([]), + null, + blockStmt([]))); +//assertStmt("try { } catch (e if foo) { } catch (e if bar) { } finally { }", + //tryStmt(blockStmt([]), + //[ catchClause(ident("e"), ident("foo"), blockStmt([])), + //catchClause(ident("e"), ident("bar"), blockStmt([])) ], + //blockStmt([]))); +//assertStmt("try { } catch (e if foo) { } catch (e if bar) { } catch (e) { } finally { }", + //tryStmt(blockStmt([]), + //[ catchClause(ident("e"), ident("foo"), blockStmt([])), + //catchClause(ident("e"), ident("bar"), blockStmt([])), + //catchClause(ident("e"), null, blockStmt([])) ], + //blockStmt([]))); + +// redeclarations (TOK_NAME nodes with lexdef) + +assertStmt("function f() { function g() { } function g() { } }", + funDecl(ident("f"), [], blockStmt([funDecl(ident("g"), [], blockStmt([])), + funDecl(ident("g"), [], blockStmt([]))]))); + +assertStmt("function f() { function g() { } function g() { return 42 } }", + funDecl(ident("f"), [], blockStmt([funDecl(ident("g"), [], blockStmt([])), + funDecl(ident("g"), [], blockStmt([returnStmt(lit(42))]))]))); + +assertStmt("function f() { var x = 42; var x = 43; }", + funDecl(ident("f"), [], blockStmt([varDecl([{ id: ident("x"), init: lit(42) }]), + varDecl([{ id: ident("x"), init: lit(43) }])]))); + + +//assertDecl("var {x:y} = foo;", varDecl([{ id: objPatt([{ key: ident("x"), value: ident("y") }]), + //init: ident("foo") }])); + +// getters and setters + +assertExpr("({ get x() { return 42 } })", + objExpr([ { key: ident("x"), + value: funExpr(null, [], blockStmt([returnStmt(lit(42))])), + kind: "get" } ])); +assertExpr("({ set x(v) { return 42 } })", + objExpr([ { key: ident("x"), + value: funExpr(null, [ident("v")], blockStmt([returnStmt(lit(42))])), + kind: "set" } ])); + +/* +// global let is var +assertGlobalDecl("let {x:y} = foo;", varDecl([{ id: objPatt([{ key: ident("x"), value: ident("y") }]), + init: ident("foo") }])); +// function-global let is var +assertLocalDecl("let {x:y} = foo;", varDecl([{ id: objPatt([{ key: ident("x"), value: ident("y") }]), + init: ident("foo") }])); +// block-local let is let +assertBlockDecl("let {x:y} = foo;", letDecl([{ id: objPatt([{ key: ident("x"), value: ident("y") }]), + init: ident("foo") }])); + +assertDecl("const {x:y} = foo;", constDecl([{ id: objPatt([{ key: ident("x"), value: ident("y") }]), + init: ident("foo") }])); + + +// various combinations of identifiers and destructuring patterns: +function makePatternCombinations(id, destr) + [ + [ id(1) ], + [ id(1), id(2) ], + [ id(1), id(2), id(3) ], + [ id(1), id(2), id(3), id(4) ], + [ id(1), id(2), id(3), id(4), id(5) ], + + [ destr(1) ], + [ destr(1), destr(2) ], + [ destr(1), destr(2), destr(3) ], + [ destr(1), destr(2), destr(3), destr(4) ], + [ destr(1), destr(2), destr(3), destr(4), destr(5) ], + + [ destr(1), id(2) ], + + [ destr(1), id(2), id(3) ], + [ destr(1), id(2), id(3), id(4) ], + [ destr(1), id(2), id(3), id(4), id(5) ], + [ destr(1), id(2), id(3), id(4), destr(5) ], + [ destr(1), id(2), id(3), destr(4) ], + [ destr(1), id(2), id(3), destr(4), id(5) ], + [ destr(1), id(2), id(3), destr(4), destr(5) ], + + [ destr(1), id(2), destr(3) ], + [ destr(1), id(2), destr(3), id(4) ], + [ destr(1), id(2), destr(3), id(4), id(5) ], + [ destr(1), id(2), destr(3), id(4), destr(5) ], + [ destr(1), id(2), destr(3), destr(4) ], + [ destr(1), id(2), destr(3), destr(4), id(5) ], + [ destr(1), id(2), destr(3), destr(4), destr(5) ], + + [ id(1), destr(2) ], + + [ id(1), destr(2), id(3) ], + [ id(1), destr(2), id(3), id(4) ], + [ id(1), destr(2), id(3), id(4), id(5) ], + [ id(1), destr(2), id(3), id(4), destr(5) ], + [ id(1), destr(2), id(3), destr(4) ], + [ id(1), destr(2), id(3), destr(4), id(5) ], + [ id(1), destr(2), id(3), destr(4), destr(5) ], + + [ id(1), destr(2), destr(3) ], + [ id(1), destr(2), destr(3), id(4) ], + [ id(1), destr(2), destr(3), id(4), id(5) ], + [ id(1), destr(2), destr(3), id(4), destr(5) ], + [ id(1), destr(2), destr(3), destr(4) ], + [ id(1), destr(2), destr(3), destr(4), id(5) ], + [ id(1), destr(2), destr(3), destr(4), destr(5) ] + ] + +// destructuring function parameters + +function testParamPatternCombinations(makePattSrc, makePattPatt) { + var pattSrcs = makePatternCombinations(function(n) ("x" + n), makePattSrc); + var pattPatts = makePatternCombinations(function(n) (ident("x" + n)), makePattPatt); + + for (var i = 0; i < pattSrcs.length; i++) { + function makeSrc(body) ("(function(" + pattSrcs[i].join(",") + ") " + body + ")") + function makePatt(body) (funExpr(null, pattPatts[i], body)) + + // no upvars, block body + assertExpr(makeSrc("{ }", makePatt(blockStmt([])))); + // upvars, block body + assertExpr(makeSrc("{ return [x1,x2,x3,x4,x5]; }"), + makePatt(blockStmt([returnStmt(arrExpr([ident("x1"), ident("x2"), ident("x3"), ident("x4"), ident("x5")]))]))); + // no upvars, expression body + assertExpr(makeSrc("(0)"), makePatt(lit(0))); + // upvars, expression body + assertExpr(makeSrc("[x1,x2,x3,x4,x5]"), + makePatt(arrExpr([ident("x1"), ident("x2"), ident("x3"), ident("x4"), ident("x5")]))); + } +} + +testParamPatternCombinations(function(n) ("{a" + n + ":x" + n + "," + "b" + n + ":y" + n + "," + "c" + n + ":z" + n + "}"), + function(n) (objPatt([{ key: ident("a" + n), value: ident("x" + n) }, + { key: ident("b" + n), value: ident("y" + n) }, + { key: ident("c" + n), value: ident("z" + n) }]))); + +testParamPatternCombinations(function(n) ("[x" + n + "," + "y" + n + "," + "z" + n + "]"), + function(n) (arrPatt([ident("x" + n), ident("y" + n), ident("z" + n)]))); + + +// destructuring variable declarations + +function testVarPatternCombinations(makePattSrc, makePattPatt) { + var pattSrcs = makePatternCombinations(function(n) ("x" + n), makePattSrc); + var pattPatts = makePatternCombinations(function(n) ({ id: ident("x" + n), init: null }), makePattPatt); + + for (var i = 0; i < pattSrcs.length; i++) { + // variable declarations in blocks + assertDecl("var " + pattSrcs[i].join(",") + ";", varDecl(pattPatts[i])); + + assertGlobalDecl("let " + pattSrcs[i].join(",") + ";", varDecl(pattPatts[i])); + assertLocalDecl("let " + pattSrcs[i].join(",") + ";", varDecl(pattPatts[i])); + assertBlockDecl("let " + pattSrcs[i].join(",") + ";", letDecl(pattPatts[i])); + + assertDecl("const " + pattSrcs[i].join(",") + ";", constDecl(pattPatts[i])); + + // variable declarations in for-loop heads + assertStmt("for (var " + pattSrcs[i].join(",") + "; foo; bar);", + forStmt(varDecl(pattPatts[i]), ident("foo"), ident("bar"), emptyStmt)); + assertStmt("for (let " + pattSrcs[i].join(",") + "; foo; bar);", + forStmt(letDecl(pattPatts[i]), ident("foo"), ident("bar"), emptyStmt)); + assertStmt("for (const " + pattSrcs[i].join(",") + "; foo; bar);", + forStmt(constDecl(pattPatts[i]), ident("foo"), ident("bar"), emptyStmt)); + } +} + +testVarPatternCombinations(function (n) ("{a" + n + ":x" + n + "," + "b" + n + ":y" + n + "," + "c" + n + ":z" + n + "} = 0"), + function (n) ({ id: objPatt([{ key: ident("a" + n), value: ident("x" + n) }, + { key: ident("b" + n), value: ident("y" + n) }, + { key: ident("c" + n), value: ident("z" + n) }]), + init: lit(0) })); + +testVarPatternCombinations(function(n) ("[x" + n + "," + "y" + n + "," + "z" + n + "] = 0"), + function(n) ({ id: arrPatt([ident("x" + n), ident("y" + n), ident("z" + n)]), + init: lit(0) })); + +// destructuring assignment + +function testAssignmentCombinations(makePattSrc, makePattPatt) { + var pattSrcs = makePatternCombinations(function(n) ("x" + n + " = 0"), makePattSrc); + var pattPatts = makePatternCombinations(function(n) (aExpr("=", ident("x" + n), lit(0))), makePattPatt); + + for (var i = 0; i < pattSrcs.length; i++) { + var src = pattSrcs[i].join(","); + var patt = pattPatts[i].length === 1 ? pattPatts[i][0] : seqExpr(pattPatts[i]); + + // assignment expression statement + assertExpr("(" + src + ")", patt); + + // for-loop head assignment + assertStmt("for (" + src + "; foo; bar);", + forStmt(patt, ident("foo"), ident("bar"), emptyStmt)); + } +} + +testAssignmentCombinations(function (n) ("{a" + n + ":x" + n + "," + "b" + n + ":y" + n + "," + "c" + n + ":z" + n + "} = 0"), + function (n) (aExpr("=", + objPatt([{ key: ident("a" + n), value: ident("x" + n) }, + { key: ident("b" + n), value: ident("y" + n) }, + { key: ident("c" + n), value: ident("z" + n) }]), + lit(0)))); + + +// destructuring in for-in and for-each-in loop heads + +var axbycz = objPatt([{ key: ident("a"), value: ident("x") }, + { key: ident("b"), value: ident("y") }, + { key: ident("c"), value: ident("z") }]); +var xyz = arrPatt([ident("x"), ident("y"), ident("z")]); + +assertStmt("for (var {a:x,b:y,c:z} in foo);", forInStmt(varDecl([{ id: axbycz, init: null }]), ident("foo"), emptyStmt)); +assertStmt("for (let {a:x,b:y,c:z} in foo);", forInStmt(letDecl([{ id: axbycz, init: null }]), ident("foo"), emptyStmt)); +assertStmt("for ({a:x,b:y,c:z} in foo);", forInStmt(axbycz, ident("foo"), emptyStmt)); +assertStmt("for (var [x,y,z] in foo);", forInStmt(varDecl([{ id: xyz, init: null }]), ident("foo"), emptyStmt)); +assertStmt("for (let [x,y,z] in foo);", forInStmt(letDecl([{ id: xyz, init: null }]), ident("foo"), emptyStmt)); +assertStmt("for ([x,y,z] in foo);", forInStmt(xyz, ident("foo"), emptyStmt)); +assertStmt("for each (var {a:x,b:y,c:z} in foo);", forEachInStmt(varDecl([{ id: axbycz, init: null }]), ident("foo"), emptyStmt)); +assertStmt("for each (let {a:x,b:y,c:z} in foo);", forEachInStmt(letDecl([{ id: axbycz, init: null }]), ident("foo"), emptyStmt)); +assertStmt("for each ({a:x,b:y,c:z} in foo);", forEachInStmt(axbycz, ident("foo"), emptyStmt)); +assertStmt("for each (var [x,y,z] in foo);", forEachInStmt(varDecl([{ id: xyz, init: null }]), ident("foo"), emptyStmt)); +assertStmt("for each (let [x,y,z] in foo);", forEachInStmt(letDecl([{ id: xyz, init: null }]), ident("foo"), emptyStmt)); +assertStmt("for each ([x,y,z] in foo);", forEachInStmt(xyz, ident("foo"), emptyStmt)); +assertError("for (const x in foo);", SyntaxError); +assertError("for (const {a:x,b:y,c:z} in foo);", SyntaxError); +assertError("for (const [x,y,z] in foo);", SyntaxError); +assertError("for each (const x in foo);", SyntaxError); +assertError("for each (const {a:x,b:y,c:z} in foo);", SyntaxError); +assertError("for each (const [x,y,z] in foo);", SyntaxError); + +// destructuring in for-in and for-each-in loop heads with initializers + +assertStmt("for (var {a:x,b:y,c:z} = 22 in foo);", forInStmt(varDecl([{ id: axbycz, init: lit(22) }]), ident("foo"), emptyStmt)); +assertStmt("for (var [x,y,z] = 22 in foo);", forInStmt(varDecl([{ id: xyz, init: lit(22) }]), ident("foo"), emptyStmt)); +assertStmt("for each (var {a:x,b:y,c:z} = 22 in foo);", forEachInStmt(varDecl([{ id: axbycz, init: lit(22) }]), ident("foo"), emptyStmt)); +assertStmt("for each (var [x,y,z] = 22 in foo);", forEachInStmt(varDecl([{ id: xyz, init: lit(22) }]), ident("foo"), emptyStmt)); +assertError("for (x = 22 in foo);", SyntaxError); +assertError("for ({a:x,b:y,c:z} = 22 in foo);", SyntaxError); +assertError("for ([x,y,z] = 22 in foo);", SyntaxError); +assertError("for (const x = 22 in foo);", SyntaxError); +assertError("for (const {a:x,b:y,c:z} = 22 in foo);", SyntaxError); +assertError("for (const [x,y,z] = 22 in foo);", SyntaxError); +assertError("for each (const x = 22 in foo);", SyntaxError); +assertError("for each (const {a:x,b:y,c:z} = 22 in foo);", SyntaxError); +assertError("for each (const [x,y,z] = 22 in foo);", SyntaxError); + +// expression closures + +assertDecl("function inc(x) (x + 1)", funDecl(ident("inc"), [ident("x")], binExpr("+", ident("x"), lit(1)))); +assertExpr("(function(x) (x+1))", funExpr(null, [ident("x")], binExpr("+"), ident("x"), lit(1))); + +// generators + +assertDecl("function gen(x) { yield }", genFunDecl(ident("gen"), [ident("x")], blockStmt([exprStmt(yieldExpr(null))]))); +assertExpr("(function(x) { yield })", genFunExpr(null, [ident("x")], blockStmt([exprStmt(yieldExpr(null))]))); +assertDecl("function gen(x) { yield 42 }", genFunDecl(ident("gen"), [ident("x")], blockStmt([exprStmt(yieldExpr(lit(42)))]))); +assertExpr("(function(x) { yield 42 })", genFunExpr(null, [ident("x")], blockStmt([exprStmt(yieldExpr(lit(42)))]))); + + +// comprehensions + +assertExpr("[ x for (x in foo)]", + compExpr(ident("x"), [compBlock(ident("x"), ident("foo"))], null)); +assertExpr("[ [x,y] for (x in foo) for (y in bar)]", + compExpr(arrExpr([ident("x"), ident("y")]), [compBlock(ident("x"), ident("foo")), compBlock(ident("y"), ident("bar"))], null)); +assertExpr("[ [x,y,z] for (x in foo) for (y in bar) for (z in baz)]", + compExpr(arrExpr([ident("x"), ident("y"), ident("z")]), + [compBlock(ident("x"), ident("foo")), compBlock(ident("y"), ident("bar")), compBlock(ident("z"), ident("baz"))], + null)); + +assertExpr("[ x for (x in foo) if (p)]", + compExpr(ident("x"), [compBlock(ident("x"), ident("foo"))], ident("p"))); +assertExpr("[ [x,y] for (x in foo) for (y in bar) if (p)]", + compExpr(arrExpr([ident("x"), ident("y")]), [compBlock(ident("x"), ident("foo")), compBlock(ident("y"), ident("bar"))], ident("p"))); +assertExpr("[ [x,y,z] for (x in foo) for (y in bar) for (z in baz) if (p) ]", + compExpr(arrExpr([ident("x"), ident("y"), ident("z")]), + [compBlock(ident("x"), ident("foo")), compBlock(ident("y"), ident("bar")), compBlock(ident("z"), ident("baz"))], + ident("p"))); + +assertExpr("[ x for each (x in foo)]", + compExpr(ident("x"), [compEachBlock(ident("x"), ident("foo"))], null)); +assertExpr("[ [x,y] for each (x in foo) for each (y in bar)]", + compExpr(arrExpr([ident("x"), ident("y")]), [compEachBlock(ident("x"), ident("foo")), compEachBlock(ident("y"), ident("bar"))], null)); +assertExpr("[ [x,y,z] for each (x in foo) for each (y in bar) for each (z in baz)]", + compExpr(arrExpr([ident("x"), ident("y"), ident("z")]), + [compEachBlock(ident("x"), ident("foo")), compEachBlock(ident("y"), ident("bar")), compEachBlock(ident("z"), ident("baz"))], + null)); + +assertExpr("[ x for each (x in foo) if (p)]", + compExpr(ident("x"), [compEachBlock(ident("x"), ident("foo"))], ident("p"))); +assertExpr("[ [x,y] for each (x in foo) for each (y in bar) if (p)]", + compExpr(arrExpr([ident("x"), ident("y")]), [compEachBlock(ident("x"), ident("foo")), compEachBlock(ident("y"), ident("bar"))], ident("p"))); +assertExpr("[ [x,y,z] for each (x in foo) for each (y in bar) for each (z in baz) if (p) ]", + compExpr(arrExpr([ident("x"), ident("y"), ident("z")]), + [compEachBlock(ident("x"), ident("foo")), compEachBlock(ident("y"), ident("bar")), compEachBlock(ident("z"), ident("baz"))], + ident("p"))); + +// generator expressions + +assertExpr("( x for (x in foo))", + genExpr(ident("x"), [compBlock(ident("x"), ident("foo"))], null)); +assertExpr("( [x,y] for (x in foo) for (y in bar))", + genExpr(arrExpr([ident("x"), ident("y")]), [compBlock(ident("x"), ident("foo")), compBlock(ident("y"), ident("bar"))], null)); +assertExpr("( [x,y,z] for (x in foo) for (y in bar) for (z in baz))", + genExpr(arrExpr([ident("x"), ident("y"), ident("z")]), + [compBlock(ident("x"), ident("foo")), compBlock(ident("y"), ident("bar")), compBlock(ident("z"), ident("baz"))], + null)); + +assertExpr("( x for (x in foo) if (p))", + genExpr(ident("x"), [compBlock(ident("x"), ident("foo"))], ident("p"))); +assertExpr("( [x,y] for (x in foo) for (y in bar) if (p))", + genExpr(arrExpr([ident("x"), ident("y")]), [compBlock(ident("x"), ident("foo")), compBlock(ident("y"), ident("bar"))], ident("p"))); +assertExpr("( [x,y,z] for (x in foo) for (y in bar) for (z in baz) if (p) )", + genExpr(arrExpr([ident("x"), ident("y"), ident("z")]), + [compBlock(ident("x"), ident("foo")), compBlock(ident("y"), ident("bar")), compBlock(ident("z"), ident("baz"))], + ident("p"))); + +assertExpr("( x for each (x in foo))", + genExpr(ident("x"), [compEachBlock(ident("x"), ident("foo"))], null)); +assertExpr("( [x,y] for each (x in foo) for each (y in bar))", + genExpr(arrExpr([ident("x"), ident("y")]), [compEachBlock(ident("x"), ident("foo")), compEachBlock(ident("y"), ident("bar"))], null)); +assertExpr("( [x,y,z] for each (x in foo) for each (y in bar) for each (z in baz))", + genExpr(arrExpr([ident("x"), ident("y"), ident("z")]), + [compEachBlock(ident("x"), ident("foo")), compEachBlock(ident("y"), ident("bar")), compEachBlock(ident("z"), ident("baz"))], + null)); + +assertExpr("( x for each (x in foo) if (p))", + genExpr(ident("x"), [compEachBlock(ident("x"), ident("foo"))], ident("p"))); +assertExpr("( [x,y] for each (x in foo) for each (y in bar) if (p))", + genExpr(arrExpr([ident("x"), ident("y")]), [compEachBlock(ident("x"), ident("foo")), compEachBlock(ident("y"), ident("bar"))], ident("p"))); +assertExpr("( [x,y,z] for each (x in foo) for each (y in bar) for each (z in baz) if (p) )", + genExpr(arrExpr([ident("x"), ident("y"), ident("z")]), + [compEachBlock(ident("x"), ident("foo")), compEachBlock(ident("y"), ident("bar")), compEachBlock(ident("z"), ident("baz"))], + ident("p"))); + +// NOTE: it would be good to test generator expressions both with and without upvars, just like functions above. + + +// sharp variables + +assertExpr("#1={me:#1#}", graphExpr(1, objExpr([{ key: ident("me"), value: idxExpr(1) }]))); + +// let expressions + +assertExpr("(let (x=1) x)", letExpr([{ id: ident("x"), init: lit(1) }], ident("x"))); +assertExpr("(let (x=1,y=2) y)", letExpr([{ id: ident("x"), init: lit(1) }, + { id: ident("y"), init: lit(2) }], + ident("y"))); +assertExpr("(let (x=1,y=2,z=3) z)", letExpr([{ id: ident("x"), init: lit(1) }, + { id: ident("y"), init: lit(2) }, + { id: ident("z"), init: lit(3) }], + ident("z"))); +assertExpr("(let (x) x)", letExpr([{ id: ident("x"), init: null }], ident("x"))); +assertExpr("(let (x,y) y)", letExpr([{ id: ident("x"), init: null }, + { id: ident("y"), init: null }], + ident("y"))); +assertExpr("(let (x,y,z) z)", letExpr([{ id: ident("x"), init: null }, + { id: ident("y"), init: null }, + { id: ident("z"), init: null }], + ident("z"))); +assertExpr("(let (x = 1, y = x) y)", letExpr([{ id: ident("x"), init: lit(1) }, + { id: ident("y"), init: ident("x") }], + ident("y"))); +assertError("(let (x = 1, x = 2) x)", TypeError); + +// let statements + +assertStmt("let (x=1) { }", letStmt([{ id: ident("x"), init: lit(1) }], blockStmt([]))); +assertStmt("let (x=1,y=2) { }", letStmt([{ id: ident("x"), init: lit(1) }, + { id: ident("y"), init: lit(2) }], + blockStmt([]))); +assertStmt("let (x=1,y=2,z=3) { }", letStmt([{ id: ident("x"), init: lit(1) }, + { id: ident("y"), init: lit(2) }, + { id: ident("z"), init: lit(3) }], + blockStmt([]))); +assertStmt("let (x) { }", letStmt([{ id: ident("x"), init: null }], blockStmt([]))); +assertStmt("let (x,y) { }", letStmt([{ id: ident("x"), init: null }, + { id: ident("y"), init: null }], + blockStmt([]))); +assertStmt("let (x,y,z) { }", letStmt([{ id: ident("x"), init: null }, + { id: ident("y"), init: null }, + { id: ident("z"), init: null }], + blockStmt([]))); +assertStmt("let (x = 1, y = x) { }", letStmt([{ id: ident("x"), init: lit(1) }, + { id: ident("y"), init: ident("x") }], + blockStmt([]))); +assertError("let (x = 1, x = 2) { }", TypeError); + + +// E4X + +assertExpr("x..tagName", binExpr("..", ident("x"), lit("tagName"))); +assertExpr("x.*", dotExpr(ident("x"), xmlAnyName)); +assertExpr("x[*]", memExpr(ident("x"), xmlAnyName)); +assertExpr("x::y", xmlQualId(ident("x"), ident("y"), false)); +assertExpr("x::[foo]", xmlQualId(ident("x"), ident("foo"), true)); +assertExpr("x::[foo()]", xmlQualId(ident("x"), callExpr(ident("foo"), []), true)); +assertExpr("*::*", xmlQualId(xmlAnyName, ident("*"), false)); +assertExpr("*::[foo]", xmlQualId(xmlAnyName, ident("foo"), true)); +assertExpr("*::[foo()]", xmlQualId(xmlAnyName, callExpr(ident("foo"), []), true)); +assertExpr("x.y::z", dotExpr(ident("x"), xmlQualId(ident("y"), ident("z"), false))); +assertExpr("x[y::z]", memExpr(ident("x"), xmlQualId(ident("y"), ident("z"), false))); +assertExpr("x[y::[z]]", memExpr(ident("x"), xmlQualId(ident("y"), ident("z"), true))); +assertExpr("function::x", xmlFuncQualId(ident("x"), false)); +assertExpr("function::[foo]", xmlFuncQualId(ident("foo"), true)); +assertExpr("@foo", xmlAttrSel(ident("foo"), false)); +assertExpr("@[foo]", xmlAttrSel(ident("foo"), true)); +assertExpr("x.@foo", dotExpr(ident("x"), xmlAttrSel(ident("foo"), false))); +assertExpr("x.@[foo]", dotExpr(ident("x"), xmlAttrSel(ident("foo"), true))); +assertExpr("x[@foo]", memExpr(ident("x"), xmlAttrSel(ident("foo"), false))); +assertExpr("x[@[foo]]", memExpr(ident("x"), xmlAttrSel(ident("foo"), true))); +assertExpr("x.(p)", xmlFilter(ident("x"), ident("p"))); +assertExpr("<{foo}/>", xmlPointTag([xmlEscape(ident("foo"))])); +assertExpr("<{foo}>", xmlElt([xmlStartTag([xmlEscape(ident("foo"))]), + xmlEndTag([xmlEscape(ident("foo"))])])); +assertExpr("<{foo} {attr}='attr'/>", xmlPointTag([xmlEscape(ident("foo")), + xmlEscape(ident("attr")), + xmlAttr("attr")])); +assertExpr("<{foo}>text", xmlElt([xmlStartTag([xmlEscape(ident("foo"))]), + xmlText("text"), + xmlEndTag([xmlEscape(ident("foo"))])])); +assertExpr("", xmlPI("xml", "")); +assertExpr("", xmlPI("xml", "version='1.0'")); +assertDecl("default xml namespace = 'js';", xmlDefNS(lit("js"))); +assertDecl("default xml namespace = foo;", xmlDefNS(ident("foo"))); + +// The parser turns these into TOK_UNARY nodes with pn_op == JSOP_SETXMLNAME. + +assertExpr("x::y = foo", aExpr("=", xmlQualId(ident("x"), ident("y"), false), ident("foo"))); +assertExpr("function::x = foo", aExpr("=", xmlFuncQualId(ident("x"), false), ident("foo"))); +assertExpr("@x = foo", aExpr("=", xmlAttrSel(ident("x")), ident("foo"))); +assertExpr("x::* = foo", aExpr("=", xmlQualId(ident("x"), ident("*"), false), ident("foo"))); +assertExpr("*::* = foo", aExpr("=", xmlQualId(xmlAnyName, ident("*"), false), ident("foo"))); +assertExpr("x.* = foo", aExpr("=", dotExpr(ident("x"), xmlAnyName), ident("foo"))); +assertExpr("x[*] = foo", aExpr("=", memExpr(ident("x"), xmlAnyName), ident("foo"))); + +assertExpr("x::y += foo", aExpr("+=", xmlQualId(ident("x"), ident("y"), false), ident("foo"))); +assertExpr("function::x += foo", aExpr("+=", xmlFuncQualId(ident("x"), false), ident("foo"))); +assertExpr("@x += foo", aExpr("+=", xmlAttrSel(ident("x")), ident("foo"))); +assertExpr("x::* += foo", aExpr("+=", xmlQualId(ident("x"), ident("*"), false), ident("foo"))); +assertExpr("*::* += foo", aExpr("+=", xmlQualId(xmlAnyName, ident("*"), false), ident("foo"))); +assertExpr("x.* += foo", aExpr("+=", dotExpr(ident("x"), xmlAnyName), ident("foo"))); +assertExpr("x[*] += foo", aExpr("+=", memExpr(ident("x"), xmlAnyName), ident("foo"))); + +assertExpr("x::y++", updExpr("++", xmlQualId(ident("x"), ident("y"), false), false)); +assertExpr("function::x++", updExpr("++", xmlFuncQualId(ident("x"), false), false)); +assertExpr("@x++", updExpr("++", xmlAttrSel(ident("x")), false)); +assertExpr("x::*++", updExpr("++", xmlQualId(ident("x"), ident("*"), false), false)); +assertExpr("*::*++", updExpr("++", xmlQualId(xmlAnyName, ident("*"), false), false)); +assertExpr("x.*++", updExpr("++", dotExpr(ident("x"), xmlAnyName), false)); +assertExpr("x[*]++", updExpr("++", memExpr(ident("x"), xmlAnyName), false)); + +assertExpr("++x::y", updExpr("++", xmlQualId(ident("x"), ident("y"), false), true)); +assertExpr("++function::x", updExpr("++", xmlFuncQualId(ident("x"), false), true)); +assertExpr("++@x", updExpr("++", xmlAttrSel(ident("x")), true)); +assertExpr("++x::*", updExpr("++", xmlQualId(ident("x"), ident("*"), false), true)); +assertExpr("++*::*", updExpr("++", xmlQualId(xmlAnyName, ident("*"), false), true)); +assertExpr("++x.*", updExpr("++", dotExpr(ident("x"), xmlAnyName), true)); +assertExpr("++x[*]", updExpr("++", memExpr(ident("x"), xmlAnyName), true)); + + +// The parser turns these into TOK_UNARY nodes with pn_op == JSOP_BINDXMLNAME. + +function singletonObjPatt(name, val) objPatt([{ key: ident(name), value: val }]) + +assertExpr("({a:x::y}) = foo", aExpr("=", singletonObjPatt("a", xmlQualId(ident("x"), ident("y"), false)), ident("foo"))); +assertExpr("({a:function::x}) = foo", aExpr("=", singletonObjPatt("a", xmlFuncQualId(ident("x"), false)), ident("foo"))); +assertExpr("({a:@x}) = foo", aExpr("=", singletonObjPatt("a", xmlAttrSel(ident("x"))), ident("foo"))); +assertExpr("({a:x::*}) = foo", aExpr("=", singletonObjPatt("a", xmlQualId(ident("x"), ident("*"), false)), ident("foo"))); +assertExpr("({a:*::*}) = foo", aExpr("=", singletonObjPatt("a", xmlQualId(xmlAnyName, ident("*"), false)), ident("foo"))); +assertExpr("({a:x.*}) = foo", aExpr("=", singletonObjPatt("a", dotExpr(ident("x"), xmlAnyName)), ident("foo"))); +assertExpr("({a:x[*]}) = foo", aExpr("=", singletonObjPatt("a", memExpr(ident("x"), xmlAnyName)), ident("foo"))); + +function emptyForInPatt(val, rhs) forInStmt(val, rhs, emptyStmt) + +assertStmt("for (x::y in foo);", emptyForInPatt(xmlQualId(ident("x"), ident("y"), false), ident("foo"))); +assertStmt("for (function::x in foo);", emptyForInPatt(xmlFuncQualId(ident("x"), false), ident("foo"))); +assertStmt("for (@x in foo);", emptyForInPatt(xmlAttrSel(ident("x")), ident("foo"))); +assertStmt("for (x::* in foo);", emptyForInPatt(xmlQualId(ident("x"), ident("*"), false), ident("foo"))); +assertStmt("for (*::* in foo);", emptyForInPatt(xmlQualId(xmlAnyName, ident("*"), false), ident("foo"))); +assertStmt("for (x.* in foo);", emptyForInPatt(dotExpr(ident("x"), xmlAnyName), ident("foo"))); +assertStmt("for (x[*] in foo);", emptyForInPatt(memExpr(ident("x"), xmlAnyName), ident("foo"))); + + +// I'm not quite sure why, but putting XML in the callee of a call expression is +// the only way I've found to be able to preserve TOK_XMLNAME, TOK_XMLSPACE, +// TOK_XMLCDATA, and TOK_XMLCOMMENT parse nodes. + +assertExpr("( )()", callExpr(xmlElt([xmlStartTag([xmlName("x")]), + xmlText(" "), + xmlEndTag([xmlName("x")])]), + [])); +assertExpr("( )()", callExpr(xmlElt([xmlStartTag([xmlName("x")]), + xmlText(" "), + xmlEndTag([xmlName("x")])]), + [])); +assertExpr("()()", callExpr(xmlElt([xmlStartTag([xmlName("x")]), + xmlCdata("hello, world"), + xmlEndTag([xmlName("x")])]), + [])); +assertExpr("()()", callExpr(xmlElt([xmlStartTag([xmlName("x")]), + xmlComment(" hello, world "), + xmlEndTag([xmlName("x")])]), + [])); + +*/ + +// Source location information + +var withoutFileOrLine = Reflect.parse("42"); +var withFile = Reflect.parse("42", {source:"foo.js"}); +var withFileAndLine = Reflect.parse("42", {source:"foo.js", line:111}); + +Pattern({ source: null, start: { line: 1, column: 0 }, end: { line: 1, column: 2 } }).match(withoutFileOrLine.loc); +Pattern({ source: "foo.js", start: { line: 1, column: 0 }, end: { line: 1, column: 2 } }).match(withFile.loc); +Pattern({ source: "foo.js", start: { line: 111, column: 0 }, end: { line: 111, column: 2 } }).match(withFileAndLine.loc); + +var withoutFileOrLine2 = Reflect.parse("foo +\nbar"); +var withFile2 = Reflect.parse("foo +\nbar", {source:"foo.js"}); +var withFileAndLine2 = Reflect.parse("foo +\nbar", {source:"foo.js", line:111}); + +Pattern({ source: null, start: { line: 1, column: 0 }, end: { line: 2, column: 3 } }).match(withoutFileOrLine2.loc); +Pattern({ source: "foo.js", start: { line: 1, column: 0 }, end: { line: 2, column: 3 } }).match(withFile2.loc); +Pattern({ source: "foo.js", start: { line: 111, column: 0 }, end: { line: 112, column: 3 } }).match(withFileAndLine2.loc); + +var nested = Reflect.parse("(-b + sqrt(sqr(b) - 4 * a * c)) / (2 * a)", {source:"quad.js"}); +var fourAC = nested.body[0].expression.left.right.arguments[0].right; + +Pattern({ source: "quad.js", start: { line: 1, column: 20 }, end: { line: 1, column: 29 } }).match(fourAC.loc); + + +// No source location +if(Reflect.parse("42", {loc:false}).loc !== null) throw 'not null'; +program([exprStmt(lit(42))]).assert(Reflect.parse("42", {loc:false})); + + +// Builder tests +Pattern("program").match(Reflect.parse("42", {builder:{program:function(){return "program"}}})); + +assertGlobalStmt("throw 42", 1, { throwStatement: function(){ return 1 }}); +assertGlobalStmt("for (;;);", 2, { forStatement: function(){ return 2 }}); +assertGlobalStmt("for (x in y);", 3, { forInStatement: function(){ return 3 }}); +assertGlobalStmt("{ }", 4, { blockStatement: function(){ return 4 }}); +assertGlobalStmt("foo: { }", 5, { labeledStatement: function(){ return 5 }}); +assertGlobalStmt("with (o) { }", 6, { withStatement: function(){ return 6 }}); +assertGlobalStmt("while (x) { }", 7, { whileStatement: function(){ return 7 }}); +assertGlobalStmt("do { } while(false);", 8, { doWhileStatement: function(){ return 8 }}); +assertGlobalStmt("switch (x) { }", 9, { switchStatement: function(){ return 9 }}); +assertGlobalStmt("try { } catch(e) { }", 10, { tryStatement: function(){ return 10 }}); +assertGlobalStmt(";", 11, { emptyStatement: function(){ return 11 }}); +assertGlobalStmt("debugger;", 12, { debuggerStatement: function(){ return 12 }}); +assertGlobalStmt("42;", 13, { expressionStatement: function(){ return 13 }}); +assertGlobalStmt("for (;;) break", forStmt(null, null, null, 14), { breakStatement: function(){ return 14 }}); +assertGlobalStmt("for (;;) continue", forStmt(null, null, null, 15), { continueStatement: function(){ return 15 }}); + +assertBlockDecl("var x", "var", { variableDeclaration: function(kind){ return kind }}); +//assertBlockDecl("let x", "let", { variableDeclaration: function(kind){ return kind }}); +assertBlockDecl("const x", "const", { variableDeclaration: function(kind){ return kind }}); +assertBlockDecl("function f() { }", "function", { functionDeclaration: function(){ return "function" }}); + +assertGlobalExpr("(x,y,z)", 1, { sequenceExpression: function(){ return 1 }}); +assertGlobalExpr("(x ? y : z)", 2, { conditionalExpression: function(){ return 2 }}); +assertGlobalExpr("x + y", 3, { binaryExpression: function(){ return 3 }}); +assertGlobalExpr("delete x", 4, { unaryExpression: function(){ return 4 }}); +assertGlobalExpr("x = y", 5, { assignmentExpression: function(){ return 5 }}); +assertGlobalExpr("x || y", 6, { logicalExpression: function(){ return 6 }}); +assertGlobalExpr("x++", 7, { updateExpression: function(){ return 7 }}); +assertGlobalExpr("new x", 8, { newExpression: function(){ return 8 }}); +assertGlobalExpr("x()", 9, { callExpression: function(){ return 9 }}); +assertGlobalExpr("x.y", 10, { memberExpression: function(){ return 10 }}); +assertGlobalExpr("(function(){ return { }})", 11, { functionExpression: function(){return 11 }}); +assertGlobalExpr("[1,2,3]", 12, { arrayExpression: function(){ return 12 }}); +assertGlobalExpr("({ x: y })", 13, { objectExpression: function(){ return 13 }}); +assertGlobalExpr("this", 14, { thisExpression: function(){ return 14 }}); +//assertGlobalExpr("#1={ }", 15, { graphExpression: function(){ return 15 }}); +//assertGlobalExpr("#1={ self: #1# }", graphExpr(1, objExpr([{ key: ident("self"), value: 16 }])), { graphIndexExpression: function(){ return 16 }}); +//assertGlobalExpr("[x for (x in y)]", 17, { comprehensionExpression: function(){ return 17 }}); +//assertGlobalExpr("(x for (x in y))", 18, { generatorExpression: function(){ return 18 }}); +//assertGlobalExpr("(function(){ return { yield 42 })", genFunExpr(null, [], blockStmt([exprStmt(19)])), { yieldExpression: function(){ return 19 }}); +//assertGlobalExpr("(let (x) x)", 20, { letExpression: function(){ return 20 }}); + +assertGlobalStmt("switch (x) { case y: }", switchStmt(ident("x"), [1]), { switchCase: function(){ return 1 }}); +assertGlobalStmt("try { } catch (e) { }", tryStmt(blockStmt([]), 2, null), { catchClause: function(){ return 2 }}); +//assertGlobalStmt("try { } catch (e if e instanceof A) { } catch (e if e instanceof B) { }", + //tryStmt(blockStmt([]), [2, 2], null), + //{ catchClause: function(){ return 2 }}); +//assertGlobalExpr("[x for (y in z) for (x in y)]", compExpr(ident("x"), [3, 3], null), { comprehensionBlock: function(){ return 3 }}); + +//assertGlobalExpr("({ x: y } = z)", aExpr("=", 1, ident("z")), { objectPattern: function(){ return 1 }}); +//assertGlobalExpr("({ x: y } = z)", aExpr("=", objPatt([2]), ident("z")), { propertyPattern: function(){ return 2 }}); +//assertGlobalExpr("[ x ] = y", aExpr("=", 3, ident("y")), { arrayPattern: function(){ return 3 }}); + +//assertGlobalExpr("({a:x::y}) = foo", aExpr("=", singletonObjPatt("a", 1), ident("foo")), { xmlQualifiedIdentifier: function(){ return 1 }}); +//assertGlobalExpr("({a:function::x}) = foo", aExpr("=", singletonObjPatt("a", 2), ident("foo")), { xmlFunctionQualifiedIdentifier: function(){ return 2 }}); +//assertGlobalExpr("({a:@x}) = foo", aExpr("=", singletonObjPatt("a", 3), ident("foo")), { xmlAttributeSelector: function(){ return 3 }}); +//assertGlobalExpr("({a:x.*}) = foo", aExpr("=", singletonObjPatt("a", dotExpr(ident("x"), 4)), ident("foo")), { xmlAnyName: function(){ return 4 }}); + +//assertGlobalExpr("( )()", callExpr(xmlElt([5, xmlText(" "), xmlEndTag([xmlName("x")])]), []), { xmlStartTag: function(){ return 5 }}); +//assertGlobalExpr("( )()", callExpr(xmlElt([xmlStartTag([6]), xmlText(" "), xmlEndTag([6])]), []), { xmlName: function(){ return 6 }}); +//assertGlobalExpr("( )()", callExpr(xmlElt([xmlStartTag([xmlName("x")]), 7, xmlEndTag([xmlName("x")])]), []), { xmlText: function(){ return 7 }}); +//assertGlobalExpr("( )()", callExpr(xmlElt([xmlStartTag([xmlName("x")]), xmlText(" "), 8]), []), { xmlEndTag: function(){ return 8 }}); +//assertGlobalExpr("()()", callExpr(xmlElt([xmlStartTag([xmlName("x")]), 9, xmlEndTag([xmlName("x")])]), []), { xmlCdata: function(){ return 9 }}); +//assertGlobalExpr("()()", callExpr(xmlElt([xmlStartTag([xmlName("x")]), 10, xmlEndTag([xmlName("x")])]), []), { xmlComment: function(){ return 10 }}); + + +// Ensure that exceptions thrown by builder methods propagate. +var thrown = false; +try { + Reflect.parse("42", { builder: { program: function() { throw "expected" } } }); +} catch (e) { + if (e === "expected") + thrown = true; +} +if (!thrown) + throw new Error("builder exception not propagated"); + + +// 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 +}; +})(); + +Pattern(["Program", {}, + ["BinaryExpr", {op: "+"}, + ["LiteralExpr", {value: 2}], + ["BinaryExpr", {op: "*"}, + ["UnaryExpr", {op: "-"}, ["IdExpr", {name: "x"}]], + ["IdExpr", {name: "y"}]]]]).match(Reflect.parse("2 + (-x * y)", {loc: false, builder: JsonMLAst})); + +