bug 533874, r=jimb: expose the parser as a JS API

This commit is contained in:
Dave Herman 2010-06-15 13:32:32 -07:00
parent 77d8fcb9e6
commit 484e1a2751
14 changed files with 3890 additions and 22 deletions

View File

@ -157,6 +157,7 @@ CPPSRCS = \
jsprf.cpp \
jspropertycache.cpp \
jspropertytree.cpp \
jsreflect.cpp \
jsregexp.cpp \
jsscan.cpp \
jsscope.cpp \
@ -220,6 +221,7 @@ INSTALLED_HEADERS = \
jsproto.tbl \
jsprvtd.h \
jspubtd.h \
jsreflect.h \
jsregexp.h \
jsscan.h \
jsscope.h \
@ -633,7 +635,7 @@ endif
ifeq ($(OS_ARCH),IRIX)
ifndef GNU_CC
_COMPILE_CFLAGS = $(patsubst -O%,-O1,$(COMPILE_CFLAGS))
jsapi.o jsxdrapi.o jsarena.o jsarray.o jsatom.o jsemit.o jsfun.o jsinterp.o jsregexp.o jsparse.o jsopcode.o jsscript.o: %.o: %.cpp Makefile.in
jsapi.o jsxdrapi.o jsarena.o jsarray.o jsatom.o jsemit.o jsfun.o jsinterp.o jsreflect.o jsregexp.o jsparse.o jsopcode.o jsscript.o: %.o: %.cpp Makefile.in
$(REPORT_BUILD)
@$(MAKE_DEPS_AUTO_CXX)
$(CXX) -o $@ -c $(_COMPILE_CFLAGS) $<

View File

