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

This commit is contained in:
Dave Herman 2010-06-15 13:32:32 -07:00
Родитель 417af815e0
Коммит 4c29abba51
14 изменённых файлов: 10790 добавлений и 4 удалений

Просмотреть файл

@ -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 \
@ -548,7 +550,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) $<

Просмотреть файл

@ -330,3 +330,5 @@ MSG_DEF(JSMSG_CSP_BLOCKED_FUNCTION, 247, 0, JSEXN_ERR, "call to Function() blo
MSG_DEF(JSMSG_BAD_GET_SET_FIELD, 248, 1, JSEXN_TYPEERR, "property descriptor's {0} field is neither undefined nor a function")
MSG_DEF(JSMSG_BAD_PROXY_FIX, 249, 0, JSEXN_TYPEERR, "proxy was fixed while executing the handler")
MSG_DEF(JSMSG_INVALID_EVAL_SCOPE_ARG, 250, 0, JSEXN_EVALERR, "invalid eval scope argument")
MSG_DEF(JSMSG_BAD_PARSE_NODE, 251, 2, JSEXN_INTERNALERR, "unimplemented parse node (type {0}, at {1})")
MSG_DEF(JSMSG_BAD_INTERNAL_ARG, 252, 0, JSEXN_INTERNALERR, "unexpected argument")

Просмотреть файл

@ -1498,6 +1498,10 @@ JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj)
CHECK_REQUEST(cx);
rt = cx->runtime;
#ifdef DEBUG_dherman
fprintf(stderr, "JS_EnumerateStandardClasses!\n");
#endif
/* Check whether we need to bind 'undefined' and define it if so. */
atom = rt->atomState.typeAtoms[JSTYPE_VOID];
if (!AlreadyHasOwnProperty(cx, obj, atom) &&

123
js/src/jsast.tbl Normal file
Просмотреть файл

@ -0,0 +1,123 @@
/* -*- 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 ***** */
/*
* These must be listed in exactly this order so the AST_xxx fields match up
* with their integer definition in jsreflect.cpp.
*/
/* AST_ERROR = -1 */
ASTDEF(AST_PROGRAM, 0, "Program")
ASTDEF(AST_IDENTIFIER, 1, "Identifier")
ASTDEF(AST_UNOP, 2, "UnaryOperator")
ASTDEF(AST_BINOP, 3, "BinaryOperator")
ASTDEF(AST_LOGOP, 4, "LogicalOperator")
ASTDEF(AST_AOP, 5, "AssignmentOperator")
ASTDEF(AST_UPDOP, 6, "UpdateOperator")
ASTDEF(AST_LITERAL, 7, "Literal")
ASTDEF(AST_FUNC_DECL, 8, "FunctionDeclaration")
ASTDEF(AST_VAR_DECL, 9, "VariableDeclaration")
ASTDEF(AST_LIST_EXPR, 10, "SequenceExpression")
ASTDEF(AST_COND_EXPR, 11, "ConditionalExpression")
ASTDEF(AST_UNARY_EXPR, 12, "UnaryExpression")
ASTDEF(AST_BINARY_EXPR, 13, "BinaryExpression")
ASTDEF(AST_ASSIGN_EXPR, 14, "AssignmentExpression")
ASTDEF(AST_LOGICAL_EXPR, 15, "LogicalExpression")
ASTDEF(AST_UPDATE_EXPR, 16, "UpdateExpression")
ASTDEF(AST_NEW_EXPR, 17, "NewExpression")
ASTDEF(AST_CALL_EXPR, 18, "CallExpression")
ASTDEF(AST_MEMBER_EXPR, 19, "MemberExpression")
ASTDEF(AST_FUNC_EXPR, 20, "FunctionExpression")
ASTDEF(AST_ARRAY_EXPR, 21, "ArrayExpression")
ASTDEF(AST_OBJECT_EXPR, 22, "ObjectExpression")
ASTDEF(AST_THIS_EXPR, 23, "ThisExpression")
ASTDEF(AST_GRAPH_EXPR, 24, "GraphExpression")
ASTDEF(AST_GRAPH_IDX_EXPR, 25, "GraphIndexExpression")
ASTDEF(AST_COMP_EXPR, 26, "ComprehensionExpression")
ASTDEF(AST_GENERATOR_EXPR, 27, "GeneratorExpression")
ASTDEF(AST_YIELD_EXPR, 28, "YieldExpression")
ASTDEF(AST_EMPTY_STMT, 29, "EmptyStatement")
ASTDEF(AST_BLOCK_STMT, 30, "BlockStatement")
ASTDEF(AST_EXPR_STMT, 31, "ExpressionStatement")
ASTDEF(AST_LAB_STMT, 32, "LabeledStatement")
ASTDEF(AST_IF_STMT, 33, "IfStatement")
ASTDEF(AST_SWITCH_STMT, 34, "SwitchStatement")
ASTDEF(AST_WHILE_STMT, 35, "WhileStatement")
ASTDEF(AST_DO_STMT, 36, "DoWhileStatement")
ASTDEF(AST_FOR_STMT, 37, "ForStatement")
ASTDEF(AST_FOR_IN_STMT, 38, "ForInStatement")
ASTDEF(AST_BREAK_STMT, 39, "BreakStatement")
ASTDEF(AST_CONTINUE_STMT, 40, "ContinueStatement")
ASTDEF(AST_WITH_STMT, 41, "WithStatement")
ASTDEF(AST_RETURN_STMT, 42, "ReturnStatement")
ASTDEF(AST_TRY_STMT, 43, "TryStatement")
ASTDEF(AST_THROW_STMT, 44, "ThrowStatement")
ASTDEF(AST_DEBUGGER_STMT, 45, "DebuggerStatement")
ASTDEF(AST_CASE, 46, "SwitchCase")
ASTDEF(AST_CATCH, 47, "CatchClause")
ASTDEF(AST_COMP_BLOCK, 48, "ComprehensionBlock")
ASTDEF(AST_ARRAY_PATT, 49, "ArrayPattern")
ASTDEF(AST_OBJECT_PATT, 50, "ObjectPattern")
ASTDEF(AST_XMLANYNAME, 51, "XMLAnyName")
ASTDEF(AST_XMLATTR_SEL, 52, "XMLAttributeSelector")
ASTDEF(AST_XMLESCAPE, 53, "XMLEscape")
ASTDEF(AST_XMLFILTER, 54, "XMLFilterExpression")
ASTDEF(AST_XMLDEFAULT, 55, "XMLDefaultDeclaration")
ASTDEF(AST_XMLQUAL, 56, "XMLQualifiedIdentifier")
ASTDEF(AST_XMLELEM, 57, "XMLElement")
ASTDEF(AST_XMLTEXT, 58, "XMLText")
ASTDEF(AST_XMLLIST, 59, "XMLList")
ASTDEF(AST_XMLSTART, 60, "XMLStartTag")
ASTDEF(AST_XMLEND, 61, "XMLEndTag")
ASTDEF(AST_XMLPOINT, 62, "XMLPointTag")
ASTDEF(AST_XMLNAME, 63, "XMLName")
ASTDEF(AST_XMLATTR, 64, "XMLAttribute")
ASTDEF(AST_XMLCDATA, 65, "XMLCdata")
ASTDEF(AST_XMLCOMMENT, 66, "XMLComment")
ASTDEF(AST_XMLPI, 67, "XMLProcessingInstruction")
/* AST_LIMIT = last + 1 */

Просмотреть файл

@ -532,6 +532,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 +988,11 @@ struct Parser : private js::AutoGCRooter
void setPrincipals(JSPrincipals *prin);
const char *getFilename()
{
return tokenStream.getFilename();
}
/*
* Parse a top-level JS script.
*/

Просмотреть файл

@ -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_InitReflectClasses)
JS_PROTO(ASTNode, 39, js_InitReflectClasses)
#undef XML_INIT
#undef NAMESPACE_INIT

