From 0897901f1fe628fd9bd547f637b540266729d04f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 6 Jan 2015 11:03:50 +0100 Subject: [PATCH] Slight cleanup of '/' disambiguation Issue #189 --- acorn.js | 44 ++++++++++++++++++++++++++++++++------------ acorn_loose.js | 2 -- test/tests.js | 16 +++++++++++++++- 3 files changed, 47 insertions(+), 15 deletions(-) diff --git a/acorn.js b/acorn.js index 64b59ed259..b116315ada 100644 --- a/acorn.js +++ b/acorn.js @@ -596,7 +596,7 @@ Position.prototype.offset = function(n) { return new Position(this.line, this.column + n); - } + }; function curPosition() { return new Position(tokCurLine, tokPos - tokLineStart); @@ -613,6 +613,7 @@ tokCurLine = 1; tokPos = tokLineStart = 0; } + tokType = _eof; tokContext = []; tokExprAllowed = true; metParenL = 0; @@ -622,6 +623,26 @@ } } + // The algorithm used to determine whether a regexp can appear at a + // given point in the program is loosely based on sweet.js' approach. + // See https://github.com/mozilla/sweet.js/wiki/design + + var b_stat = {token: "{", isExpr: false}, b_expr = {token: "{", isExpr: true}; + var p_stat = {token: "(", isExpr: false}, p_expr = {token: "(", isExpr: true}; + + function braceIsBlock(prevType) { + var parent; + if (prevType === _colon && (parent = tokContext[tokContext.length - 1]).token == "{") + return !parent.isExpr; + if (prevType === _return) + return newline.test(input.slice(lastEnd, tokStart)); + if (prevType === _else || prevType === _semi || prevType === _eof) + return true; + if (prevType == _braceL) + return tokContext[tokContext.length - 1] === b_stat; + return !tokExprAllowed; + } + // Called at the end of every token. Sets `tokEnd`, `tokVal`, and // maintains `tokContext` and `tokExprAllowed`, and skips the space // after the token, so that the next one's `tokStart` will point at @@ -636,17 +657,16 @@ tokVal = val; // Update context info - if (type == _parenR || type == _braceR) { - tokExprAllowed = tokContext.pop(); - } else if (type == _braceL) { - var ctx = tokExprAllowed === false; - if (prevType == _return && newline.test(input.slice(lastEnd, tokStart))) { - console.log("fired"); - ctx = false; - } - tokContext.push(ctx); + if (type === _parenR || type === _braceR) { + var out = tokContext.pop(); + tokExprAllowed = !(out && out.isExpr); + } else if (type === _braceL) { + tokContext.push(braceIsBlock(prevType) ? b_stat : b_expr); + tokExprAllowed = true; } else if (type == _parenL) { - tokContext.push(prevType == _if || prevType == _for || prevType == _with || prevType == _while); + var statementParens = prevType === _if || prevType === _for || prevType === _with || prevType === _while; + tokContext.push(statementParens ? p_stat : p_expr); + tokExprAllowed = true; } else if (type == _incDec) { // tokExprAllowed stays unchanged } else if (type.keyword && prevType == _dot) { @@ -1089,7 +1109,7 @@ out += readEscapedChar(); } else { ++tokPos; - if (newline.test(String.fromCharCode(ch))) { + if (isNewLine(ch)) { raise(tokStart, "Unterminated string constant"); } out += String.fromCharCode(ch); // '\' diff --git a/acorn_loose.js b/acorn_loose.js index 3cd8e89f6b..8bc6dfda26 100644 --- a/acorn_loose.js +++ b/acorn_loose.js @@ -987,8 +987,6 @@ case "AssignmentExpression": if (node.operator === "=") node.type = "AssignmentPattern"; - else - unexpected(node.left.end); break; } } diff --git a/test/tests.js b/test/tests.js index 105a7255b2..172eb1fb87 100644 --- a/test/tests.js +++ b/test/tests.js @@ -26687,7 +26687,21 @@ test("a.in / b", { ] }); -test("return {}\n/foo/", {}, {allowReturnOutsideFunction: true}); +// A number of slash-disambiguation corner cases +test("return {} / 2", {}, {allowReturnOutsideFunction: true}); +test("return\n{}\n/foo/", {}, {allowReturnOutsideFunction: true}); +test("+{} / 2", {}); +test("{}\n/foo/", {}); +test("x++\n{}\n/foo/", {}); +test("{{}\n/foo/}", {}); +test("while (1) /foo/", {}); +test("(1) / 2", {}); +test("({a: [1]}+[]) / 2", {}); +test("{[1]}\n/foo/", {}); +test("switch(a) { case 1: {}\n/foo/ }", {}); +test("({1: {} / 2})", {}); +test("+x++ / 2", {}); +test("foo.in\n{}\n/foo/", {}); test("{}/=/", { type: "Program",