@ -333,3 +333,4 @@ MSG_DEF(JSMSG_INVALID_EVAL_SCOPE_ARG, 250, 0, JSEXN_EVALERR, "invalid eval scope
MSG_DEF(JSMSG_ACCESSOR_WRONG_ARGS, 251, 3, JSEXN_SYNTAXERR, "{0} functions must have {1} argument{2}")
MSG_DEF(JSMSG_THROW_TYPE_ERROR, 252, 0, JSEXN_TYPEERR, "'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them")
MSG_DEF(JSMSG_BAD_TOISOSTRING_PROP, 253, 0, JSEXN_TYPEERR, "toISOString property is not callable")
MSG_DEF(JSMSG_BAD_PARSE_NODE, 254, 0, JSEXN_INTERNALERR, "bad parse node")

115
js/src/jsast.tbl Normal file
View File

@ -0,0 +1,115 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/* AST_ERROR = -1 */
ASTDEF(AST_PROGRAM, "Program")
ASTDEF(AST_IDENTIFIER, "Identifier")
ASTDEF(AST_LITERAL, "Literal")
ASTDEF(AST_PROPERTY, "Property")
ASTDEF(AST_FUNC_DECL, "FunctionDeclaration")
ASTDEF(AST_VAR_DECL, "VariableDeclaration")
ASTDEF(AST_VAR_DTOR, "VariableDeclarator")
ASTDEF(AST_LIST_EXPR, "SequenceExpression")
ASTDEF(AST_COND_EXPR, "ConditionalExpression")
ASTDEF(AST_UNARY_EXPR, "UnaryExpression")
ASTDEF(AST_BINARY_EXPR, "BinaryExpression")
ASTDEF(AST_ASSIGN_EXPR, "AssignmentExpression")
ASTDEF(AST_LOGICAL_EXPR, "LogicalExpression")
ASTDEF(AST_UPDATE_EXPR, "UpdateExpression")
ASTDEF(AST_NEW_EXPR, "NewExpression")
ASTDEF(AST_CALL_EXPR, "CallExpression")
ASTDEF(AST_MEMBER_EXPR, "MemberExpression")
ASTDEF(AST_FUNC_EXPR, "FunctionExpression")
ASTDEF(AST_ARRAY_EXPR, "ArrayExpression")
ASTDEF(AST_OBJECT_EXPR, "ObjectExpression")
ASTDEF(AST_THIS_EXPR, "ThisExpression")
ASTDEF(AST_GRAPH_EXPR, "GraphExpression")
ASTDEF(AST_GRAPH_IDX_EXPR, "GraphIndexExpression")
ASTDEF(AST_COMP_EXPR, "ComprehensionExpression")
ASTDEF(AST_GENERATOR_EXPR, "GeneratorExpression")
ASTDEF(AST_YIELD_EXPR, "YieldExpression")
ASTDEF(AST_EMPTY_STMT, "EmptyStatement")
ASTDEF(AST_BLOCK_STMT, "BlockStatement")
ASTDEF(AST_EXPR_STMT, "ExpressionStatement")
ASTDEF(AST_LAB_STMT, "LabeledStatement")
ASTDEF(AST_IF_STMT, "IfStatement")
ASTDEF(AST_SWITCH_STMT, "SwitchStatement")
ASTDEF(AST_WHILE_STMT, "WhileStatement")
ASTDEF(AST_DO_STMT, "DoWhileStatement")
ASTDEF(AST_FOR_STMT, "ForStatement")
ASTDEF(AST_FOR_IN_STMT, "ForInStatement")
ASTDEF(AST_BREAK_STMT, "BreakStatement")
ASTDEF(AST_CONTINUE_STMT, "ContinueStatement")
ASTDEF(AST_WITH_STMT, "WithStatement")
ASTDEF(AST_RETURN_STMT, "ReturnStatement")
ASTDEF(AST_TRY_STMT, "TryStatement")
ASTDEF(AST_THROW_STMT, "ThrowStatement")
ASTDEF(AST_DEBUGGER_STMT, "DebuggerStatement")
ASTDEF(AST_CASE, "SwitchCase")
ASTDEF(AST_CATCH, "CatchClause")
ASTDEF(AST_COMP_BLOCK, "ComprehensionBlock")
ASTDEF(AST_ARRAY_PATT, "ArrayPattern")
ASTDEF(AST_OBJECT_PATT, "ObjectPattern")
ASTDEF(AST_XMLANYNAME, "XMLAnyName")
ASTDEF(AST_XMLATTR_SEL, "XMLAttributeSelector")
ASTDEF(AST_XMLESCAPE, "XMLEscape")
ASTDEF(AST_XMLFILTER, "XMLFilterExpression")
ASTDEF(AST_XMLDEFAULT, "XMLDefaultDeclaration")
ASTDEF(AST_XMLQUAL, "XMLQualifiedIdentifier")
ASTDEF(AST_XMLELEM, "XMLElement")
ASTDEF(AST_XMLTEXT, "XMLText")
ASTDEF(AST_XMLLIST, "XMLList")
ASTDEF(AST_XMLSTART, "XMLStartTag")
ASTDEF(AST_XMLEND, "XMLEndTag")
ASTDEF(AST_XMLPOINT, "XMLPointTag")
ASTDEF(AST_XMLNAME, "XMLName")
ASTDEF(AST_XMLATTR, "XMLAttribute")
ASTDEF(AST_XMLCDATA, "XMLCdata")
ASTDEF(AST_XMLCOMMENT, "XMLComment")
ASTDEF(AST_XMLPI, "XMLProcessingInstruction")
/* AST_LIMIT = last + 1 */

View File

@ -60,7 +60,7 @@
#define STRING_TO_ATOM(str) (JS_ASSERT(str->isAtomized()), \
(JSAtom *)str)
#define ATOM_TO_STRING(atom) ((JSString *)atom)
#define ATOM_TO_STRING(atom) ((JSString *)(atom))
#define ATOM_TO_JSVAL(atom) STRING_TO_JSVAL(ATOM_TO_STRING(atom))
/* Engine-internal extensions of jsid */

View File

