Skip to content

Commit

Permalink
New: Rest Parameter (refs: #10)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jamund Ferguson committed Feb 10, 2015
1 parent 64e5dbf commit 69f4dd3
Show file tree
Hide file tree
Showing 10 changed files with 202 additions and 6 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ var ast = espree.parse(code, {
// enable parsing of default parameters
defaultParams: false,

// enable parsing of rest parameters
restParam: true,

// enable parsing of for-of statement
forOf: true,

Expand Down
52 changes: 49 additions & 3 deletions espree.js
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,18 @@ function scanPunctuator() {
}
}

// 3-character punctuators: === !== >>> <<= >>=
// 3-character punctuators: ... === !== >>> <<= >>=

if (ch1 === "." && ch2 === "." && ch3 === ".") {
index += 3;
return {
type: Token.Punctuator,
value: "...",
lineNumber: lineNumber,
lineStart: lineStart,
range: [start, index]
};
}

if (ch1 === ">" && ch2 === ">" && ch3 === ">") {
index += 3;
Expand Down Expand Up @@ -3218,6 +3229,13 @@ function reinterpretAsCoverFormalsList(expressions) {
reinterpretAsDestructuredParameter(options, param);
params.push(param);
defaults.push(null);
} else if (param.type === astNodeTypes.SpreadElement) {
assert(i === len - 1, "It is guaranteed that SpreadElement is last element by parseExpression");
if (param.argument.type !== astNodeTypes.Identifier) {
throwError({}, Messages.InvalidLHSInFormalsList);
}
reinterpretAsDestructuredParameter(options, param.argument);
rest = param.argument;
} else if (param.type === astNodeTypes.AssignmentExpression) {
params.push(param.left);
defaults.push(param.right);
Expand Down Expand Up @@ -3266,7 +3284,13 @@ function parseArrowFunctionExpression(options, marker) {
}

strict = previousStrict;
return markerApply(marker, astNodeFactory.createArrowFunctionExpression(options.params, options.defaults, body, body.type !== astNodeTypes.BlockStatement));
return markerApply(marker, astNodeFactory.createArrowFunctionExpression(
options.params,
options.defaults,
body,
options.rest,
body.type !== astNodeTypes.BlockStatement
));
}

// 11.13 Assignment Operators
Expand Down Expand Up @@ -4265,11 +4289,19 @@ function validateParam(options, param, name) {
}

function parseParam(options) {
var token, param, def,
var token, rest, param, def,
allowRestParam = extra.ecmaFeatures.restParam,
allowDestructuring = extra.ecmaFeatures.destructuring,
allowDefaultParams = extra.ecmaFeatures.defaultParams;

token = lookahead;
if (token.value === "...") {
if (!allowRestParam) {
throwUnexpected(lookahead);
}
token = lex();
rest = true;
}

if (match("[")) {
if (!allowDestructuring) {
Expand All @@ -4278,6 +4310,9 @@ function parseParam(options) {
param = parseArrayInitialiser();
reinterpretAsDestructuredParameter(options, param);
} else if (match("{")) {
if (rest) {
throwError({}, Messages.ObjectPatternAsRestParameter);
}
if (!allowDestructuring) {
throwUnexpected(lookahead);
}
Expand All @@ -4289,6 +4324,9 @@ function parseParam(options) {
}

if (match("=")) {
if (rest) {
throwErrorTolerant(lookahead, Messages.DefaultRestParameter);
}
if (!allowDefaultParams) {
throwUnexpected(lookahead);
}
Expand All @@ -4297,6 +4335,14 @@ function parseParam(options) {
++options.defaultCount;
}

if (rest) {
if (!match(")")) {
throwError({}, Messages.ParameterAfterRestParameter);
}
options.rest = param;
return false;
}

options.params.push(param);
options.defaults.push(def ? def : null); // TODO: determine if null or undefined (see: #55)

Expand Down
5 changes: 3 additions & 2 deletions lib/ast-node-factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,19 +56,20 @@ module.exports = {
* @param {ASTNode} params The function arguments
* @param {ASTNode} defaults Any default arguments
* @param {ASTNode} body The function body
* @param {ASTNode} rest The rest parameter
* @param {boolean} expression True if the arrow function is created via an expression.
* Always false for declarations, but kept here to be in sync with
* FunctionExpression objects.
* @returns {ASTNode} An ASTNode representing the entire arrow function expression
*/
createArrowFunctionExpression: function (params, defaults, body, expression) {
createArrowFunctionExpression: function (params, defaults, body, rest, expression) {
return {
type: astNodeTypes.ArrowFunctionExpression,
id: null,
params: params,
defaults: defaults,
body: body,
rest: null,
rest: rest,
generator: false,
expression: expression
};
Expand Down
1 change: 1 addition & 0 deletions lib/ast-node-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ module.exports = {
Property: "Property",
ReturnStatement: "ReturnStatement",
SequenceExpression: "SequenceExpression",
SpreadElement: "SpreadElement",
SwitchCase: "SwitchCase",
SwitchStatement: "SwitchStatement",
TaggedTemplateExpression: "TaggedTemplateExpression",
Expand Down
3 changes: 3 additions & 0 deletions lib/features.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ module.exports = {
// enable parsing of default parameters
defaultParams: false,

// enable parsing of rest parameters
restParam: false,

// enable parsing of for-of statements
forOf: false,

Expand Down
6 changes: 6 additions & 0 deletions lib/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,17 @@ module.exports = {
IllegalBreak: "Illegal break statement",
IllegalReturn: "Illegal return statement",
IllegalYield: "Illegal yield expression",
IllegalSpread: "Illegal spread element",
StrictModeWith: "Strict mode code may not include a with statement",
StrictCatchVariable: "Catch variable may not be eval or arguments in strict mode",
StrictVarName: "Variable name may not be eval or arguments in strict mode",
StrictParamName: "Parameter name eval or arguments is not allowed in strict mode",
StrictParamDupe: "Strict mode function may not have duplicate parameter names",
ParameterAfterRestParameter: "Rest parameter must be final parameter of an argument list",
DefaultRestParameter: "Rest parameter can not have a default value",
ElementAfterSpreadElement: "Spread must be the final element of an element list",
ObjectPatternAsRestParameter: "Invalid rest parameter",
ObjectPatternAsSpread: "Invalid spread argument",
StrictFunctionName: "Function name may not be eval or arguments in strict mode",
StrictOctalLiteral: "Octal literals are not allowed in strict mode.",
StrictDelete: "Delete of an unqualified identifier in strict mode.",
Expand Down
1 change: 1 addition & 0 deletions tests/fixtures/ast/API.json
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@
"Property": "Property",
"ReturnStatement": "ReturnStatement",
"SequenceExpression": "SequenceExpression",
"SpreadElement": "SpreadElement",
"SwitchCase": "SwitchCase",
"SwitchStatement": "SwitchStatement",
"TaggedTemplateExpression": "TaggedTemplateExpression",
Expand Down
130 changes: 130 additions & 0 deletions tests/fixtures/ecma-features/restParam/basic-rest.result.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
module.exports = {
"type": "Program",
"body": [
{
"type": "FunctionDeclaration",
"id": {
"type": "Identifier",
"name": "f",
"range": [
9,
10
],
"loc": {
"start": {
"line": 1,
"column": 9
},
"end": {
"line": 1,
"column": 10
}
}
},
"params": [
{
"type": "Identifier",
"name": "a",
"range": [
11,
12
],
"loc": {
"start": {
"line": 1,
"column": 11
},
"end": {
"line": 1,
"column": 12
}
}
}
],
"defaults": [],
"body": {
"type": "BlockStatement",
"body": [],
"range": [
20,
22
],
"loc": {
"start": {
"line": 1,
"column": 20
},
"end": {
"line": 1,
"column": 22
}
}
},
"rest": {
"type": "Identifier",
"name": "b",
"range": [
17,
18
],
"loc": {
"start": {
"line": 1,
"column": 17
},
"end": {
"line": 1,
"column": 18
}
}
},
"generator": false,
"expression": false,
"range": [
0,
22
],
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 22
}
}
},
{
"type": "EmptyStatement",
"range": [
22,
23
],
"loc": {
"start": {
"line": 1,
"column": 22
},
"end": {
"line": 1,
"column": 23
}
}
}
],
"range": [
0,
23
],
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 23
}
}
}
1 change: 1 addition & 0 deletions tests/fixtures/ecma-features/restParam/basic-rest.src.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
function f(a, ...b) {};
6 changes: 5 additions & 1 deletion tests/lib/ecma-features.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,11 @@ describe("ecmaFeatures", function() {

result = ex; // if an error is thrown, match the error
}
assert.deepEqual(result, expected);
try {
assert.deepEqual(result, expected);
} catch (e) {
throw result;
}
});

it("should throw an error when " + feature + " is false", function() {
Expand Down

0 comments on commit 69f4dd3

Please sign in to comment.