Support rest parameters

http://wiki.ecmascript.org/doku.php?id=harmony:rest_parameters
The final parameter to a function is a rest parameter if it is
prefixed by "...". FunctionExpression and FunctionDeclaration
nodes have a new "rest" property that is null if there is no
rest parameter, or contains an Identifer for the parameter.
https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API#Functions

Implemented by adding a new token, `_ellipsis`, which consists of
three dots. Modified the body of parseFunction to allow a single
rest parameter at the end of an argument list. Both the token and
the rest parameter require `options.ecmaVersion` >= 6, otherwise
three dots are tokenized as three dots.
This commit is contained in:
Brandon Mills 2013-11-13 14:25:38 -05:00 committed by Marijn Haverbeke
parent 2de16b8cb0
commit 5552e866f9
2 changed files with 350 additions and 10 deletions

View File

@ -308,7 +308,7 @@
var _bracketL = {type: "[", beforeExpr: true}, _bracketR = {type: "]"}, _braceL = {type: "{", beforeExpr: true};
var _braceR = {type: "}"}, _parenL = {type: "(", beforeExpr: true}, _parenR = {type: ")"};
var _comma = {type: ",", beforeExpr: true}, _semi = {type: ";", beforeExpr: true};
var _colon = {type: ":", beforeExpr: true}, _dot = {type: "."}, _question = {type: "?", beforeExpr: true};
var _colon = {type: ":", beforeExpr: true}, _dot = {type: "."}, _ellipsis = {type: "..."}, _question = {type: "?", beforeExpr: true};
// Operators. These carry several kinds of properties to help the
// parser use them properly (the presence of these properties is
@ -345,8 +345,8 @@
exports.tokTypes = {bracketL: _bracketL, bracketR: _bracketR, braceL: _braceL, braceR: _braceR,
parenL: _parenL, parenR: _parenR, comma: _comma, semi: _semi, colon: _colon,
dot: _dot, question: _question, slash: _slash, eq: _eq, name: _name, eof: _eof,
num: _num, regexp: _regexp, string: _string};
dot: _dot, ellipsis: _ellipsis, question: _question, slash: _slash, eq: _eq,
name: _name, eof: _eof, num: _num, regexp: _regexp, string: _string};
for (var kw in keywordTypes) exports.tokTypes["_" + kw] = keywordTypes[kw];
// This is a trick taken from Esprima. It turns out that, on
@ -582,8 +582,14 @@
function readToken_dot() {
var next = input.charCodeAt(tokPos + 1);
if (next >= 48 && next <= 57) return readNumber(true);
++tokPos;
return finishToken(_dot);
var next2 = input.charCodeAt(tokPos + 2);
if (options.ecmaVersion >= 6 && next === 46 && next2 === 46) { // 46 = dot '.'
tokPos += 3;
return finishToken(_ellipsis);
} else {
++tokPos;
return finishToken(_dot);
}
}
function readToken_slash() { // '/'
@ -659,7 +665,7 @@
function getTokenFromCode(code) {
switch(code) {
// The interpretation of a dot depends on whether it is followed
// by a digit.
// by a digit or another two dots.
case 46: // '.'
return readToken_dot();
@ -1706,11 +1712,22 @@
else if (isStatement) unexpected();
else node.id = null;
node.params = [];
var first = true;
node.rest = null;
expect(_parenL);
while (!eat(_parenR)) {
if (!first) expect(_comma); else first = false;
node.params.push(parseIdent());
for (;;) {
if (eat(_parenR)) {
break;
} else if (options.ecmaVersion >= 6 && eat(_ellipsis)) {
node.rest = parseIdent();
expect(_parenR);
break;
} else {
node.params.push(parseIdent());
if (!eat(_comma)) {
expect(_parenR);
break;
}
}
}
// Start a new scope with regard to labels and the `inFunction`

View File

@ -23572,6 +23572,171 @@ test("function hello(a, b) { sayHi(); }", {
}
});
test("function hello(...rest) { }", {
type: "Program",
body: [
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "hello",
loc: {
start: {
line: 1,
column: 9
},
end: {
line: 1,
column: 14
}
}
},
params: [],
rest: {
type: "Identifier",
name: "rest",
loc: {
start: {
line: 1,
column: 18
},
end: {
line: 1,
column: 22
}
}
},
body: {
type: "BlockStatement",
body: [],
loc: {
start: {
line: 1,
column: 24
},
end: {
line: 1,
column: 27
}
}
},
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 1,
column: 27
}
}
}
],
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 1,
column: 27
}
}
}, {
ecmaVersion: 6,
locations: true
});
test("function hello(a, ...rest) { }", {
type: "Program",
body: [
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "hello",
loc: {
start: {
line: 1,
column: 9
},
end: {
line: 1,
column: 14
}
}
},
params: [
{
type: "Identifier",
name: "a",
loc: {
start: {
line: 1,
column: 15
},
end: {
line: 1,
column: 16
}
}
}
],
rest: {
type: "Identifier",
name: "rest",
loc: {
start: {
line: 1,
column: 21
},
end: {
line: 1,
column: 25
}
}
},
body: {
type: "BlockStatement",
body: [],
loc: {
start: {
line: 1,
column: 27
},
end: {
line: 1,
column: 30
}
}
},
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 1,
column: 30
}
}
}
],
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 1,
column: 30
}
}
}, {
ecmaVersion: 6,
locations: true
});
test("var hi = function() { sayHi() };", {
type: "Program",
body: [
@ -23702,6 +23867,153 @@ test("var hi = function() { sayHi() };", {
}
});
test("var hi = function (...r) { sayHi() };", {
type: "Program",
body: [
{
type: "VariableDeclaration",
declarations: [
{
type: "VariableDeclarator",
id: {
type: "Identifier",
name: "hi",
loc: {
start: {
line: 1,
column: 4
},
end: {
line: 1,
column: 6
}
}
},
init: {
type: "FunctionExpression",
id: null,
params: [],
rest: {
type: "Identifier",
name: "r",
loc: {
start: {
line: 1,
column: 22
},
end: {
line: 1,
column: 23
}
}
},
body: {
type: "BlockStatement",
body: [
{
type: "ExpressionStatement",
expression: {
type: "CallExpression",
callee: {
type: "Identifier",
name: "sayHi",
loc: {
start: {
line: 1,
column: 27
},
end: {
line: 1,
column: 32
}
}
},
arguments: [],
loc: {
start: {
line: 1,
column: 27
},
end: {
line: 1,
column: 34
}
}
},
loc: {
start: {
line: 1,
column: 27
},
end: {
line: 1,
column: 34
}
}
}
],
loc: {
start: {
line: 1,
column: 25
},
end: {
line: 1,
column: 36
}
}
},
loc: {
start: {
line: 1,
column: 9
},
end: {
line: 1,
column: 36
}
}
},
loc: {
start: {
line: 1,
column: 4
},
end: {
line: 1,
column: 36
}
}
}
],
kind: "var",
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 1,
column: 37
}
}
}
],
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 1,
column: 37
}
}
}, {
ecmaVersion: 6,
locations: true
});
test("var hi = function eval() { };", {
type: "Program",
body: [
@ -26551,6 +26863,17 @@ testFail("({ get i() { }, get i() { } })",
testFail("({ set i(x) { }, set i(x) { } })",
"Redefinition of property (1:21)");
testFail("function t(...) { }",
"Unexpected token (1:11)");
testFail("function t(...) { }",
"Unexpected token (1:14)",
{ ecmaVersion: 6 });
testFail("function t(...rest, b) { }",
"Unexpected token (1:18)",
{ ecmaVersion: 6 });
testFail("function t(if) { }",
"Unexpected token (1:11)");