contextual async/await keywords - closes 6to5/6to5#157

This commit is contained in:
Sebastian McKenzie 2014-12-13 00:37:40 +11:00
parent 6623756312
commit 71bb9d6123
3 changed files with 601 additions and 111 deletions

128
acorn.js
View File

@ -169,9 +169,7 @@
if (options.strictMode) {
strict = true;
}
if (options.ecmaVersion >= 7) {
isKeyword = isEcma7Keyword;
} else if (options.ecmaVersion === 6) {
if (options.ecmaVersion >= 6) {
isKeyword = isEcma6Keyword;
} else {
isKeyword = isEcma5AndLessKeyword;
@ -390,7 +388,6 @@
var _class = {keyword: "class"}, _extends = {keyword: "extends", beforeExpr: true};
var _export = {keyword: "export"}, _import = {keyword: "import"};
var _yield = {keyword: "yield", beforeExpr: true};
var _async = {keyword: "async"}, _await = {keyword: "await", beforeExpr: true};
// The keywords that denote values.
@ -417,8 +414,7 @@
"void": {keyword: "void", prefix: true, beforeExpr: true},
"delete": {keyword: "delete", prefix: true, beforeExpr: true},
"class": _class, "extends": _extends,
"export": _export, "import": _import, "yield": _yield,
"await": _await, "async": _async};
"export": _export, "import": _import, "yield": _yield};
// Punctuation token types. Again, the `type` property is purely for debugging.
@ -556,8 +552,6 @@
var isEcma6Keyword = makePredicate(ecma6AndLessKeywords);
var isEcma7Keyword = makePredicate(ecma6AndLessKeywords + " async await");
var isKeyword = isEcma5AndLessKeyword;
// ## Character categories
@ -1962,7 +1956,6 @@
case _debugger: return parseDebuggerStatement(node);
case _do: return parseDoStatement(node);
case _for: return parseForStatement(node);
case _async: return parseAsync(node, true);
case _function: return parseFunctionStatement(node);
case _class: return parseClass(node, true);
case _if: return parseIfStatement(node);
@ -1985,9 +1978,18 @@
// Identifier node, we switch to interpreting it as a label.
default:
var maybeName = tokVal, expr = parseExpression();
if (starttype === _name && expr.type === "Identifier" && eat(_colon))
return parseLabeledStatement(node, maybeName, expr);
else return parseExpressionStatement(node, expr);
if (starttype === _name) {
if (expr.type === "FunctionExpression" && expr.async) {
expr.type = "FunctionDeclaration";
return expr;
} else if (expr.type === "Identifier") {
if (eat(_colon)) {
return parseLabeledStatement(node, maybeName, expr);
}
}
}
return parseExpressionStatement(node, expr);
}
}
@ -2071,42 +2073,12 @@
return parseFunction(node, true, false);
}
function parseAsync(node, isStatement) {
if (options.ecmaVersion < 7) {
unexpected();
}
next();
switch (tokType) {
case _function:
next();
return parseFunction(node, isStatement, true);
if (!isStatement) unexpected();
case _name:
var id = parseIdent(tokType !== _name);
if (eat(_arrow)) {
return parseArrowExpression(node, [id], true);
}
case _parenL:
var oldParenL = ++metParenL;
var exprList = [];
next();
if (tokType !== _parenR) {
var val = parseExpression();
exprList = val.type === "SequenceExpression" ? val.expressions : [val];
}
expect(_parenR);
// if '=>' follows '(...)', convert contents to arguments
if (metParenL === oldParenL && eat(_arrow)) {
return parseArrowExpression(node, exprList, true);
}
default:
unexpected();
function eatAsync() {
if (tokType === _name && tokVal === "async") {
next();
return true;
} else {
return false;
}
}
@ -2539,14 +2511,53 @@
case _yield:
if (inGenerator) return parseYield();
case _await:
if (inAsync) return parseAwait();
case _name:
var start = storeCurrentPos();
var node = startNode();
var id = parseIdent(tokType !== _name);
if (options.ecmaVersion >= 7) {
// async functions!
if (id.name === "async") {
// arrow functions
if (tokType === _parenL) {
next();
var oldParenL = ++metParenL;
if (tokType !== _parenR) {
val = parseExpression();
exprList = val.type === "SequenceExpression" ? val.expressions : [val];
} else {
exprList = [];
}
expect(_parenR);
// if '=>' follows '(...)', convert contents to arguments
if (metParenL === oldParenL && eat(_arrow)) {
return parseArrowExpression(node, exprList, true);
} else {
node.callee = id;
node.arguments = exprList;
return parseSubscripts(finishNode(node, "CallExpression"), start);
}
} else if (tokType === _name) {
id = parseIdent();
if (eat(_arrow)) {
return parseArrowExpression(node, [id], true);
}
return id;
}
// normal functions
if (tokType === _function) {
next();
return parseFunction(node, false, true);
}
} else if (id.name === "await") {
if (inAsync) return parseAwait(node);
}
}
if (eat(_arrow)) {
return parseArrowExpression(startNodeAt(start), [id]);
return parseArrowExpression(node, [id]);
}
return id;
@ -2623,9 +2634,6 @@
case _braceL:
return parseObj();
case _async:
return parseAsync(startNode(), false);
case _function:
var node = startNode();
next();
@ -2727,7 +2735,7 @@
var prop = startNode(), isGenerator, isAsync;
if (options.ecmaVersion >= 7) {
isAsync = eat(_async);
isAsync = eatAsync();
if (isAsync && tokType === _star) unexpected();
}
if (options.ecmaVersion >= 6) {
@ -2953,7 +2961,7 @@
}
var isAsync = false;
if (options.ecmaVersion >= 7) {
isAsync = eat(_async);
isAsync = eatAsync();
if (isAsync && tokType === _star) unexpected();
}
var isGenerator = eat(_star);
@ -3024,7 +3032,7 @@
function parseExport(node) {
next();
// export var|const|let|function|class ...;
if (tokType === _var || tokType === _const || tokType === _let || tokType === _function || tokType === _class || tokType === _async) {
if (tokType === _var || tokType === _const || tokType === _let || tokType === _function || tokType === _class) {
node.declaration = parseStatement();
node['default'] = false;
node.specifiers = null;
@ -3170,9 +3178,7 @@
// Parses await expression inside async function.
function parseAwait() {
var node = startNode();
next();
function parseAwait(node) {
if (eat(_semi) || canInsertSemicolon()) {
unexpected();
}

View File

@ -115,7 +115,6 @@ test("class Foo { memo bar() {} }", {
defaults: [],
rest: null,
generator: false,
async: false,
body: {
type: "BlockStatement",
start: 23,
@ -178,7 +177,6 @@ test("var foo = { memo bar() {} };",
defaults: [],
rest: null,
generator: false,
async: false,
body: {
type: "BlockStatement",
start: 23,
@ -199,6 +197,54 @@ test("var foo = { memo bar() {} };",
// Memoization assignment operator
testFail("obj ?= 2;", "You can only use member expressions in memoization assignment (1:0)");
test("obj.x ?= 2;", {
type: "Program",
start: 0,
end: 11,
body: [{
type: "ExpressionStatement",
start: 0,
end: 11,
expression: {
type: "AssignmentExpression",
start: 0,
end: 10,
left: {
type: "MemberExpression",
start: 0,
end: 5,
object: {
type: "Identifier",
start: 0,
end: 3,
name: "obj"
},
property: {
type: "Identifier",
start: 4,
end: 5,
name: "x"
},
computed: false
},
right: {
type: "Literal",
start: 9,
end: 10,
value: 2,
raw: "2"
},
operator: "?="
}
}]
}, {
playground: true
});
// Method binding
//- Make sure conditionals still work
test("y ? 1 : 2", {
@ -287,54 +333,6 @@ test("y ? 1 : 2", {
playground: true
});
testFail("obj ?= 2;", "You can only use member expressions in memoization assignment (1:0)");
test("obj.x ?= 2;", {
type: "Program",
start: 0,
end: 11,
body: [{
type: "ExpressionStatement",
start: 0,
end: 11,
expression: {
type: "AssignmentExpression",
start: 0,
end: 10,
left: {
type: "MemberExpression",
start: 0,
end: 5,
object: {
type: "Identifier",
start: 0,
end: 3,
name: "obj"
},
property: {
type: "Identifier",
start: 4,
end: 5,
name: "x"
},
computed: false
},
right: {
type: "Literal",
start: 9,
end: 10,
value: 2,
raw: "2"
},
operator: "?="
}
}]
}, {
playground: true
});
// Method binding
test("var fn = obj:method", {
type: "Program",
start: 0,

View File

@ -1247,6 +1247,492 @@ test('f(async function(promise) { await promise })', {
locations: true
});
test('f(a, async(1, 2), b)', {
type: "Program",
body: [{
"type": "ExpressionStatement",
"expression": {
"type": "CallExpression",
"callee": {
"type": "Identifier",
"name": "f",
"range": [
0,
1
],
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 1
}
}
},
"arguments": [
{
"type": "Identifier",
"name": "a",
"range": [
2,
3
],
"loc": {
"start": {
"line": 1,
"column": 2
},
"end": {
"line": 1,
"column": 3
}
}
},
{
"type": "CallExpression",
"callee": {
"type": "Identifier",
"name": "async",
"range": [
5,
10
],
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 10
}
}
},
"arguments": [
{
"type": "Literal",
"value": 1,
"raw": "1",
"range": [
11,
12
],
"loc": {
"start": {
"line": 1,
"column": 11
},
"end": {
"line": 1,
"column": 12
}
}
},
{
"type": "Literal",
"value": 2,
"raw": "2",
"range": [
14,
15
],
"loc": {
"start": {
"line": 1,
"column": 14
},
"end": {
"line": 1,
"column": 15
}
}
}
],
"range": [
5,
16
],
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 16
}
}
},
{
"type": "Identifier",
"name": "b",
"range": [
18,
19
],
"loc": {
"start": {
"line": 1,
"column": 18
},
"end": {
"line": 1,
"column": 19
}
}
}
],
"range": [
0,
20
],
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 20
}
}
},
"range": [
0,
20
],
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 20
}
}
}]
}, {
ecmaVersion: 7,
locations: true,
ranges: true
});
test('var ok = async(x)', {
type: "Program",
body: [{
"type": "VariableDeclaration",
"declarations": [
{
"type": "VariableDeclarator",
"id": {
"type": "Identifier",
"name": "ok",
"range": [
4,
6
],
"loc": {
"start": {
"line": 1,
"column": 4
},
"end": {
"line": 1,
"column": 6
}
}
},
"init": {
"type": "CallExpression",
"callee": {
"type": "Identifier",
"name": "async",
"range": [
9,
14
],
"loc": {
"start": {
"line": 1,
"column": 9
},
"end": {
"line": 1,
"column": 14
}
}
},
"arguments": [
{
"type": "Identifier",
"name": "x",
"range": [
15,
16
],
"loc": {
"start": {
"line": 1,
"column": 15
},
"end": {
"line": 1,
"column": 16
}
}
}
],
"range": [
9,
17
],
"loc": {
"start": {
"line": 1,
"column": 9
},
"end": {
"line": 1,
"column": 17
}
}
},
"range": [
4,
17
],
"loc": {
"start": {
"line": 1,
"column": 4
},
"end": {
"line": 1,
"column": 17
}
}
}
],
"kind": "var",
"range": [
0,
17
],
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 17
}
}
}]
}, {
ecmaVersion: 7,
locations: true,
ranges: true
});
test('(function() { var async; async = 10 })', {
type: "Program",
body: [{
"type": "ExpressionStatement",
"expression": {
"type": "FunctionExpression",
"id": null,
"params": [],
"defaults": [],
"body": {
"type": "BlockStatement",
"body": [
{
"type": "VariableDeclaration",
"declarations": [
{
"type": "VariableDeclarator",
"id": {
"type": "Identifier",
"name": "async",
"range": [
18,
23
],
"loc": {
"start": {
"line": 1,
"column": 18
},
"end": {
"line": 1,
"column": 23
}
}
},
"init": null,
"range": [
18,
23
],
"loc": {
"start": {
"line": 1,
"column": 18
},
"end": {
"line": 1,
"column": 23
}
}
}
],
"kind": "var",
"range": [
14,
24
],
"loc": {
"start": {
"line": 1,
"column": 14
},
"end": {
"line": 1,
"column": 24
}
}
},
{
"type": "ExpressionStatement",
"expression": {
"type": "AssignmentExpression",
"operator": "=",
"left": {
"type": "Identifier",
"name": "async",
"range": [
25,
30
],
"loc": {
"start": {
"line": 1,
"column": 25
},
"end": {
"line": 1,
"column": 30
}
}
},
"right": {
"type": "Literal",
"value": 10,
"raw": "10",
"range": [
33,
35
],
"loc": {
"start": {
"line": 1,
"column": 33
},
"end": {
"line": 1,
"column": 35
}
}
},
"range": [
25,
35
],
"loc": {
"start": {
"line": 1,
"column": 25
},
"end": {
"line": 1,
"column": 35
}
}
},
"range": [
25,
35
],
"loc": {
"start": {
"line": 1,
"column": 25
},
"end": {
"line": 1,
"column": 35
}
}
}
],
"range": [
12,
37
],
"loc": {
"start": {
"line": 1,
"column": 12
},
"end": {
"line": 1,
"column": 37
}
}
},
"rest": null,
"generator": false,
"expression": false,
"range": [
1,
37
],
"loc": {
"start": {
"line": 1,
"column": 1
},
"end": {
"line": 1,
"column": 37
}
}
},
"range": [
0,
38
],
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 38
}
}
}]
}, {
ecmaVersion: 7,
locations: true,
ranges: true
});
// ES7: Abstract references
test('foo::bar;', {