Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New: Rest Parameter (refs: #10) #57

Merged
merged 1 commit into from
Feb 10, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
restParams: true,

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

Expand Down
83 changes: 69 additions & 14 deletions espree.js
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ function scanPunctuator() {
}

// The ... operator only valid in JSX mode for now
if (extra.ecmaFeatures.jsx && state.inJSXSpreadAttribute) {
if (extra.ecmaFeatures.restParams || (extra.ecmaFeatures.jsx && state.inJSXSpreadAttribute)) {
if (ch1 === "." && ch2 === "." && ch3 === ".") {
index += 3;
return {
Expand Down Expand Up @@ -2860,6 +2860,15 @@ function parseArguments() {
return args;
}

function parseSpreadOrAssignmentExpression() {
if (match("...")) {
var marker = markerCreate();
lex();
return markerApply(marker, astNodeFactory.createSpreadElement(parseAssignmentExpression()));
}
return parseAssignmentExpression();
}

function parseNonComputedProperty() {
var token,
marker = markerCreate();
Expand Down Expand Up @@ -3218,6 +3227,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 +3282,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 @@ -3323,7 +3345,7 @@ function reinterpretAsDestructuredParameter(options, expr) {
allowDestructuring = extra.ecmaFeatures.destructuring;

if (!allowDestructuring) {
throwUnexpected(lex());
throwUnexpected(lex());
}

if (expr.type === astNodeTypes.ObjectExpression) {
Expand Down Expand Up @@ -3374,7 +3396,7 @@ function parseAssignmentExpression() {

if (match("(")) {
token = lookahead2();
if (token.value === ")" && token.type === Token.Punctuator) {
if ((token.value === ")" && token.type === Token.Punctuator) || token.value === "...") {
params = parseParams();
if (!match("=>")) {
throwUnexpected(lex());
Expand Down Expand Up @@ -3435,26 +3457,37 @@ function parseAssignmentExpression() {
// 11.14 Comma Operator

function parseExpression() {
var expr,
marker = markerCreate();

expr = parseAssignmentExpression();
var marker = markerCreate(),
expr = parseAssignmentExpression(),
expressions = [ expr ],
sequence, spreadFound;

if (match(",")) {
expr = astNodeFactory.createSequenceExpression([ expr ]);

while (index < length) {
if (!match(",")) {
break;
}
lex();
expr.expressions.push(parseAssignmentExpression());
expr = parseSpreadOrAssignmentExpression();
expressions.push(expr);

if (expr.type === astNodeTypes.SpreadElement) {
spreadFound = true;
if (!match(")")) {
throwError({}, Messages.ElementAfterSpreadElement);
}
break;
}
}

markerApply(marker, expr);
sequence = markerApply(marker, astNodeFactory.createSequenceExpression(expressions));
}

return expr;
if (spreadFound && lookahead2().value !== "=>") {
throwError({}, Messages.IllegalSpread);
}

return sequence || expr;
}

// 12.1 Block
Expand Down Expand Up @@ -4265,11 +4298,19 @@ function validateParam(options, param, name) {
}

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

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

if (match("[")) {
if (!allowDestructuring) {
Expand All @@ -4278,6 +4319,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 +4333,9 @@ function parseParam(options) {
}

if (match("=")) {
if (rest) {
throwErrorTolerant(lookahead, Messages.DefaultRestParameter);
}
if (!allowDefaultParams) {
throwUnexpected(lookahead);
}
Expand All @@ -4297,6 +4344,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
17 changes: 15 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) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about FunctionExpression and FunctionDeclaration?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it was actually already there, but I'll add some more tests!

return {
type: astNodeTypes.ArrowFunctionExpression,
id: null,
params: params,
defaults: defaults,
body: body,
rest: null,
rest: rest,
generator: false,
expression: expression
};
Expand Down Expand Up @@ -442,6 +443,18 @@ module.exports = {
};
},

/**
* Create an ASTNode representation of a spread element
* @param {ASTNode} argument The array being spread
* @returns {ASTNode} An ASTNode representing a spread element
*/
createSpreadElement: function (argument) {
return {
type: astNodeTypes.SpreadElement,
argument: argument
};
},

/**
* Create an ASTNode tagged template expression
* @param {ASTNode} tag The tag 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
restParams: 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
arrowFunctions: true,
restParams: true,
destructuring: true
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
module.exports = {
"type": "Program",
"body": [
{
"type": "ExpressionStatement",
"expression": {
"type": "ArrowFunctionExpression",
"id": null,
"params": [
{
"type": "Identifier",
"name": "a",
"range": [
1,
2
],
"loc": {
"start": {
"line": 1,
"column": 1
},
"end": {
"line": 1,
"column": 2
}
}
}
],
"defaults": [],
"body": {
"type": "BlockStatement",
"body": [],
"range": [
13,
15
],
"loc": {
"start": {
"line": 1,
"column": 13
},
"end": {
"line": 1,
"column": 15
}
}
},
"rest": {
"type": "Identifier",
"name": "b",
"range": [
7,
8
],
"loc": {
"start": {
"line": 1,
"column": 7
},
"end": {
"line": 1,
"column": 8
}
}
},
"generator": false,
"expression": false,
"range": [
0,
15
],
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 15
}
}
},
"range": [
0,
16
],
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 16
}
}
}
],
"range": [
0,
16
],
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 16
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(a, ...b) => {};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
arrowFunctions: true,
restParams: true
};
Loading