2925
js/src/jsreflect.cpp Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

142
js/src/jsreflect.h Normal file
Просмотреть файл

@ -0,0 +1,142 @@
/* -*- 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 {
typedef enum ASTType {
AST_ERROR = -1,
#define ASTDEF(ast, val, str) ast = val,
#include "jsast.tbl"
#undef ASTDEF
AST_LIMIT
} ASTType;
typedef enum {
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
} AssignmentOperator;
typedef enum {
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
} BinaryOperator;
typedef enum {
UNOP_ERR = -1,
UNOP_DELETE = 0,
UNOP_NEG,
UNOP_POS,
UNOP_NOT,
UNOP_BITNOT,
UNOP_TYPEOF,
UNOP_VOID,
UNOP_LIMIT
} UnaryOperator;
typedef enum {
VARDECL_ERR = -1,
VARDECL_VAR = 0,
VARDECL_CONST,
VARDECL_LET,
VARDECL_LIMIT
} VarDeclKind;
typedef enum {
PROP_ERR = -1,
PROP_INIT = 0,
PROP_GETTER,
PROP_SETTER,
PROP_LIMIT
} PropKind;
extern char const *aopNames[];
extern char const *binopNames[];
extern char const *unopNames[];
extern char const *nodeTypeNames[];
} /* namespace js */
JS_BEGIN_EXTERN_C
extern JSClass js_ReflectClass;
extern JSClass js_ASTNodeClass;
extern JSObject *
js_InitReflectClasses(JSContext *cx, JSObject *obj);
JS_END_EXTERN_C
#endif /* jsreflect_h___ */