@ -2886,10 +2886,9 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
#if JS_HAS_DESTRUCTURING
/*
* If there were destructuring formal parameters, prepend the initializing
* comma expression that we synthesized to body. If the body is a lexical
* scope node, we must make a special TOK_SEQ node, to prepend the formal
* parameter destructuring code without bracing the decompilation of the
* function body's lexical scope.
* comma expression that we synthesized to body. If the body is a return
* node, we must make a special TOK_SEQ node, to prepend the destructuring
* code without bracing the decompilation of the function body.
*/
if (prolog) {
if (body->pn_arity != PN_LIST) {
@ -7881,10 +7880,9 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot)
* ARRAYPUSH node after we parse the rest of the comprehension.
*/
pnexp = pn->last();
JS_ASSERT(pn->pn_count == 1 || pn->pn_count == 2);
pn->pn_tail = (--pn->pn_count == 1)
? &pn->pn_head->pn_next
: &pn->pn_head;
JS_ASSERT(pn->pn_count == 1);
pn->pn_count = 0;
pn->pn_tail = &pn->pn_head;
*pn->pn_tail = NULL;
pntop = comprehensionTail(pnexp, pn->pn_blockid,

View File

@ -69,7 +69,10 @@ JS_BEGIN_EXTERN_C
* pn_body: TOK_UPVARS if the function's source body
* depends on outer names, else TOK_ARGSBODY
* if formal parameters, else TOK_LC node for
* function body statements
* function body statements, else TOK_RETURN
* for expression closure, else TOK_SEQ for
* expression closure with destructured
* formal parameters
* pn_cookie: static level and var index for function
* pn_dflags: PND_* definition/use flags (see below)
* pn_blockid: block id number
@ -84,7 +87,10 @@ JS_BEGIN_EXTERN_C
*
* <Statements>
* TOK_LC list pn_head: list of pn_count statements
* TOK_IF ternary pn_kid1: cond, pn_kid2: then, pn_kid3: else or null
* TOK_IF ternary pn_kid1: cond, pn_kid2: then, pn_kid3: else or null.
* In body of a comprehension or desugared generator
* expression, pn_kid2 is TOK_YIELD, TOK_ARRAYPUSH,
* or (if the push was optimized away) empty TOK_LC.
* TOK_SWITCH binary pn_left: discriminant
* pn_right: list of TOK_CASE nodes, with at most one
* TOK_DEFAULT node, or if there are let bindings
@ -128,7 +134,7 @@ JS_BEGIN_EXTERN_C
* pn_expr: initializer or null
* each assignment node has
* pn_left: TOK_NAME with pn_used true and
* pn_lexdef (NOT pn_expr) set
* pn_lexdef (NOT pn_expr) set
* pn_right: initializer
* TOK_RETURN unary pn_kid: return expr or null
* TOK_SEMI unary pn_kid: expr or null statement
@ -207,6 +213,9 @@ JS_BEGIN_EXTERN_C
* TOK_PRIMARY nullary pn_op: JSOp bytecode
*
* <E4X node descriptions>
* TOK_DEFAULT name pn_atom: default XML namespace string literal
* TOK_FILTER binary pn_left: container expr, pn_right: filter expr
* TOK_DBLDOT binary pn_left: container expr, pn_right: selector expr
* TOK_ANYNAME nullary pn_op: JSOP_ANYNAME
* pn_atom: cx->runtime->atomState.starAtom
* TOK_AT unary pn_op: JSOP_TOATTRNAME; pn_kid attribute id/expr
@ -220,11 +229,13 @@ JS_BEGIN_EXTERN_C
* pn_head: start tag, content1, ... contentN, end tag
* pn_count: 2 + N where N is number of content nodes
* N may be > x.length() if {expr} embedded
* After constant folding, these contents may be
* concatenated into string nodes.
* TOK_XMLLIST list XML list node
* pn_head: content1, ... contentN
* TOK_XMLSTAGO, list XML start, end, and point tag contents
* TOK_XMLETAGC, pn_head: tag name or {expr}, ... XML attrs ...
* TOK_XMLPTAGO
* TOK_XMLETAGO, pn_head: tag name or {expr}, ... XML attrs ...
* TOK_XMLPTAGC
* TOK_XMLNAME nullary pn_atom: XML name, with no {expr} embedded
* TOK_XMLNAME list pn_head: tag name or {expr}, ... name or {expr}
* TOK_XMLATTR, nullary pn_atom: attribute value string; pn_op: JSOP_STRING
@ -267,10 +278,10 @@ JS_BEGIN_EXTERN_C
* TOK_LEXICALSCOPE name pn_op: JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR
* pn_objbox: block object in JSObjectBox holder
* pn_expr: block body
* TOK_ARRAYCOMP list pn_head: list of pn_count (1 or 2) elements
* if pn_count is 2, first element is #n=[...]
* last element is block enclosing for loop(s)
* and optionally if-guarded TOK_ARRAYPUSH
* TOK_ARRAYCOMP list pn_count: 1
* pn_head: list of 1 element, which is block
* enclosing for loop(s) and optionally
* if-guarded TOK_ARRAYPUSH
* TOK_ARRAYPUSH unary pn_op: JSOP_ARRAYCOMP
* pn_kid: array comprehension expression
*/
@ -532,6 +543,35 @@ public:
pn_pos.begin.index + str->length() + 2 == pn_pos.end.index);
}
#ifdef JS_HAS_GENERATOR_EXPRS
/*
* True if this node is a desugared generator expression.
*/
bool isGeneratorExpr() const {
if (PN_TYPE(this) == js::TOK_LP) {
JSParseNode *callee = this->pn_head;
if (PN_TYPE(callee) == js::TOK_FUNCTION) {
JSParseNode *body = (PN_TYPE(callee->pn_body) == js::TOK_UPVARS)
? callee->pn_body->pn_tree
: callee->pn_body;
if (PN_TYPE(body) == js::TOK_LEXICALSCOPE)
return true;
}
}
return false;
}
JSParseNode *generatorExpr() const {
JS_ASSERT(isGeneratorExpr());
JSParseNode *callee = this->pn_head;
JSParseNode *body = PN_TYPE(callee->pn_body) == js::TOK_UPVARS
? callee->pn_body->pn_tree
: callee->pn_body;
JS_ASSERT(PN_TYPE(body) == js::TOK_LEXICALSCOPE);
return body->pn_expr;
}
#endif
/*
* Compute a pointer to the last element in a singly-linked list. NB: list
* must be non-empty for correct PN_LAST usage -- this is asserted!
@ -959,6 +999,11 @@ struct Parser : private js::AutoGCRooter
void setPrincipals(JSPrincipals *prin);
const char *getFilename()
{
return tokenStream.getFilename();
}
/*
* Parse a top-level JS script.
*/

