Skip to content

Commit fb12aa0

Browse files
author
Jamund Ferguson
committed
New: Support for Arrow Functions (refs #10)
1 parent 47d4bff commit fb12aa0

File tree

83 files changed

+2945
-5
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+2945
-5
lines changed

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ var ast = espree.parse(code, {
5959
// specify parsing features (default only has blockBindings: true)
6060
ecmaFeatures: {
6161

62+
// enable parsing of arrow functions
63+
arrowFunctions: true,
64+
6265
// enable parsing of let/const
6366
blockBindings: true,
6467

espree.js

+157-5
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ var Token = tokenInfo.Token,
4747
TokenName = tokenInfo.TokenName,
4848
FnExprTokens = tokenInfo.FnExprTokens,
4949
Regex = syntax.Regex,
50+
PlaceHolders,
5051
PropertyKind,
5152
source,
5253
strict,
@@ -58,6 +59,12 @@ var Token = tokenInfo.Token,
5859
state,
5960
extra;
6061

62+
PlaceHolders = {
63+
ArrowParameterPlaceHolder: {
64+
type: "ArrowParameterPlaceHolder"
65+
}
66+
};
67+
6168
PropertyKind = {
6269
Data: 1,
6370
Get: 2,
@@ -2747,6 +2754,13 @@ function parseGroupExpression() {
27472754

27482755
expect("(");
27492756

2757+
++state.parenthesisCount;
2758+
2759+
if (match(")")) {
2760+
lex();
2761+
return PlaceHolders.ArrowParameterPlaceHolder;
2762+
}
2763+
27502764
expr = parseExpression();
27512765

27522766
expect(")");
@@ -2815,10 +2829,9 @@ function parsePrimaryExpression() {
28152829
peek();
28162830
} else if (type === Token.Template) {
28172831
return parseTemplateLiteral();
2818-
} else {
2832+
} else if (!expr) {
28192833
throwUnexpected(lex());
28202834
}
2821-
28222835
return markerApply(marker, expr);
28232836
}
28242837

@@ -3170,11 +3183,89 @@ function parseConditionalExpression() {
31703183
return expr;
31713184
}
31723185

3186+
// [ES6] 14.2 Arrow Function
3187+
3188+
function parseConciseBody() {
3189+
if (match("{")) {
3190+
return parseFunctionSourceElements();
3191+
}
3192+
return parseAssignmentExpression();
3193+
}
3194+
3195+
function reinterpretAsCoverFormalsList(expressions) {
3196+
var i, len, param, params, defaults, defaultCount, options, rest;
3197+
3198+
params = [];
3199+
defaults = [];
3200+
defaultCount = 0;
3201+
rest = null;
3202+
options = {
3203+
paramSet: {}
3204+
};
3205+
3206+
for (i = 0, len = expressions.length; i < len; i += 1) {
3207+
param = expressions[i];
3208+
if (param.type === astNodeTypes.Identifier) {
3209+
params.push(param);
3210+
defaults.push(null);
3211+
validateParam(options, param, param.name);
3212+
} else if (param.type === astNodeTypes.AssignmentExpression) {
3213+
params.push(param.left);
3214+
defaults.push(param.right);
3215+
++defaultCount;
3216+
validateParam(options, param.left, param.left.name);
3217+
} else {
3218+
return null;
3219+
}
3220+
}
3221+
3222+
if (options.message === Messages.StrictParamDupe) {
3223+
throwError(
3224+
strict ? options.stricted : options.firstRestricted,
3225+
options.message
3226+
);
3227+
}
3228+
3229+
// must be here so it's not an array of [null, null]
3230+
if (defaultCount === 0) {
3231+
defaults = [];
3232+
}
3233+
3234+
return {
3235+
params: params,
3236+
defaults: defaults,
3237+
rest: rest,
3238+
stricted: options.stricted,
3239+
firstRestricted: options.firstRestricted,
3240+
message: options.message
3241+
};
3242+
}
3243+
3244+
function parseArrowFunctionExpression(options, marker) {
3245+
var previousStrict, body;
3246+
3247+
expect("=>");
3248+
previousStrict = strict;
3249+
3250+
body = parseConciseBody();
3251+
3252+
if (strict && options.firstRestricted) {
3253+
throwError(options.firstRestricted, options.message);
3254+
}
3255+
if (strict && options.stricted) {
3256+
throwErrorTolerant(options.stricted, options.message);
3257+
}
3258+
3259+
strict = previousStrict;
3260+
return markerApply(marker, astNodeFactory.createArrowFunctionExpression(options.params, options.defaults, body, body.type !== astNodeTypes.BlockStatement));
3261+
}
3262+
31733263
// 11.13 Assignment Operators
31743264

31753265
function parseAssignmentExpression() {
3176-
var token, left, right, node,
3266+
var token, left, right, list, node, params,
31773267
marker,
3268+
oldParenthesisCount = state.parenthesisCount,
31783269
allowGenerators = extra.ecmaFeatures.generators;
31793270

31803271
// Note that 'yield' is treated as a keyword in strict mode, but a
@@ -3185,10 +3276,41 @@ function parseAssignmentExpression() {
31853276
}
31863277

31873278
marker = markerCreate();
3188-
token = lookahead;
31893279

3280+
if (match("(")) {
3281+
token = lookahead2();
3282+
if (token.value === ")" && token.type === Token.Punctuator) {
3283+
params = parseParams();
3284+
list = {
3285+
defaults: [],
3286+
params: params.params
3287+
};
3288+
return markerApply(marker, parseArrowFunctionExpression(list, marker));
3289+
}
3290+
}
3291+
3292+
// revert to the previous lookahead style object
3293+
token = lookahead;
31903294
node = left = parseConditionalExpression();
31913295

3296+
if (node === PlaceHolders.ArrowParameterPlaceHolder || match("=>")) {
3297+
if (state.parenthesisCount === oldParenthesisCount ||
3298+
state.parenthesisCount === (oldParenthesisCount + 1)) {
3299+
if (node.type === astNodeTypes.Identifier) {
3300+
list = reinterpretAsCoverFormalsList([ node ]);
3301+
} else if (node.type === astNodeTypes.AssignmentExpression) {
3302+
list = reinterpretAsCoverFormalsList([ node ]);
3303+
} else if (node.type === astNodeTypes.SequenceExpression) {
3304+
list = reinterpretAsCoverFormalsList(node.expressions);
3305+
} else if (node === PlaceHolders.ArrowParameterPlaceHolder) {
3306+
list = reinterpretAsCoverFormalsList([]);
3307+
}
3308+
if (list) {
3309+
return parseArrowFunctionExpression(list, marker);
3310+
}
3311+
}
3312+
}
3313+
31923314
if (matchAssign()) {
31933315
// LeftHandSideExpression
31943316
if (!isLeftHandSide(left)) {
@@ -3939,7 +4061,7 @@ function parseStatement() {
39394061

39404062
function parseFunctionSourceElements() {
39414063
var sourceElement, sourceElements = [], token, directive, firstRestricted,
3942-
oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody,
4064+
oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody, oldParenthesisCount,
39434065
marker = markerCreate();
39444066

39454067
expect("{");
@@ -3973,6 +4095,7 @@ function parseFunctionSourceElements() {
39734095
oldInIteration = state.inIteration;
39744096
oldInSwitch = state.inSwitch;
39754097
oldInFunctionBody = state.inFunctionBody;
4098+
oldParenthesisCount = state.parenthesizedCount;
39764099

39774100
state.labelSet = {};
39784101
state.inIteration = false;
@@ -4000,10 +4123,37 @@ function parseFunctionSourceElements() {
40004123
state.inIteration = oldInIteration;
40014124
state.inSwitch = oldInSwitch;
40024125
state.inFunctionBody = oldInFunctionBody;
4126+
state.parenthesizedCount = oldParenthesisCount;
40034127

40044128
return markerApply(marker, astNodeFactory.createBlockStatement(sourceElements));
40054129
}
40064130

4131+
function validateParam(options, param, name) {
4132+
var key = "$" + name;
4133+
if (strict) {
4134+
if (syntax.isRestrictedWord(name)) {
4135+
options.stricted = param;
4136+
options.message = Messages.StrictParamName;
4137+
}
4138+
if (Object.prototype.hasOwnProperty.call(options.paramSet, key)) {
4139+
options.stricted = param;
4140+
options.message = Messages.StrictParamDupe;
4141+
}
4142+
} else if (!options.firstRestricted) {
4143+
if (syntax.isRestrictedWord(name)) {
4144+
options.firstRestricted = param;
4145+
options.message = Messages.StrictParamName;
4146+
} else if (syntax.isStrictModeReservedWord(name)) {
4147+
options.firstRestricted = param;
4148+
options.message = Messages.StrictReservedWord;
4149+
} else if (Object.prototype.hasOwnProperty.call(options.paramSet, key)) {
4150+
options.firstRestricted = param;
4151+
options.message = Messages.StrictParamDupe;
4152+
}
4153+
}
4154+
options.paramSet[key] = true;
4155+
}
4156+
40074157
function parseParams(firstRestricted) {
40084158
var param, params = [], defaults = [], defaultCount = 0, token, stricted, paramSet, key, message, def,
40094159
allowDefaultParams = extra.ecmaFeatures.defaultParams;
@@ -4349,6 +4499,7 @@ function tokenize(code, options) {
43494499
state = {
43504500
allowIn: true,
43514501
labelSet: {},
4502+
parenthesisCount: 0,
43524503
inFunctionBody: false,
43534504
inIteration: false,
43544505
inSwitch: false,
@@ -4453,6 +4604,7 @@ function parse(code, options) {
44534604
state = {
44544605
allowIn: true,
44554606
labelSet: {},
4607+
parenthesisCount: 0,
44564608
inFunctionBody: false,
44574609
inIteration: false,
44584610
inSwitch: false,

lib/ast-node-factory.js

+23
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,29 @@ module.exports = {
5151
};
5252
},
5353

54+
/**
55+
* Create an Arrow Function Expression ASTNode
56+
* @param {ASTNode} params The function arguments
57+
* @param {ASTNode} defaults Any default arguments
58+
* @param {ASTNode} body The function body
59+
* @param {boolean} expression True if the arrow function is created via an expression.
60+
* Always false for declarations, but kept here to be in sync with
61+
* FunctionExpression objects.
62+
* @returns {ASTNode} An ASTNode representing the entire arrow function expression
63+
*/
64+
createArrowFunctionExpression: function (params, defaults, body, expression) {
65+
return {
66+
type: astNodeTypes.ArrowFunctionExpression,
67+
id: null,
68+
params: params,
69+
defaults: defaults,
70+
body: body,
71+
rest: null,
72+
generator: false,
73+
expression: expression
74+
};
75+
},
76+
5477
/**
5578
* Create an ASTNode representation of an assignment expression
5679
* @param {ASTNode} operator The assignment operator

lib/ast-node-types.js

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
module.exports = {
4141
AssignmentExpression: "AssignmentExpression",
4242
ArrayExpression: "ArrayExpression",
43+
ArrowFunctionExpression: "ArrowFunctionExpression",
4344
BlockStatement: "BlockStatement",
4445
BinaryExpression: "BinaryExpression",
4546
BreakStatement: "BreakStatement",

lib/features.js

+3
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@
4040

4141
module.exports = {
4242

43+
// enable parsing of arrow functions
44+
arrowFunctions: false,
45+
4346
// enable parsing of let and const
4447
blockBindings: true,
4548

tests/fixtures/ast/API.json

+1
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@
151151
"result": {
152152
"AssignmentExpression": "AssignmentExpression",
153153
"ArrayExpression": "ArrayExpression",
154+
"ArrowFunctionExpression": "ArrowFunctionExpression",
154155
"BlockStatement": "BlockStatement",
155156
"BinaryExpression": "BinaryExpression",
156157
"BreakStatement": "BreakStatement",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module.exports = {
2+
defaultParams: true,
3+
arrowFunctions: true
4+
};

0 commit comments

Comments
 (0)