Просмотреть файл

@ -72,6 +72,7 @@
#include "jsscope.h"
#include "jsscript.h"
#include "jstracer.h"
#include "jsreflect.h"
#include "jsxml.h"
#include "prmjtime.h"
@ -2657,6 +2658,24 @@ split_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
return JS_TRUE;
}
static JSBool
ResolveClass(JSContext *cx, JSObject *obj, jsval id, JSBool *resolved)
{
if (!JS_ResolveStandardClass(cx, obj, id, resolved))
return JS_FALSE;
if (! *resolved) {
JSAtom *atom = OFFSET_TO_ATOM(cx->runtime, CLASS_ATOM_OFFSET(Reflect));
if (ATOM_KEY(atom) == id) {
if (!js_InitReflectClasses(cx, obj))
return JS_FALSE;
*resolved = JS_TRUE;
}
}
return JS_TRUE;
}
static JSBool
split_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp)
{
@ -2691,7 +2710,7 @@ split_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **ob
if (!(flags & JSRESOLVE_ASSIGNING)) {
JSBool resolved;
if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
if (!ResolveClass(cx, obj, id, &resolved))
return JS_FALSE;
if (resolved) {
@ -2905,7 +2924,7 @@ sandbox_resolve(JSContext *cx, JSObject *obj, jsval 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;
@ -4614,7 +4633,7 @@ global_resolve(JSContext *cx, JSObject *obj, jsval 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;

Просмотреть файл

@ -0,0 +1,22 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
function Elements(arrayLike) {
this.i = 0;
this.stop = arrayLike.length;
this.source = arrayLike;
}
Elements.prototype = {
// FIXME: use Proxy.iterator when it's available
__iterator__: function() this,
next: function() {
if (this.i >= this.stop)
throw StopIteration;
return this.source[this.i++];
}
};
function elements(arrayLike) {
return new Elements(arrayLike);
}

Просмотреть файл

@ -0,0 +1,157 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
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 };
})();

Просмотреть файл

@ -0,0 +1,437 @@
FrJS = function() {
// Used to disambiguate references to 'this' in inner classes.
var mgr = this;
// INNER CLASSES --------------------------------
// Utility map class... because standard JS associative arrays don't work properly.
this.Hash = function() {
this.keys = new Array();
this.values = new Array();
this.contains = function (key) {
for(var i = 0; i < this.keys.length; i++) {
if(this.keys[i] == key) {
return true;
}
}
return false;
};
this.get = function (key) {
for(var i = 0; i < this.keys.length; i++) {
if(this.keys[i] == key) {
return this.values[i];
}
}
return null;
};
this.put = function (key, value) {
this.keys[this.keys.length] = key;
this.values[this.values.length] = value;
return value;
};
this.toString = function () {
var ans = "(";
if(this.keys.length > 0) {
ans += this.keys[0] + ":" + this.values[0];
for(var i = 1; i < this.keys.length - 1; i++) {
ans += "," + this.keys[i] + ":" + this.values[i];
}
}
return ans + ")";
};
};
// Class representing a signal.
this.Signal = function(src, value, timestamp) {
this.src = src;
this.value = value;
this.timestamp = timestamp;
this.toString = function() {
return "(Signal " + this.value + " " + this.timestamp + ")";
};
};
// Node (arrayof node) -> node
//
// Superclass of Event and Behaviour... contains code for receiving and
// propagating signals.
this.Node = function(prev) {
this.next = new Array;
this.type = "Node";
this.addNext = function(n) {
this.next[this.next.length] = n;
};
this.propagate = function(signal) {
for(var i = 0; i < this.next.length; i++) {
this.next[i].onSignal(signal);
}
};
for(var i = 0; i < prev.length; i++) {
prev[i].addNext(this);
}
};
// Event (arrayof node) -> event
//
// Class representing an event.
//
// Events receive incoming signals, process them and (if appropriate)
// immediately send output signals. Values of output signals are based
// directly on values of input signals.
this.Event = function(prev) {
this.superclass = mgr.Node;
this.superclass(prev);
this.type = "Event";
this.onSignal = function(signal) {
this.propagate(new mgr.Signal(this, signal.value, new Date));
};
this.toString = function() {
return "(" + this.type + ")";
};
};
// Event any (arrayof node) -> behaviour<any>
//
// Class representing a behaviour.
//
// Behaviours store persistent values that can be accessed by other
// behaviours. They accept input signals and use them as cues to recalculate
// their values. Stored values are not necessarily related to signal values:
// they may depend on values from other nodes or other bits of JavaScript
// data. Output signals are emitted only if the stored value changes.
this.Behaviour = function(init, prev) {
this.superclass = mgr.Node;
this.superclass(prev);
this.type = "Behaviour";
this.value = init;
this.onSignal = function(signal) {
this.setValue(signal.value);
};
this.setValue = function(newValue) {
if(this.value != newValue) {
this.value = newValue;
this.propagate(new mgr.Signal(this, newValue, new Date));
}
};
this.toString = function() {
return "(" + this.type + " " + this.value + ")";
};
};
// FIELDS ---------------------------------------
this.domEventInputs = new mgr.Hash();
this.domEventOutputs = new mgr.Hash();
// METHODS --------------------------------------
// getDomEventData element string (union mgr.DomEventInputs mgr.DomEventOutputs) -> (arrayof node)
//
// This internal function returns an array of nodes registered as
// DomEventInputs or DomEventOutputs, depending on the value of the
// third parameter.
this.getDomEventData = function(id, type, data) {
var temp = data.get(id);
if(temp == null) {
temp = data.put(id, new mgr.Hash());
}
var ans = temp.get(type);
if(ans == null) {
ans = temp.put(type, new Array());
}
return ans;
};
// getDomEventInputs element string -> (arrayof node)
//
// This internal function returns an array in which nodes registered as
// DomEventInputs for the specified event can be stored and retrieved.
this.getDomEventInputs = function(id, type) {
return this.getDomEventData(id, type, mgr.domEventInputs);
};
// getDomEventOutputs element string -> (arrayof node)
//
// This internal function returns an array in which nodes registered as
// DomEventOutputs for the specified event can be stored and retrieved.
this.getDomEventOutputs = function(id, type) {
return this.getDomEventData(id, type, mgr.domEventOutputs);
};
// doDomEvent element domEvent -> void
//
// This internal function is bound to DOM event handlers such as
// 'onsubmit' when they are attached to FrJS via createDomEventInput().
this.doDomEvent = function(elem, domEvent) {
var id = elem.id;
var type = "on" + domEvent.type;
var inputs = mgr.getDomEventInputs(id, type);
var outputs = mgr.getDomEventOutputs(id, type);
// alert("Manager: Handling {" + id + "} {" + type + "} {" + inputs + "} {" + outputs + "}");
for(var i = 0; i < inputs.length; i++) {
inputs[i].doDomEvent(domEvent);
}
if(outputs.length > 0) {
var node1 = outputs["cancelBubble"];
if(node1) domEvent.cancelBubble = node1.value;
var node2 = outputs["returnValue"];
if(node2) {
domEvent.returnValue = node2.value;
return node2.value;
}
}
};
// createDomEventInput
// element
// (union "onclick" ...)
// [(domEvent -> <a>)]
// -> node<a>
//
// Registers FrJS to handle a particular DOM event. Returns an Event that
// receives an input signal whenever the specified event occurs.
//
// The third argument is a function mapping DOM events to signal values.
// If no argument is supplied, all incoming signals have the boolean value
// 'true'.
this.createDomEventInput = function(elem, type) {
var id = elem.id;
var valueFn = arguments.length == 3
? function(domEvent) { eval("return domEvent." + arguments[2] + ";"); }
: function(domEvent) { return true; };
var node = new mgr.Event(new Array);
node.type = "DomEventInput " + id + " " + type;
node.doDomEvent = function(domEvent) {
// alert(this + ": Handling " + domEvent);
this.propagate(new mgr.Signal(this, valueFn(domEvent), new Date));
};
var inputs = mgr.getDomEventInputs(id, type);
inputs[inputs.length] = node;
// alert("Registering {" + id + "} {" + type + "} {" + inputs + "}");
eval("elem." + type + " = function(domEvent) { return mgr.doDomEvent(elem, domEvent); };");
return node;
};
// createDomEventOutput
// element
// (union "onclick" ...)
// (union "resultValue" "cancelBubble")
// node
// -> void
//
// Specifies that a particular node should be used to pass a value back to
// a DOM event object. When the event occurs and its input signal has been
// processed by the network, the specified field ("returnValue" or
// "cancelBubble") is set to the value of the specified node, allowing the
// event to be cancelled in the relevant way.
this.registerDomEventOutput = function(elem, type, field, node) {
var id = elem.id;
var outputs = mgr.getDomEventOutputs(id, type);
outputs[field] = node;
// alert("Registering {" + id + "} {" + type + "} {" + field + "} {" + node + "} {{" + outputs.field + "}} {" + mgr.domEventOutputs + "}");
eval("elem." + type + " = function(domEvent) { return mgr.doDomEvent(elem, domEvent); };");
};
// createDomProperty element string -> node
//
// Creates a node whose value is echoed a property of an element of the DOM.
// The initial value of the node is set from the DOM and any changes to the
// node's value are copied to the DOM.
this.createDomOutput = function(elem, prop, prev) {
var id = elem.id;
var node = new mgr.Behaviour(eval("elem." + prop), prev);
node.type = "DomOutput " + id + " " + prop;
node.elem = elem;
node.setValue = function(newValue) {
if(this.value != newValue) {
this.value = newValue;
eval("this.elem." + prop + " = newValue;");
this.propagate(new mgr.Signal(this, newValue, new Date));
}
};
return node;
};
// filter (a -> boolean) (arrayof node<a>) -> node<b>
//
// Creates an event that filters input signals based on a predicate function.
// The function (first parameter) must take a Signal as its single parameter.
this.createFilter = function(filterFn, prev) {
var node = new mgr.Event(prev);
node.type = "Filter " + filterFn;
node.onSignal = function(signal) {
if(filterFn(signal.value)) {
this.value = signal.value;
this.propagate(new mgr.Signal(this, signal.value, new Date));
}
};
return node;
}
// createMap (a -> b) (arrayof event<a>) -> event<b>
//
// Creates an event that maps input signal values to output signal values
// using a Javascript mapping function.
this.createMap = function(mapFn, prev) {
var node = new mgr.Node(prev);
node.type = "Map " + mapFn;
node.onSignal = function(signal) {
var newValue = mapFn(signal.value);
if(this.value != newValue) {
this.value = newValue;
this.propagate(new mgr.Signal(this, newValue, new Date));
}
};
return node;
}
// createOnce (arrayof event<a>) -> event<a>
this.createOnce = function(prev) {
var node = new mgr.Event(prev);
node.type = "Once";
node.fired = false;
node.onSignal = function(signal) {
if(!this.fired) {
this.fired = true;
this.propagate(new mgr.Signal(this, signal.value, new Date));
}
};
return node;
};
// createAlert (arrayof event<a>) -> event<a>
//
// Creates an event
this.createAlert = function(prev) {
var node = new mgr.Event(prev);
node.onSignal = function(signal) {
alert(signal);
this.propagate(new mgr.Signal(this, signal.value, new Date));
};
return node;
}
this.createDelay = function(delay, prev) {
var node = new mgr.Event(prev);
node.type = "Delay " + delay;
node.onSignal = function(signal) {
window.setTimeout(delay, "this.propagate(new mgr.Signal(this, signal.value, new Date))");
};
return node;
};
// createMerge (arrayof event<a>) -> event<a>
this.createMerge = function(prev) {
var node = new mgr.Event(prev);
node.type = "Merge";
return node;
};
// createHold (arrayof event<a>) -> behaviour<a>
this.createHold = function(init, prev) {
var node = new mgr.Behaviour(prev);
node.type = "Hold";
return node;
};
// createChanges (arrayof behaviour<a>) -> event<a>
this.createChanges = function(prev) {
var node = new mgr.Event(prev);
node.type = "Changes";
return node;
};
this.createSwitch = function(init, prev) {
var node = new mgr.Behaviour(init, prev);
node.type = "Switch";
node.onSignal = function(signal) {
this.setValue(!this.value);
};
return node;
};
// createFunction (void -> a) (arrayof node<a>) -> behaviour<a>
//
// Creates a behaviour whose value is determined by a JavaScript function.
// Input signals trigger refreshes, but their values have no effect on the
// value of the behaviour. Output signals are only created when the value of
// the behaviour changes.
this.createFunction = function(valueFn, prev) {
var node = new mgr.Behaviour(valueFn(), prev);
node.type = "Function " + valueFn;
node.onSignal = function(signal) {
this.setValue(valueFn());
};
return node;
};
};

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,677 @@
load(libdir + 'match.js');
load(libdir + 'array.js');
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 })
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 unop(token) Pattern({ type: "UnaryOperator", token: token })
function binop(token) Pattern({ type: "BinaryOperator", token: token })
function aop(token) Pattern({ type: "AssignmentOperator", token: token })
function updop(token) Pattern({ type: "UpdateOperator", token: token })
function logop(token) Pattern({ type: "LogicalOperator", token: token })
function unExpr(op, arg) Pattern({ type: "UnaryExpression", operator: unop(op), argument: arg })
function binExpr(op, left, right) Pattern({ type: "BinaryExpression", operator: binop(op), left: left, right: right })
function aExpr(op, left, right) Pattern({ type: "AssignmentExpression", operator: aop(op), left: left, right: right })
function updExpr(op, arg, prefix) Pattern({ type: "UpdateExpression", operator: updop(op), argument: arg, prefix: prefix })
function logExpr(op, left, right) Pattern({ type: "LogicalExpression", operator: logop(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
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("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 test XMLNAME, XMLCDATA, and XMLCOMMENT.