Permalink
Browse files

added experimental Harmony Object literal extensions

added new features described in harmony.md file.  Also various bug fixes
and ES5 feature additions including: * ES5 future reserved words are
defined as keywords * various paren-free related bug fixes * as per ES5
reserved keywords can be used as property names * as per ES5 numeric and
string literals may be used property names in access property
definitions within object literals.
  • Loading branch information...
1 parent 1731e16 commit 38f361d42216f73bd95881913b5859de2ec65d08 @allenwb committed Aug 15, 2011
Showing with 326 additions and 117 deletions.
  1. +4 −1 README.md
  2. +47 −23 lib/jsdecomp.js
  3. +9 −6 lib/jsdefs.js
  4. +122 −23 lib/jsexec.js
  5. +144 −64 lib/jsparse.js
View
5 README.md
@@ -2,11 +2,13 @@
Narcissus is a JavaScript interpreter written in pure JavaScript (i.e., a [meta-circular evaluator](http://en.wikipedia.org/wiki/Meta-circular_evaluator)), using the [SpiderMonkey](http://www.mozilla.org/js/spidermonkey/) engine.
+This verson of Narcissus adds various experimental features that have been proposed for ES Harmony. See `harmony.md` for a list of new features.
+
Originally a proof-of-concept by [Brendan Eich](http://brendaneich.com/), Narcissus is being revived as a test-bed for rapidly prototyping new language features for the JavaScript language (as well as the ECMAScript standard).
# Usage
-To run the Narcissus shell, install the [SpiderMonkey shell](https://developer.mozilla.org/en/Introduction_to_the_JavaScript_shell) and either set the `NJS_SHELL` environment variable to the path to the `js` executable, or creating a symbolic link to `js` in the top-level Narcissus directory. Then run Narcissus with the `njs` script.
+To run the Narcissus shell, install the [SpiderMonkey shell](https://developer.mozilla.org/en/Introduction_to_the_JavaScript_shell) (note that pre-built versions of the shell are available in the Firefox nightly builds, for example <http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-trunk/jsshell-win32.zip> or <http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-trunk/jsshell-mac.zip>). After bbuilding or downloading set the `NJS_SHELL` environment variable to the path to the `js` executable, or creating a symbolic link to `js` in the top-level Narcissus directory. Then run Narcissus with the `njs` script.
Usage: njs
@@ -36,3 +38,4 @@ More to come.
* Dave Herman
* Dimitris Vardoulakis
* Patrick Walton
+* Allen Wirfs-Brock
View
70 lib/jsdecomp.js
@@ -90,17 +90,11 @@ Narcissus.decompiler = (function() {
p += "(";
switch (n.type) {
+
case FUNCTION:
- case GETTER:
- case SETTER:
- if (n.type === FUNCTION)
- p += "function";
- else if (n.type === GETTER)
- p += "get";
- else
- p += "set";
-
- p += (n.name ? " " + n.name : "") + "(";
+ if (!n.skipKeyword)
+ p += "function" + (n.name ? " " : "");
+ p += (n.name ? n.name : "") + "(";
for (var i = 0, j = n.params.length; i < j; i++)
p += (i > 0 ? ", " : "") + pp(n.params[i], d);
p += ") " + pp(n.body, d);
@@ -388,12 +382,20 @@ Narcissus.decompiler = (function() {
}
break;
+ case PROTO:
+ var nc = n.children;
+ p += pp(nc[0], d) + " <| " + pp(nc[1], d);
+ break;
+
+ case EXTEND:
+ case SUPER_DOT:
case DOT:
var nc = n.children;
p += pp(nc[0], d) + "." + pp(nc[1], d);
break;
- case INDEX:
+ case SUPER_INDEX:
+ case INDEX:
var nc = n.children;
p += pp(nc[0], d) + "[" + pp(nc[1], d) + "]";
break;
@@ -439,29 +441,47 @@ Narcissus.decompiler = (function() {
p += " if (" + pp(n.guard, d) + ")";
break;
- case OBJECT_INIT:
+ case OBJECT_INIT: {
var nc = n.children;
- if (nc[0] && nc[0].type === PROPERTY_INIT)
- p += "{\n";
- else
- p += "{";
+ var pname = nc.propertyName;
+// if (nc[0] && nc[0].type !== PROPERTY_INIT)
+ p += "{\n";
+// else
+// p += "{";
for (var i = 0, j = nc.length; i < j; i++) {
if (i > 0) {
p += ",\n";
}
var t = nc[i];
- if (t.type === PROPERTY_INIT) {
- var tc = t.children;
- p += indent(4, pp(tc[0], d)) + ": " +
- indent(4, pp(tc[1], d)).substring(4);
- } else {
- p += indent(4, pp(t, d));
+ pname = t.propertyName;
+ if (pname.type === IDENTIFIER)
+ pname = '"' + pname.value + '"';
+ else
+ pname = '[' + pp(pname, d) + ']';
+ switch (t.type) {
+ case PROPERTY_INIT:
+ p += indent(4, pname + ": " +
+ pp(t.initializer, d));
+ break;
+ case GETTER:
+ case SETTER:
+ t.functionDef.skipKeyword = true;
+ p += indent(4, (t.type===GETTER ? "get " : "set ") +
+ (!t.functionDef.name ? pname + " " : "" ) +
+ pp(t.functionDef, d));
+ break;
+ case METHOD_INIT:
+ t.functionDef.skipKeyword = true;
+ p += indent(4, (!t.functionDef.name ? pname + " " : "") +
+ pp(t.functionDef, d));
+ break;
}
}
p += "\n}";
break;
-
+ }
+
case NULL:
p += "null";
break;
@@ -470,6 +490,10 @@ Narcissus.decompiler = (function() {
p += "this";
break;
+ case SUPER:
+ p += "super";
+ break;
+
case TRUE:
p += "true";
break;
View
15 lib/jsdefs.js
@@ -86,29 +86,31 @@ Narcissus.definitions = (function() {
"!", "~", "UNARY_PLUS", "UNARY_MINUS",
"++", "--",
".",
+ "<|",
"[", "]",
"{", "}",
"(", ")",
// Nonterminal tree node type codes.
"SCRIPT", "BLOCK", "LABEL", "FOR_IN", "CALL", "NEW_WITH_ARGS", "INDEX",
- "ARRAY_INIT", "OBJECT_INIT", "PROPERTY_INIT", "GETTER", "SETTER",
+ "ARRAY_INIT", "OBJECT_INIT", "PROPERTY_INIT", "METHOD_INIT", "GETTER", "SETTER",
"GROUP", "LIST", "LET_BLOCK", "ARRAY_COMP", "GENERATOR", "COMP_TAIL",
-
+ "EXTEND", "SUPER_DOT", "SUPER_INDEX",
+
// Terminals.
"IDENTIFIER", "NUMBER", "STRING", "REGEXP",
// Keywords.
"break",
- "case", "catch", "const", "continue",
+ "case", "catch", "class", "const", "continue",
"debugger", "default", "delete", "do",
- "else",
+ "else", "enum", "export", "extends",
"false", "finally", "for", "function",
- "if", "in", "instanceof",
+ "if", "import", "in", "instanceof",
"let",
"new", "null",
"return",
- "switch",
+ "super","switch",
"this", "throw", "true", "try", "typeof",
"var", "void",
"yield",
@@ -137,6 +139,7 @@ Narcissus.definitions = (function() {
'!=': "NE",
'<<': "LSH",
'<=': "LE",
+ '<|': "PROTO",
'<': "LT",
'>>>': "URSH",
'>>': "RSH",
View
145 lib/jsexec.js
@@ -271,19 +271,38 @@ Narcissus.interpreter = (function() {
Reference.prototype.toString = function () { return this.node.getSource(); }
function getValue(v) {
+ var sup;
if (v instanceof Reference) {
if (!v.base) {
throw new ReferenceError(v.propertyName + " is not defined",
v.node.filename, v.node.lineno);
}
- return v.base[v.propertyName];
+ if ((sup=v.superStart) === undefined) return v.base[v.propertyName];
+ if (sup===-1) throw new ReferenceError(v.propertyName + ": super used in non-method",
+ v.node.filename, v.node.lineno);
+ let desc = lookupPropertyDescriptor(sup,v.propertyName);
+ if (!desc) return undefined;
+ let getter = desc.get;
+ if (getter) return getter.__call__(v,[],ExecutionContext.current);
+ return desc.value;
}
return v;
}
function putValue(v, w, vn) {
- if (v instanceof Reference)
- return (v.base || global)[v.propertyName] = w;
+ var sup;
+ if (v instanceof Reference) {
+ if ((sup=v.superStart) === undefined) return (v.base || global)[v.propertyName] = w;
+ if (sup===-1) throw new ReferenceError(v.propertyName + ": super used in non-method",
+ v.node.filename, v.node.lineno);
+ let desc = lookupPropertyDescriptor(sup,v.propertyName);
+ if (!desc) return;
+ let setter = desc.set;
+ if (setter) return setter.__call__(v,[w],ExecutionContext.current);
+ if (hasDirectProperty(desc,"set")) return /* must be default setter */;
+ throw new ReferenceError("super assignment left-hand side is a data property",
+ vn.filename, vn.lineno)
+ }
throw new ReferenceError("Invalid assignment left-hand side",
vn.filename, vn.lineno);
}
@@ -318,6 +337,15 @@ Narcissus.interpreter = (function() {
throw rn ? new TypeError(message, rn.filename, rn.lineno)
: new TypeError(message);
}
+
+ function lookupPropertyDescriptor(obj,name) {
+ while (obj) {
+ let desc = Object.getOwnPropertyDescriptor(obj,name);
+ if (desc) return desc;
+ obj = Object.getPrototypeOf(obj);
+ }
+ return undefined;
+ };
function execute(n, x) {
var a, c, f, i, j, r, s, t, u, v;
@@ -770,13 +798,55 @@ Narcissus.interpreter = (function() {
v = new Reference(toObject(t, r, c[0]), u, n);
break;
+ case SUPER_DOT:
+ c = n.children;
+ r = execute(c[0], x);
+ t = getValue(r);
+ u = c[1].value;
+ v = new Reference(toObject(t, r, c[0]), u, n);
+ v.superStart = x.superBinding;
+ break;
+
case INDEX:
c = n.children;
r = execute(c[0], x);
t = getValue(r);
u = getValue(execute(c[1], x));
v = new Reference(toObject(t, r, c[0]), String(u), n);
break;
+
+ case SUPER_INDEX:
+ c = n.children;
+ r = execute(c[0], x);
+ t = getValue(r);
+ u = getValue(execute(c[1], x));
+ v = new Reference(toObject(t, r, c[0]), String(u), n);
+ v.superStart = x.superBinding;
+ break;
+
+ case PROTO:
+ c = n.children;
+ r = execute(c[0], x);
+ t = getValue(r);
+ if (t !== null) t = toObject(t,r,c[0]);
+ if (c[1].type === OBJECT_INIT) {
+ v = Object.create(t);
+ addPropertyFromLiteral(v, c[1], x);
+ break;
+ }
+ u = getValue(execute(c[1], x));
+ v = toObject(u,u);
+ v.__proto__ = t;
+ if (t && typeof v === "function" && hasDirectProperty(v,"prototype")
+ && hasDirectProperty(t,"prototype")) v.prototype.__proto__ = t.prototype;
+ break;
+
+ case EXTEND:
+ c = n.children;
+ r = execute(c[0], x);
+ v = getValue(r);
+ addPropertyFromLiteral(v, c[1], x);
+ break;
case LIST:
// Curse ECMA for specifying that arguments is not an Array object!
@@ -832,25 +902,14 @@ Narcissus.interpreter = (function() {
case OBJECT_INIT:
v = {};
- c = n.children;
- for (i = 0, j = c.length; i < j; i++) {
- t = c[i];
- if (t.type === PROPERTY_INIT) {
- let c2 = t.children;
- v[c2[0].value] = getValue(execute(c2[1], x));
- } else {
- f = newFunction(t, x);
- u = (t.type === GETTER) ? '__defineGetter__'
- : '__defineSetter__';
- v[u](t.name, thunk(f, x));
- }
- }
+ addPropertyFromLiteral(v, n, x);
break;
case NULL:
v = null;
break;
+ case SUPER:
case THIS:
v = x.thisObject;
break;
@@ -900,14 +959,53 @@ Narcissus.interpreter = (function() {
Activation.prototype.__proto__ = null;
delete Activation.prototype.constructor;
-
- function FunctionObject(node, scope) {
+
+
+ function addPropertyFromLiteral(obj,litNode,x) {
+ var i,j,f,t,u,c2;
+ var c = litNode.children;
+ var pname, pvalue, pinit, pattr;
+ var proto = Object.getPrototypeOf(obj);
+ for (i = 0, j = c.length; i < j; i++) {
+ t = c[i];
+ pname = t.propertyName === IDENTIFIER ? t.propertypName.value : String(getValue(execute(t.propertyName, x)));
+ switch (t.type) {
+ case GETTER: case SETTER:
+ f = newFunction(t.functionDef, x, proto);
+ u = (t.type === GETTER) ? '__defineGetter__'
+ : '__defineSetter__';
+ obj[u](pname, thunk(f, x));
+ break;
+ case METHOD_INIT:
+ f = newFunction(t.functionDef, x, proto);
+ Object.defineProperty(obj,pname,{value:f,writable:false,enumerable: false, configurable: false});
+ break;
+ case PROPERTY_INIT:
+ pinit = t.initializer;
+ if (pinit.type === FUNCTION) pvalue = newFunction(pinit, x, proto);
+ else pvalue = getValue(execute(pinit, x));
+ pattrs = {value:pvalue,writable:true,enumerable: true, configurable: true};
+ Object.defineProperty(obj,pname,pattrs);
+ break;
+ default:
+ throw "PANIC: unknown proptery init " + t.type + ": " + uneval(litNode);
+ }
+ }
+ }
+
+ function FunctionObject(node, scope, superBinding) {
this.node = node;
this.scope = scope;
+ this.__call__ = this.__call__; //force to be an own property
+ if (node.usesSuper) this.superBinding = superBinding || -1;
definitions.defineProperty(this, "length", node.params.length, true, true, true);
- var proto = {};
- definitions.defineProperty(this, "prototype", proto, true);
- definitions.defineProperty(proto, "constructor", this, false, false, true);
+ if (node.functionForm !== parser.METHOD_FORM) {
+ this.__construct__ = this.__construct__;
+ this.__hasInstance__ = this.__hasInstance__;
+ var proto = {};
+ definitions.defineProperty(this, "prototype", proto, true);
+ definitions.defineProperty(proto, "constructor", this, false, false, true);
+ } else this.__construct__=null;
}
function getPropertyDescriptor(obj, name) {
@@ -926,8 +1024,8 @@ Narcissus.interpreter = (function() {
}
// Returns a new function wrapped with a Proxy.
- function newFunction(n, x) {
- var fobj = new FunctionObject(n, x.scope);
+ function newFunction(n, x, superBinding) {
+ var fobj = new FunctionObject(n, x.scope, superBinding);
var handler = definitions.makePassthruHandler(fobj);
var p = Proxy.createFunction(handler,
function() { return fobj.__call__(this, arguments, x); },
@@ -943,6 +1041,7 @@ Narcissus.interpreter = (function() {
x2.thisObject = t || global;
x2.caller = x;
x2.callee = this;
+ x2.superBinding = this.superBinding;
definitions.defineProperty(a, "callee", this, false, false, true);
var f = this.node;
x2.scope = {object: new Activation(f, a), parent: this.scope};
View
208 lib/jsparse.js
@@ -76,11 +76,16 @@ Narcissus.parser = (function() {
this.hasEmptyReturn = false;
this.hasReturnWithValue = false;
this.isGenerator = false;
+ this.usesSuper = false;
+ this.possibleDirectEval = false;
this.stmtStack = [];
this.funDecls = [];
this.varDecls = [];
Narcissus.options.ecma3OnlyMode && (this.ecma3OnlyMode = true);
Narcissus.options.parenFreeMode && (this.parenFreeMode = true);
+ Narcissus.options.version === "harmony" && (this.harmonyMode = true);
+ Narcissus.options.ecma3OnlyMode && (this.harmonyMode = this.parenFreeMode = false);
+
}
StaticContext.prototype = {
@@ -91,14 +96,15 @@ Narcissus.parser = (function() {
inForLoopInit: false,
ecma3OnlyMode: false,
parenFreeMode: false,
+ harmonyMode: false,
maybeMatchLeftParen: function (t) {
if (this.parenFreeMode)
return t.match(LEFT_PAREN) ? LEFT_PAREN : END;
return t.mustMatch(LEFT_PAREN);
},
maybeMatchRightParen: function (t, p) {
- if (p === LEFT_PAREN)
- t.mustMatch(RIGHT_PAREN);
+ if (this.parenFreeMode && p !== LEFT_PAREN) return;
+ t.mustMatch(RIGHT_PAREN);
},
};
@@ -247,7 +253,7 @@ Narcissus.parser = (function() {
return n;
}
- const DECLARED_FORM = 0, EXPRESSED_FORM = 1, STATEMENT_FORM = 2;
+ const DECLARED_FORM = 0, EXPRESSED_FORM = 1, STATEMENT_FORM = 2, METHOD_FORM = 3;
/*
* Statement :: (tokenizer, compiler context) -> node
@@ -316,10 +322,12 @@ Narcissus.parser = (function() {
case FOR:
n = new Node(t, LOOP_INIT);
- if (t.match(IDENTIFIER) && t.token.value === "each")
- n.isEach = true;
- else
- t.unget();
+ if (t.match(IDENTIFIER)) {
+ if (t.token.value === "each")
+ n.isEach = true;
+ else
+ t.unget();
+ }
if (!x.parenFreeMode)
t.mustMatch(LEFT_PAREN);
if ((tt = t.peek()) !== SEMICOLON) {
@@ -611,15 +619,14 @@ Narcissus.parser = (function() {
/*
* FunctionDefinition :: (tokenizer, compiler context, boolean,
- * DECLARED_FORM or EXPRESSED_FORM or STATEMENT_FORM)
+ * DECLARED_FORM or EXPRESSED_FORM or STATEMENT_FORM or METHOD_FORM)
* -> node
*/
function FunctionDefinition(t, x, requireName, functionForm) {
var tt, x2;
var f = new Node(t, { params: [] });
- if (f.type !== FUNCTION)
- f.type = (f.value === "get") ? GETTER : SETTER;
- if (t.match(IDENTIFIER))
+ if (f.type !== FUNCTION) f.type = FUNCTION;
+ if (t.match(IDENTIFIER))
f.name = t.token.value;
else if (requireName)
throw t.newSyntaxError("missing function identifier");
@@ -662,7 +669,8 @@ Narcissus.parser = (function() {
if (tt === LEFT_CURLY)
t.mustMatch(RIGHT_CURLY);
-
+
+ f.usesSuper = x2.usesSuper || x2.possibleDirectEval;
f.end = t.token.end;
f.functionForm = functionForm;
if (functionForm === DECLARED_FORM)
@@ -972,7 +980,7 @@ Narcissus.parser = (function() {
case ARRAY_INIT:
lhs.destructuredNames = checkDestructuring(t, x, lhs);
// FALL THROUGH
- case IDENTIFIER: case DOT: case INDEX: case CALL:
+ case IDENTIFIER: case DOT: case INDEX: case CALL: case SUPER_DOT: case SUPER_INDEX:
break;
default:
throw t.newSyntaxError("Bad left-hand side of assignment");
@@ -1219,14 +1227,30 @@ Narcissus.parser = (function() {
while ((tt = t.get()) !== END) {
switch (tt) {
case DOT:
+ if (!x.ecma3OnlyMode && t.peek(true) === LEFT_CURLY) {
+ if (!x.harmonyMode) throw t.newSyntaxError("Missing identifier");
+ n2 = new Node(t, { type: EXTEND });
+ n2.push(n);
+ n2.push(ObjectInitializer(t, x, true));
+ break;
+ }
n2 = new Node(t);
+ if (x.harmonyMode && n.type===SUPER) n2.type=SUPER_DOT;
+ n2.push(n);
+ if (t.get() === IDENTIFIER || t.token.isKeyword) n2.push(new Node(t, { type: IDENTIFIER }))
+ else throw t.newSyntaxError("Missing identifier" + (x.harmonyMode ? " or object literal": ""));
+ break;
+
+ case PROTO: // <|
+ if (x.ecma3OnlyMode || !x.harmonyMode)
+ throw t.newSyntaxError("Illegal operator");
+ n2 = new Node(t, {type: PROTO});
n2.push(n);
- t.mustMatch(IDENTIFIER);
- n2.push(new Node(t));
+ n2.push(PrimaryExpression(t, x, true));
break;
case LEFT_BRACKET:
- n2 = new Node(t, { type: INDEX });
+ n2 = new Node(t, { type: n.type===SUPER ? SUPER_INDEX : INDEX });
n2.push(n);
n2.push(Expression(t, x));
t.mustMatch(RIGHT_BRACKET);
@@ -1237,9 +1261,9 @@ Narcissus.parser = (function() {
n2 = new Node(t, { type: CALL });
n2.push(n);
n2.push(ArgumentList(t, x));
+ if (n.type === IDENTIFIER && n.value === "eval") x.possibleDirectEval = true;
break;
}
-
// FALL THROUGH
default:
t.unget();
@@ -1274,7 +1298,7 @@ Narcissus.parser = (function() {
return n;
}
- function PrimaryExpression(t, x) {
+ function PrimaryExpression(t, x, mustBeLiteral) {
var n, n2, tt = t.get(true);
switch (tt) {
@@ -1307,64 +1331,28 @@ Narcissus.parser = (function() {
break;
case LEFT_CURLY:
- var id, fd;
- n = new Node(t, { type: OBJECT_INIT });
-
- object_init:
- if (!t.match(RIGHT_CURLY)) {
- do {
- tt = t.get();
- if ((t.token.value === "get" || t.token.value === "set") &&
- t.peek() === IDENTIFIER) {
- if (x.ecma3OnlyMode)
- throw t.newSyntaxError("Illegal property accessor");
- n.push(FunctionDefinition(t, x, true, EXPRESSED_FORM));
- } else {
- switch (tt) {
- case IDENTIFIER: case NUMBER: case STRING:
- id = new Node(t, { type: IDENTIFIER });
- break;
- case RIGHT_CURLY:
- if (x.ecma3OnlyMode)
- throw t.newSyntaxError("Illegal trailing ,");
- break object_init;
- default:
- if (t.token.value in definitions.keywords) {
- id = new Node(t, { type: IDENTIFIER });
- break;
- }
- throw t.newSyntaxError("Invalid property name");
- }
- if (t.match(COLON)) {
- n2 = new Node(t, { type: PROPERTY_INIT });
- n2.push(id);
- n2.push(AssignExpression(t, x));
- n.push(n2);
- } else {
- // Support, e.g., |var {x, y} = o| as destructuring shorthand
- // for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
- if (t.peek() !== COMMA && t.peek() !== RIGHT_CURLY)
- throw t.newSyntaxError("missing : after property");
- n.push(id);
- }
- }
- } while (t.match(COMMA));
- t.mustMatch(RIGHT_CURLY);
- }
+ n = ObjectInitializer(t, x, false);
break;
case LEFT_PAREN:
+ if (mustBeLiteral) throw t.newSyntaxError("literal expected");
n = ParenExpression(t, x);
t.mustMatch(RIGHT_PAREN);
n.parenthesized = true;
break;
case LET:
+ if (mustBeLiteral) throw t.newSyntaxError("literal expected");
n = LetBlock(t, x, false);
break;
- case NULL: case THIS: case TRUE: case FALSE:
- case IDENTIFIER: case NUMBER: case STRING: case REGEXP:
+ case SUPER:
+ if (x.ecma3OnlyMode || !x.harmonyMode) throw t.newSyntaxError("super is reserved");
+ x.usesSuper = true;
+ case THIS: case IDENTIFIER:
+ if (mustBeLiteral) throw t.newSyntaxError("literal expected");
+ case NULL: case TRUE: case FALSE:
+ case NUMBER: case STRING: case REGEXP:
n = new Node(t);
break;
@@ -1375,7 +1363,98 @@ Narcissus.parser = (function() {
return n;
}
+
+ function isPropertyName(tt) {
+ return tt === IDENTIFIER || tt === STRING || tt === NUMBER || tt ===LEFT_BRACKET
+ || definitions.keywords[definitions.tokens[tt]] === tt
+ }
+
+ function ObjectInitializer(t, x, lookForLeftCurly) {
+ var n, n2, tt, ttpn;
+ var id, fd, getter;
+ var commaOptional;
+ if (lookForLeftCurly) t.mustMatch(LEFT_CURLY);
+ n = new Node(t, { type: OBJECT_INIT });
+
+ object_init:
+ if (!t.match(RIGHT_CURLY)) {
+ do {
+ commaOptional = false;
+ tt = t.get();
+ ttpn = t.peek();
+ if ((t.token.value === "get" || t.token.value === "set") && isPropertyName(ttpn)) {
+ if (x.ecma3OnlyMode)
+ throw t.newSyntaxError("Illegal property accessor");
+ getter = t.token.value === "get";
+ n2 = new Node(t, { type: getter ? GETTER : SETTER });
+ t.get();
+ n2.propertyName = PropertyName(t, x);
+ if (ttpn === IDENTIFIER) {
+ t.unget();
+ fd = FunctionDefinition(t, x, true, EXPRESSED_FORM);
+ } else fd = FunctionDefinition(t, x, false, EXPRESSED_FORM);
+ if (getter && fd.params.length !== 0)
+ throw t.newSyntaxError("get accessor, too many arguments")
+ else if (!getter && fd.params.length !== 1)
+ throw t.newSyntaxError("set accessor, must have one argument");
+ n2.functionDef =fd;
+ n.push(n2);
+ x.harmonyMode && (commaOptional = true);
+ } else {
+ if (tt===RIGHT_CURLY) {
+ if (x.ecma3OnlyMode)
+ throw t.newSyntaxError("Illegal trailing ,");
+ break object_init;
+ } else id = PropertyName(t,x);
+ if (t.match(COLON)) {
+ n2 = new Node(t, { type: PROPERTY_INIT, propertyName: id });
+ n2.initializer = AssignExpression(t, x);
+ n.push(n2);
+ } else if (t.peek() === LEFT_PAREN) {
+ if (x.ecma3OnlyMode || !x.harmonyMode)
+ throw t.newSyntaxError("Illegal property definition");
+ n2 = new Node(t, { type: METHOD_INIT, propertyName: id });
+ if (tt === IDENTIFIER) {
+ t.unget();
+ n2.functionDef = FunctionDefinition(t, x, true, METHOD_FORM);
+ } else n2.functionDef = FunctionDefinition(t, x, false, METHOD_FORM);
+ n2.attributes = "method";
+ n.push(n2);
+ x.harmonyMode && (commaOptional = true);
+ } else {
+ // Support, e.g., |var {x, y} = o| as destructuring shorthand
+ // for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
+ if (it.peek() !== COMMA && t.peek() !== RIGHT_CURLY)
+ throw t.newSyntaxError("missing : after property");
+ if (tt !== IDENTIFIER) throw t.newSyntaxError("Property name must be identifier");
+ n.push({ type: PROPERTY_INIT, propertyName: id, initializer: id });
+ }
+ }
+ } while (t.match(COMMA) || commaOptional);
+ t.mustMatch(RIGHT_CURLY);
+ }
+ return n;
+ }
+
+ function PropertyName(t, x) {
+ var n2, ttn;
+ tt = t.token.type;
+ switch (tt) {
+ case IDENTIFIER: case NUMBER: case STRING:
+ return new Node(t, { type: IDENTIFIER });
+ case LEFT_BRACKET:
+ if (x.ecma3OnlyMode || !x.harmonyMode) throw t.newSyntaxError("Invalid property name");
+ n2 =Expression(t, x);
+ t.mustMatch(RIGHT_BRACKET);
+ return n2;
+ default:
+ if (!x.ecma3OnlyMode && t.token.value in definitions.keywords)
+ return new Node(t, { type: IDENTIFIER });
+ throw t.newSyntaxError("Invalid property name");
+ }
+ }
+
/*
* parse :: (file ptr, path, line number) -> node
*/
@@ -1395,6 +1474,7 @@ Narcissus.parser = (function() {
DECLARED_FORM: DECLARED_FORM,
EXPRESSED_FORM: EXPRESSED_FORM,
STATEMENT_FORM: STATEMENT_FORM,
+ METHOD_FORM: METHOD_FORM,
Tokenizer: lexer.Tokenizer,
FunctionDefinition: FunctionDefinition
};

0 comments on commit 38f361d

Please sign in to comment.