View File

@ -103,6 +103,8 @@ JS_PROTO(Float32Array, 34, js_InitTypedArrayClasses)
JS_PROTO(Float64Array, 35, js_InitTypedArrayClasses)
JS_PROTO(Uint8ClampedArray, 36, js_InitTypedArrayClasses)
JS_PROTO(Proxy, 37, js_InitProxyClass)
JS_PROTO(Reflect, 38, js_InitReflectClass)
JS_PROTO(ASTNode, 39, js_InitReflectClass)
#undef XML_INIT
#undef NAMESPACE_INIT

2691
js/src/jsreflect.cpp Normal file

File diff suppressed because it is too large Load Diff

138
js/src/jsreflect.h Normal file
View File

@ -0,0 +1,138 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=99 ft=cpp:
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
* June 12, 2009.
*
* The Initial Developer of the Original Code is
* the Mozilla Corporation.
*
* Contributor(s):
* Dave Herman <dherman@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*
* JS reflection package.
*/
#ifndef jsreflect_h___
#define jsreflect_h___
#include <stdlib.h>
#include "jspubtd.h"
namespace js {
enum ASTType {
AST_ERROR = -1,
#define ASTDEF(ast, str) ast,
#include "jsast.tbl"
#undef ASTDEF
AST_LIMIT
};
enum AssignmentOperator {
AOP_ERR = -1,
/* assign */
AOP_ASSIGN = 0,
/* operator-assign */
AOP_PLUS, AOP_MINUS, AOP_STAR, AOP_DIV, AOP_MOD,
/* shift-assign */
AOP_LSH, AOP_RSH, AOP_URSH,
/* binary */
AOP_BITOR, AOP_BITXOR, AOP_BITAND,
AOP_LIMIT
};
enum BinaryOperator {
BINOP_ERR = -1,
/* eq */
BINOP_EQ = 0, BINOP_NE, BINOP_STRICTEQ, BINOP_STRICTNE,
/* rel */
BINOP_LT, BINOP_LE, BINOP_GT, BINOP_GE,
/* shift */
BINOP_LSH, BINOP_RSH, BINOP_URSH,
/* arithmetic */
BINOP_PLUS, BINOP_MINUS, BINOP_STAR, BINOP_DIV, BINOP_MOD,
/* binary */
BINOP_BITOR, BINOP_BITXOR, BINOP_BITAND,
/* misc */
BINOP_IN, BINOP_INSTANCEOF,
/* xml */
BINOP_DBLDOT,
BINOP_LIMIT
};
enum UnaryOperator {
UNOP_ERR = -1,
UNOP_DELETE = 0,
UNOP_NEG,
UNOP_POS,
UNOP_NOT,
UNOP_BITNOT,
UNOP_TYPEOF,
UNOP_VOID,
UNOP_LIMIT
};
enum VarDeclKind {
VARDECL_ERR = -1,
VARDECL_VAR = 0,
VARDECL_CONST,
VARDECL_LET,
VARDECL_LIMIT
};
enum PropKind {
PROP_ERR = -1,
PROP_INIT = 0,
PROP_GETTER,
PROP_SETTER,
PROP_LIMIT
};
extern char const *aopNames[];
extern char const *binopNames[];
extern char const *unopNames[];
extern char const *nodeTypeNames[];
} /* namespace js */
extern js::Class js_ReflectClass;
extern JSObject *
js_InitReflectClass(JSContext *cx, JSObject *obj);
#endif /* jsreflect_h___ */

View File

@ -261,6 +261,7 @@ typedef enum JSWhyMagic
JS_FAST_CONSTRUCTOR, /* 'this' value for fast natives invoked with 'new' */
JS_NO_CONSTANT, /* compiler sentinel value */
JS_THIS_POISON, /* used in debug builds to catch tracing errors */
JS_SERIALIZE_NO_NODE, /* an empty subnode in the AST serializer */
JS_GENERIC_MAGIC /* for local use */
} JSWhyMagic;

View File

