| // |reftest| skip-if(!xulRuntime.shell) |
| // 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. |
| |
| function test() { |
| |
| 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: expr => 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", {}]; |
| }, |
| templateLiteral: function(elts) { |
| for (var i = 0; i < elts.length; i++) { |
| if (!elts[i]) |
| elts[i] = ["Empty"]; |
| } |
| elts.unshift("TemplateLit", {}); |
| return elts; |
| }, |
| |
| graphExpression: reject, |
| graphIndexExpression: reject, |
| comprehensionExpression: reject, |
| generatorExpression: reject, |
| yieldExpression: reject, |
| |
| emptyStatement: () => ["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: () => ["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, |
| }; |
| })(); |
| |
| 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})); |
| |
| } |
| |
| runtest(test); |