From 71bb9d61230767be78e191acb2139ccf4538fa3d Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Sat, 13 Dec 2014 00:37:40 +1100 Subject: [PATCH] contextual async/await keywords - closes 6to5/6to5#157 --- acorn.js | 128 ++++----- test/tests-6to5-playground.js | 98 ++++--- test/tests-6to5.js | 486 ++++++++++++++++++++++++++++++++++ 3 files changed, 601 insertions(+), 111 deletions(-) diff --git a/acorn.js b/acorn.js index 5b7337aa6f..439d85aebc 100644 --- a/acorn.js +++ b/acorn.js @@ -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(); } diff --git a/test/tests-6to5-playground.js b/test/tests-6to5-playground.js index 85b016d2cc..d8076b886c 100644 --- a/test/tests-6to5-playground.js +++ b/test/tests-6to5-playground.js @@ -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, diff --git a/test/tests-6to5.js b/test/tests-6to5.js index 183ae8e6ef..757e67762d 100644 --- a/test/tests-6to5.js +++ b/test/tests-6to5.js @@ -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;', {