Skip to content

Commit 0033586

Browse files
committed
Merge pull request #50 from xjamundx/arrow-func
New: Support for Arrow Functions
2 parents 1514359 + fb12aa0 commit 0033586

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(")");
@@ -2823,10 +2837,9 @@ function parsePrimaryExpression() {
28232837
peek();
28242838
} else if (type === Token.Template) {
28252839
return parseTemplateLiteral();
2826-
} else {
2840+
} else if (!expr) {
28272841
throwUnexpected(lex());
28282842
}
2829-
28302843
return markerApply(marker, expr);
28312844
}
28322845

@@ -3178,11 +3191,89 @@ function parseConditionalExpression() {
31783191
return expr;
31793192
}
31803193

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

31833273
function parseAssignmentExpression() {
3184-
var token, left, right, node,
3274+
var token, left, right, list, node, params,
31853275
marker,
3276+
oldParenthesisCount = state.parenthesisCount,
31863277
allowGenerators = extra.ecmaFeatures.generators;
31873278

31883279
// Note that 'yield' is treated as a keyword in strict mode, but a
@@ -3193,10 +3284,41 @@ function parseAssignmentExpression() {
31933284
}
31943285

31953286
marker = markerCreate();
3196-
token = lookahead;
31973287

3288+
if (match("(")) {
3289+
token = lookahead2();
3290+
if (token.value === ")" && token.type === Token.Punctuator) {
3291+
params = parseParams();
3292+
list = {
3293+
defaults: [],
3294+
params: params.params
3295+
};
3296+
return markerApply(marker, parseArrowFunctionExpression(list, marker));
3297+
}
3298+
}
3299+
3300+
// revert to the previous lookahead style object
3301+
token = lookahead;
31983302
node = left = parseConditionalExpression();
31993303

3304+
if (node === PlaceHolders.ArrowParameterPlaceHolder || match("=>")) {
3305+
if (state.parenthesisCount === oldParenthesisCount ||
3306+
state.parenthesisCount === (oldParenthesisCount + 1)) {
3307+
if (node.type === astNodeTypes.Identifier) {
3308+
list = reinterpretAsCoverFormalsList([ node ]);
3309+
} else if (node.type === astNodeTypes.AssignmentExpression) {
3310+
list = reinterpretAsCoverFormalsList([ node ]);
3311+
} else if (node.type === astNodeTypes.SequenceExpression) {
3312+
list = reinterpretAsCoverFormalsList(node.expressions);
3313+
} else if (node === PlaceHolders.ArrowParameterPlaceHolder) {
3314+
list = reinterpretAsCoverFormalsList([]);
3315+
}
3316+
if (list) {
3317+
return parseArrowFunctionExpression(list, marker);
3318+
}
3319+
}
3320+
}
3321+
32003322
if (matchAssign()) {
32013323
// LeftHandSideExpression
32023324
if (!isLeftHandSide(left)) {
@@ -3947,7 +4069,7 @@ function parseStatement() {
39474069

39484070
function parseFunctionSourceElements() {
39494071
var sourceElement, sourceElements = [], token, directive, firstRestricted,
3950-
oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody,
4072+
oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody, oldParenthesisCount,
39514073
marker = markerCreate();
39524074

39534075
expect("{");
@@ -3981,6 +4103,7 @@ function parseFunctionSourceElements() {
39814103
oldInIteration = state.inIteration;
39824104
oldInSwitch = state.inSwitch;
39834105
oldInFunctionBody = state.inFunctionBody;
4106+
oldParenthesisCount = state.parenthesizedCount;
39844107

39854108
state.labelSet = {};
39864109
state.inIteration = false;
@@ -4008,10 +4131,37 @@ function parseFunctionSourceElements() {
40084131
state.inIteration = oldInIteration;
40094132
state.inSwitch = oldInSwitch;
40104133
state.inFunctionBody = oldInFunctionBody;
4134+
state.parenthesizedCount = oldParenthesisCount;
40114135

40124136
return markerApply(marker, astNodeFactory.createBlockStatement(sourceElements));
40134137
}
40144138

4139+
function validateParam(options, param, name) {
4140+
var key = "$" + name;
4141+
if (strict) {
4142+
if (syntax.isRestrictedWord(name)) {
4143+
options.stricted = param;
4144+
options.message = Messages.StrictParamName;
4145+
}
4146+
if (Object.prototype.hasOwnProperty.call(options.paramSet, key)) {
4147+
options.stricted = param;
4148+
options.message = Messages.StrictParamDupe;
4149+
}
4150+
} else if (!options.firstRestricted) {
4151+
if (syntax.isRestrictedWord(name)) {
4152+
options.firstRestricted = param;
4153+
options.message = Messages.StrictParamName;
4154+
} else if (syntax.isStrictModeReservedWord(name)) {
4155+
options.firstRestricted = param;
4156+
options.message = Messages.StrictReservedWord;
4157+
} else if (Object.prototype.hasOwnProperty.call(options.paramSet, key)) {
4158+
options.firstRestricted = param;
4159+
options.message = Messages.StrictParamDupe;
4160+
}
4161+
}
4162+
options.paramSet[key] = true;
4163+
}
4164+
40154165
function parseParams(firstRestricted) {
40164166
var param, params = [], defaults = [], defaultCount = 0, token, stricted, paramSet, key, message, def,
40174167
allowDefaultParams = extra.ecmaFeatures.defaultParams;
@@ -4357,6 +4507,7 @@ function tokenize(code, options) {
43574507
state = {
43584508
allowIn: true,
43594509
labelSet: {},
4510+
parenthesisCount: 0,
43604511
inFunctionBody: false,
43614512
inIteration: false,
43624513
inSwitch: false,
@@ -4461,6 +4612,7 @@ function parse(code, options) {
44614612
state = {
44624613
allowIn: true,
44634614
labelSet: {},
4615+
parenthesisCount: 0,
44644616
inFunctionBody: false,
44654617
inIteration: false,
44664618
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)