@ -71,6 +71,7 @@
#include "jsnum.h"
#include "jsobj.h"
#include "jsparse.h"
#include "jsreflect.h"
#include "jsscope.h"
#include "jsscript.h"
#include "jstracer.h"
@ -2706,6 +2707,23 @@ split_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
return JS_TRUE;
}
static JSBool
ResolveClass(JSContext *cx, JSObject *obj, jsid id, JSBool *resolved)
{
if (!JS_ResolveStandardClass(cx, obj, id, resolved))
return JS_FALSE;
if (!*resolved) {
if (JSID_IS_ATOM(id, CLASS_ATOM(cx, Reflect))) {
if (!js_InitReflectClass(cx, obj))
return JS_FALSE;
*resolved = JS_TRUE;
}
}
return JS_TRUE;
}
static JSBool
split_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp)
{
@ -2736,7 +2754,7 @@ split_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **obj
if (!(flags & JSRESOLVE_ASSIGNING)) {
JSBool resolved;
if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
if (!ResolveClass(cx, obj, id, &resolved))
return JS_FALSE;
if (resolved) {
@ -2962,7 +2980,7 @@ sandbox_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
JS_ValueToBoolean(cx, v, &b);
if (b && (flags & JSRESOLVE_ASSIGNING) == 0) {
if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
if (!ResolveClass(cx, obj, id, &resolved))
return JS_FALSE;
if (resolved) {
*objp = obj;
@ -4711,7 +4729,7 @@ global_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
#ifdef LAZY_STANDARD_CLASSES
JSBool resolved;
if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
if (!ResolveClass(cx, obj, id, &resolved))
return JS_FALSE;
if (resolved) {
*objp = obj;

View File

@ -12,4 +12,5 @@ script scripted-proxies.js
script array-length-protochange.js
script parseInt-octal.js
script proxy-enumerateOwn-duplicates.js
skip-if(!xulRuntime.shell) script reflect-parse.js
script destructure-accessor.js

View File

@ -0,0 +1,703 @@
/* -*- 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 { Pattern, MatchError } = Match;
var _ = Pattern.ANY;
function program(elts) Pattern({ type: "Program", body: elts })
function exprStmt(expr) Pattern({ type: "ExpressionStatement", expression: expr })
function throwStmt(expr) Pattern({ type: "ThrowStatement", argument: expr })
function returnStmt(expr) Pattern({ type: "ReturnStatement", argument: expr })
function yieldExpr(expr) Pattern({ type: "YieldExpression", argument: expr })
function lit(val) Pattern({ type: "Literal", value: val })
var thisExpr = Pattern({ type: "ThisExpression" });
function funDecl(id, params, body) Pattern({ type: "FunctionDeclaration",
id: id,
params: params,
body: body,
generator: false })
function genFunDecl(id, params, body) Pattern({ type: "FunctionDeclaration",
id: id,
params: params,
body: body,
generator: true })
function varDecl(decls) Pattern({ type: "VariableDeclaration", declarations: decls, kind: "var" })
function letDecl(decls) Pattern({ type: "VariableDeclaration", declarations: decls, kind: "let" })
function constDecl(decls) Pattern({ type: "VariableDeclaration", declarations: decls, kind: "const" })
function blockStmt(body) Pattern({ type: "BlockStatement", body: body })
function ident(name) Pattern({ type: "Identifier", name: name })
function dotExpr(obj, id) Pattern({ type: "MemberExpression", computed: false, object: obj, property: id })
function memExpr(obj, id) Pattern({ type: "MemberExpression", computed: true, object: obj, property: id })
function forStmt(init, test, update, body) Pattern({ type: "ForStatement", init: init, test: test, update: update, body: body })
function forInStmt(lhs, rhs, body) Pattern({ type: "ForInStatement", left: lhs, right: rhs, body: body, each: false })
function forEachInStmt(lhs, rhs, body) Pattern({ type: "ForInStatement", left: lhs, right: rhs, body: body, each: true })
function breakStmt(lab) Pattern({ type: "BreakStatement", label: lab })
function continueStmt(lab) Pattern({ type: "ContinueStatement", label: lab })
function blockStmt(stmts) Pattern({ type: "BlockStatement", body: stmts })
var emptyStmt = Pattern({ type: "EmptyStatement" })
function ifStmt(test, cons, alt) Pattern({ type: "IfStatement", test: test, alternate: alt, consequent: cons })
function labStmt(lab, stmt) Pattern({ type: "LabeledStatement", label: lab, body: stmt })
function withStmt(obj, stmt) Pattern({ type: "WithStatement", object: obj, body: stmt })
function whileStmt(test, stmt) Pattern({ type: "WhileStatement", test: test, body: stmt })
function doStmt(stmt, test) Pattern({ type: "DoWhileStatement", test: test, body: stmt })
function switchStmt(disc, cases) Pattern({ type: "SwitchStatement", discriminant: disc, cases: cases })
function caseClause(test, stmts) Pattern({ type: "SwitchCase", test: test, consequent: stmts })
function defaultClause(stmts) Pattern({ type: "SwitchCase", test: null, consequent: stmts })
function catchClause(id, guard, body) Pattern({ type: "CatchClause", param: id, guard: guard, body: body })
function tryStmt(body, catches, fin) Pattern({ type: "TryStatement", block: body, handler: catches, finalizer: fin })
function funExpr(id, args, body, gen) Pattern({ type: "FunctionExpression",
id: id,
params: args,
body: body,
generator: false })
function genFunExpr(id, args, body) Pattern({ type: "FunctionExpression",
id: id,
params: args,
body: body,
generator: true })
function unExpr(op, arg) Pattern({ type: "UnaryExpression", operator: op, argument: arg })
function binExpr(op, left, right) Pattern({ type: "BinaryExpression", operator: op, left: left, right: right })
function aExpr(op, left, right) Pattern({ type: "AssignmentExpression", operator: op, left: left, right: right })
function updExpr(op, arg, prefix) Pattern({ type: "UpdateExpression", operator: op, argument: arg, prefix: prefix })
function logExpr(op, left, right) Pattern({ type: "LogicalExpression", operator: op, left: left, right: right })
function condExpr(test, cons, alt) Pattern({ type: "ConditionalExpression", test: test, consequent: cons, alternate: alt })
function seqExpr(exprs) Pattern({ type: "SequenceExpression", expressions: exprs })
function newExpr(callee, args) Pattern({ type: "NewExpression", callee: callee, arguments: args })
function callExpr(callee, args) Pattern({ type: "CallExpression", callee: callee, arguments: args })
function arrExpr(elts) Pattern({ type: "ArrayExpression", elements: elts })
function objExpr(elts) Pattern({ type: "ObjectExpression", properties: elts })
function compExpr(body, blocks, filter) Pattern({ type: "ComprehensionExpression", body: body, blocks: blocks, filter: filter })
function genExpr(body, blocks, filter) Pattern({ type: "GeneratorExpression", body: body, blocks: blocks, filter: filter })
function graphExpr(idx, body) Pattern({ type: "GraphExpression", index: idx, expression: body })
function idxExpr(idx) Pattern({ type: "GraphIndexExpression", index: idx })
function compBlock(left, right) Pattern({ type: "ComprehensionBlock", left: left, right: right, each: false })
function compEachBlock(left, right) Pattern({ type: "ComprehensionBlock", left: left, right: right, each: true })
function arrPatt(elts) Pattern({ type: "ArrayPattern", elements: elts })
function objPatt(elts) Pattern({ type: "ObjectPattern", properties: elts })
function localSrc(src) "(function(){ " + src + " })"
function localPatt(patt) program([exprStmt(funExpr(null, [], blockStmt([patt])))])
function blockSrc(src) "(function(){ { " + src + " } })"
function blockPatt(patt) program([exprStmt(funExpr(null, [], blockStmt([blockStmt([patt])])))])
var xmlAnyName = Pattern({ type: "XMLAnyName" });
function xmlQualId(left, right, computed) Pattern({ type: "XMLQualifiedIdentifier", left: left, right: right, computed: computed })
function xmlAttrSel(id) Pattern({ type: "XMLAttributeSelector", attribute: id })
function xmlFilter(left, right) Pattern({ type: "XMLFilterExpression", left: left, right: right })
function xmlPointTag(contents) Pattern({ type: "XMLPointTag", contents: contents })
function xmlStartTag(contents) Pattern({ type: "XMLStartTag", contents: contents })
function xmlEndTag(contents) Pattern({ type: "XMLEndTag", contents: contents })
function xmlEscape(expr) Pattern({ type: "XMLEscape", expression: expr })
function xmlElt(contents) Pattern({ type: "XMLElement", contents: contents })
function xmlAttr(value) Pattern({ type: "XMLAttribute", value: value })
function xmlText(text) Pattern({ type: "XMLText", text: text })
function xmlPI(target, contents) Pattern({ type: "XMLProcessingInstruction", target: target, contents: contents })
function assertBlockStmt(src, patt) {
blockPatt(patt).assert(Reflect.parse(blockSrc(src)));
}
function assertBlockExpr(src, patt) {
assertBlockStmt(src, exprStmt(patt));
}
function assertBlockDecl(src, patt) {
blockPatt(patt).assert(Reflect.parse(blockSrc(src)));
}
function assertLocalStmt(src, patt) {
localPatt(patt).assert(Reflect.parse(localSrc(src)));
}
function assertLocalExpr(src, patt) {
assertLocalStmt(src, exprStmt(patt));
}
function assertLocalDecl(src, patt) {
localPatt(patt).assert(Reflect.parse(localSrc(src)));
}
function assertGlobalStmt(src, patt) {
program([patt]).assert(Reflect.parse(src));
}
function assertGlobalExpr(src, patt) {
assertStmt(src, exprStmt(patt));
}
function assertGlobalDecl(src, patt) {
program([patt]).assert(Reflect.parse(src));
}
function assertStmt(src, patt) {
assertLocalStmt(src, patt);
assertGlobalStmt(src, patt);
assertBlockStmt(src, patt);
}
function assertExpr(src, patt) {
assertLocalExpr(src, patt);
assertGlobalExpr(src, patt);
assertBlockExpr(src, patt);
}
function assertDecl(src, patt) {
assertLocalDecl(src, patt);
assertGlobalDecl(src, patt);
assertBlockDecl(src, patt);
}
// general tests
// NB: These are useful but for now trace-test doesn't do I/O reliably.
//program(_).assert(Reflect.parse(snarf('data/flapjax.txt')));
//program(_).assert(Reflect.parse(snarf('data/jquery-1.4.2.txt')));
//program(_).assert(Reflect.parse(snarf('data/prototype.js')));
//program(_).assert(Reflect.parse(snarf('data/dojo.js.uncompressed.js')));
//program(_).assert(Reflect.parse(snarf('data/mootools-1.2.4-core-nc.js')));
// declarations
assertDecl("var x = 1, y = 2, z = 3",
varDecl([{ id: ident("x"), init: lit(1) },
{ id: ident("y"), init: lit(2) },
{ id: ident("z"), init: lit(3) }]));
assertDecl("var x, y, z",
varDecl([{ id: ident("x"), init: null },
{ id: ident("y"), init: null },
{ id: ident("z"), init: null }]));
assertDecl("function foo() { }",
funDecl(ident("foo"), [], blockStmt([])));
assertDecl("function foo() { return 42 }",
funDecl(ident("foo"), [], blockStmt([returnStmt(lit(42))])));
// expressions
assertExpr("true", lit(true));
assertExpr("false", lit(false));
assertExpr("42", lit(42));
assertExpr("(/asdf/)", lit(/asdf/));
assertExpr("this", thisExpr);
assertExpr("foo", ident("foo"));
assertExpr("foo.bar", dotExpr(ident("foo"), ident("bar")));
assertExpr("foo[bar]", memExpr(ident("foo"), ident("bar")));
assertExpr("(function(){})", funExpr(null, [], blockStmt([])));
assertExpr("(function f() {})", funExpr(ident("f"), [], blockStmt([])));
assertExpr("(function f(x,y,z) {})", funExpr(ident("f"), [ident("x"),ident("y"),ident("z")], blockStmt([])));
assertExpr("(++x)", updExpr("++", ident("x"), true));
assertExpr("(x++)", updExpr("++", ident("x"), false));
assertExpr("(+x)", unExpr("+", ident("x")));
assertExpr("(-x)", unExpr("-", ident("x")));
assertExpr("(!x)", unExpr("!", ident("x")));
assertExpr("(~x)", unExpr("~", ident("x")));
assertExpr("(delete x)", unExpr("delete", ident("x")));
assertExpr("(typeof x)", unExpr("typeof", ident("x")));
assertExpr("(void x)", unExpr("void", ident("x")));
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 > 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("+", ident("w"), binExpr("+", ident("x", binExpr("+", 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 & 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("||", ident("w"), logExpr("||", ident("x", logExpr("||", 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),null,lit(2),lit(3)]));
assertExpr("[1,,,2,3]", arrExpr([lit(1),null,null,lit(2),lit(3)]));
assertExpr("[1,,,2,,3]", arrExpr([lit(1),null,null,lit(2),null,lit(3)]));
assertExpr("[1,,,2,,,3]", arrExpr([lit(1),null,null,lit(2),null,null,lit(3)]));
assertExpr("[,1,2,3]", arrExpr([null,lit(1),lit(2),lit(3)]));
assertExpr("[,,1,2,3]", arrExpr([null,null,lit(1),lit(2),lit(3)]));
assertExpr("[,,,1,2,3]", arrExpr([null,null,null,lit(1),lit(2),lit(3)]));
assertExpr("[,,,1,2,3,]", arrExpr([null,null,null,lit(1),lit(2),lit(3)]));
assertExpr("[,,,1,2,3,,]", arrExpr([null,null,null,lit(1),lit(2),lit(3),null]));
assertExpr("[,,,1,2,3,,,]", arrExpr([null,null,null,lit(1),lit(2),lit(3),null,null]));
assertExpr("[,,,,,]", arrExpr([null,null,null,null,null]));
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([])));
assertDecl("var {x:y} = foo;", varDecl([{ id: objPatt([{ key: ident("x"), value: ident("y") }]),
init: ident("foo") }]));
// 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));
// 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)))])));
// 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" } ]));
// 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) }])))
// E4X
assertExpr("x..tagName", binExpr("..", ident("x"), lit("tagName")));
assertExpr("x.*", memExpr(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("@foo", xmlAttrSel(ident("foo")));
assertExpr("x.(p)", xmlFilter(ident("x"), ident("p")));
assertExpr("<{foo}/>", xmlPointTag([xmlEscape(ident("foo"))]));
assertExpr("<{foo}></{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</{foo}>", xmlElt([xmlStartTag([xmlEscape(ident("foo"))]),
xmlText("text"),
xmlEndTag([xmlEscape(ident("foo"))])]));
assertExpr("<?xml?>", xmlPI("xml", ""));
assertExpr("<?xml version='1.0'?>", xmlPI("xml", "version='1.0'"));
// NOTE: We appear to be unable to test XMLNAME, XMLCDATA, and XMLCOMMENT.
// Source location information
var withoutFileOrLine = Reflect.parse("42");
var withFile = Reflect.parse("42", "foo.js");
var withFileAndLine = Reflect.parse("42", "foo.js", 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", "foo.js");
var withFileAndLine2 = Reflect.parse("foo +\nbar", "foo.js", 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)", "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);
reportCompare(true, true);

View File

@ -17,3 +17,156 @@ if (typeof version != 'undefined')
version(185);
}
// A little pattern-matching library.
var Match =
(function() {
function Pattern(template) {
// act like a constructor even as a function
if (!(this instanceof Pattern))
return new Pattern(template);
this.template = template;
}
Pattern.prototype = {
match: function(act) {
return match(act, this.template);
},
matches: function(act) {
try {
return this.match(act);
}
catch (e if e instanceof MatchError) {
return false;
}
},
assert: function(act, message) {
try {
return this.match(act);
}
catch (e if e instanceof MatchError) {
throw new Error((message || "failed match") + ": " + e.message);
}
},
toString: function() "[object Pattern]"
};
Pattern.ANY = new Pattern;
Pattern.ANY.template = Pattern.ANY;
var quote = uneval;
function MatchError(msg) {
this.message = msg;
}
MatchError.prototype = {
toString: function() {
return "match error: " + this.message;
}
};
function isAtom(x) {
return (typeof x === "number") ||
(typeof x === "string") ||
(typeof x === "boolean") ||
(x === null) ||
(typeof x === "object" && x instanceof RegExp);
}
function isObject(x) {
return (x !== null) && (typeof x === "object");
}
function isArrayLike(x) {
return isObject(x) && ("length" in x);
}
function matchAtom(act, exp) {
if ((typeof exp) === "number" && isNaN(exp)) {
if ((typeof act) !== "number" || !isNaN(act))
throw new MatchError("expected NaN, got: " + quote(act));
return true;
}
if (exp === null) {
if (act !== null)
throw new MatchError("expected null, got: " + quote(act));
return true;
}
if (exp instanceof RegExp) {
if (!(act instanceof RegExp) || exp.source !== act.source)
throw new MatchError("expected " + quote(exp) + ", got: " + quote(act));
return true;
}
switch (typeof exp) {
case "string":
if (act !== exp)
throw new MatchError("expected " + exp.quote() + ", got " + quote(act));
return true;
case "boolean":
case "number":
if (exp !== act)
throw new MatchError("expected " + exp + ", got " + quote(act));
return true;
}
throw new Error("bad pattern: " + exp.toSource());
}
function matchObject(act, exp) {
if (!isObject(act))
throw new MatchError("expected object, got " + quote(act));
for (var key in exp) {
if (!(key in act))
throw new MatchError("expected property " + key.quote() + " not found in " + quote(act));
match(act[key], exp[key]);
}
return true;
}
function matchArray(act, exp) {
if (!isObject(act) || !("length" in act))
throw new MatchError("expected array-like object, got " + quote(act));
var length = exp.length;
for (var i = 0; i < length; i++) {
if (i in exp) {
if (!(i in act))
throw new MatchError("expected array property " + i + " not found in " + quote(act));
match(act[i], exp[i]);
}
}
return true;
}
function match(act, exp) {
if (exp === Pattern.ANY)
return true;
if (exp instanceof Pattern)
return exp.match(act);
if (isAtom(exp))
return matchAtom(act, exp);
if (isArrayLike(exp))
return matchArray(act, exp);
return matchObject(act, exp);
}
return { Pattern: Pattern,
MatchError: MatchError };
})();