From 53422ccc6f94b180cbbf278778006ac69ad7cd8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Dessoude?= Date: Tue, 24 Sep 2019 11:38:47 +0200 Subject: [PATCH 1/9] WIP: update version --- packages/java-parser/package.json | 2 +- .../src/productions/blocks-and-statements.js | 4 +- .../java-parser/src/productions/classes.js | 4 +- .../src/productions/expressions.js | 46 ++++++++++++------- .../java-parser/src/productions/interfaces.js | 12 ++--- .../src/productions/packages-and-modules.js | 8 +++- yarn.lock | 8 ++-- 7 files changed, 50 insertions(+), 34 deletions(-) diff --git a/packages/java-parser/package.json b/packages/java-parser/package.json index 44d610ec..f241e593 100644 --- a/packages/java-parser/package.json +++ b/packages/java-parser/package.json @@ -6,7 +6,7 @@ "repository": "https://github.com/jhipster/prettier-java/tree/master/packages/java-parser", "license": "Apache-2.0", "dependencies": { - "chevrotain": "4.7.0", + "chevrotain": "6.5.0", "lodash": "4.17.15" }, "scripts": { diff --git a/packages/java-parser/src/productions/blocks-and-statements.js b/packages/java-parser/src/productions/blocks-and-statements.js index 6d775da0..ce23886a 100644 --- a/packages/java-parser/src/productions/blocks-and-statements.js +++ b/packages/java-parser/src/productions/blocks-and-statements.js @@ -25,8 +25,8 @@ function defineRules($, t) { // https://docs.oracle.com/javase/specs/jls/se11/html/jls-14.html#jls-BlockStatement $.RULE("blockStatement", () => { - const isLocalVariableDeclaration = this.BACKTRACK_LOOKAHEAD( - $.isLocalVariableDeclaration + const isLocalVariableDeclaration = $.ACTION(() => + this.BACKTRACK_LOOKAHEAD($.isLocalVariableDeclaration) ); $.OR([ { diff --git a/packages/java-parser/src/productions/classes.js b/packages/java-parser/src/productions/classes.js index 0b2a5662..b13400fe 100644 --- a/packages/java-parser/src/productions/classes.js +++ b/packages/java-parser/src/productions/classes.js @@ -108,8 +108,8 @@ function defineRules($, t) { // https://docs.oracle.com/javase/specs/jls/se11/html/jls-8.html#jls-ClassBodyDeclaration $.RULE("classBodyDeclaration", () => { - const nextRuleType = $.BACKTRACK_LOOKAHEAD( - $.identifyClassBodyDeclarationType + const nextRuleType = $.ACTION(() => + $.BACKTRACK_LOOKAHEAD($.identifyClassBodyDeclarationType) ); $.OR([ diff --git a/packages/java-parser/src/productions/expressions.js b/packages/java-parser/src/productions/expressions.js index bbc40e7a..b1034cbc 100644 --- a/packages/java-parser/src/productions/expressions.js +++ b/packages/java-parser/src/productions/expressions.js @@ -203,10 +203,14 @@ function defineRules($, t) { }); $.RULE("primaryPrefix", () => { - let isCastExpression = false; - if (tokenMatcher($.LA(1).tokenType, t.LBrace)) { - isCastExpression = this.BACKTRACK_LOOKAHEAD($.isCastExpression); - } + const isCastExpression = $.ACTION(() => { + let isCastExpression = false; + if (tokenMatcher($.LA(1).tokenType, t.LBrace)) { + isCastExpression = this.BACKTRACK_LOOKAHEAD($.isCastExpression); + } + + return isCastExpression; + }); $.OR([ { ALT: () => $.SUBRULE($.literal) }, @@ -305,14 +309,18 @@ function defineRules($, t) { { ALT: () => $.CONSUME(t.Super) } ]); - let isRefTypeInMethodRef = false; - // Performance optimization, only perform this backtracking when a '<' is found - // TODO: performance optimization evaluation: avoid doing this backtracking for every "<" encountered. - // we could do it once (using global state) per "fqnOrRefType" - // We could do it only once for - if (tokenMatcher($.LA(1).tokenType, t.Less)) { - isRefTypeInMethodRef = this.BACKTRACK_LOOKAHEAD($.isRefTypeInMethodRef); - } + const isRefTypeInMethodRef = $.ACTION(() => { + let isRefTypeInMethodRef = false; + // Performance optimization, only perform this backtracking when a '<' is found + // TODO: performance optimization evaluation: avoid doing this backtracking for every "<" encountered. + // we could do it once (using global state) per "fqnOrRefType" + // We could do it only once for + if (tokenMatcher($.LA(1).tokenType, t.Less)) { + isRefTypeInMethodRef = this.BACKTRACK_LOOKAHEAD($.isRefTypeInMethodRef); + } + + return isRefTypeInMethodRef; + }); $.OPTION2({ NAME: "$classTypeArguments", @@ -373,7 +381,9 @@ function defineRules($, t) { unqualifiedClassInstanceCreationExpression: 2 }; $.RULE("newExpression", () => { - const type = this.BACKTRACK_LOOKAHEAD($.identifyNewExpressionType); + const type = $.ACTION(() => + this.BACKTRACK_LOOKAHEAD($.identifyNewExpressionType) + ); $.OR([ { @@ -590,10 +600,12 @@ function defineRules($, t) { }); $.RULE("isCastExpression", () => { - if (this.BACKTRACK_LOOKAHEAD($.isPrimitiveCastExpression)) { - return true; - } - return this.BACKTRACK_LOOKAHEAD($.isReferenceTypeCastExpression); + return $.ACTION(() => { + if (this.BACKTRACK_LOOKAHEAD($.isPrimitiveCastExpression)) { + return true; + } + return this.BACKTRACK_LOOKAHEAD($.isReferenceTypeCastExpression); + }); }); $.RULE("isPrimitiveCastExpression", () => { diff --git a/packages/java-parser/src/productions/interfaces.js b/packages/java-parser/src/productions/interfaces.js index 0c9133c9..d1210939 100644 --- a/packages/java-parser/src/productions/interfaces.js +++ b/packages/java-parser/src/productions/interfaces.js @@ -68,8 +68,8 @@ function defineRules($, t) { // https://docs.oracle.com/javase/specs/jls/se11/html/jls-9.html#jls-InterfaceMemberDeclaration $.RULE("interfaceMemberDeclaration", () => { - const detectedType = this.BACKTRACK_LOOKAHEAD( - $.identifyInterfaceBodyDeclarationType + const detectedType = $.ACTION(() => + this.BACKTRACK_LOOKAHEAD($.identifyInterfaceBodyDeclarationType) ); $.OR([ { @@ -167,8 +167,8 @@ function defineRules($, t) { // https://docs.oracle.com/javase/specs/jls/se11/html/jls-9.html#jls-InterfaceMemberDeclaration $.RULE("annotationTypeMemberDeclaration", () => { - const detectedType = this.BACKTRACK_LOOKAHEAD( - $.identifyAnnotationBodyDeclarationType + const detectedType = $.ACTION(() => + this.BACKTRACK_LOOKAHEAD($.identifyAnnotationBodyDeclarationType) ); $.OR([ { @@ -271,8 +271,8 @@ function defineRules($, t) { // https://docs.oracle.com/javase/specs/jls/se11/html/jls-9.html#jls-ElementValue $.RULE("elementValue", () => { - const isSimpleElementValueAnnotation = this.BACKTRACK_LOOKAHEAD( - $.isSimpleElementValueAnnotation + const isSimpleElementValueAnnotation = $.ACTION(() => + this.BACKTRACK_LOOKAHEAD($.isSimpleElementValueAnnotation) ); $.OR([ // Spec Deviation: "conditionalExpression" replaced with "expression" diff --git a/packages/java-parser/src/productions/packages-and-modules.js b/packages/java-parser/src/productions/packages-and-modules.js index 99b61bd0..b048f009 100644 --- a/packages/java-parser/src/productions/packages-and-modules.js +++ b/packages/java-parser/src/productions/packages-and-modules.js @@ -5,7 +5,9 @@ function defineRules($, t) { // https://docs.oracle.com/javase/specs/jls/se11/html/jls-7.html#CompilationUnit $.RULE("compilationUnit", () => { // custom optimized backtracking lookahead logic - const isModule = $.BACKTRACK_LOOKAHEAD($.isModuleCompilationUnit); + const isModule = $.ACTION(() => + $.BACKTRACK_LOOKAHEAD($.isModuleCompilationUnit) + ); $.OR([ { GATE: () => isModule === false, @@ -97,7 +99,9 @@ function defineRules($, t) { // https://docs.oracle.com/javase/specs/jls/se11/html/jls-7.html#jls-TypeDeclaration $.RULE("typeDeclaration", () => { // TODO: consider extracting the prefix modifiers here to avoid backtracking - const isClassDeclaration = this.BACKTRACK_LOOKAHEAD($.isClassDeclaration); + const isClassDeclaration = $.ACTION(() => + this.BACKTRACK_LOOKAHEAD($.isClassDeclaration) + ); $.OR([ { GATE: () => isClassDeclaration, diff --git a/yarn.lock b/yarn.lock index e653f8a4..b1b52ee9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1517,10 +1517,10 @@ check-error@^1.0.2: resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= -chevrotain@4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/chevrotain/-/chevrotain-4.7.0.tgz#1a1acab4275cee8b1e208e6a10b1622c5b92b02e" - integrity sha512-FmXTi1hMN/6TT9NlrYP1kHHW6P7qpVzQD2vGrbsfI7wbFXXg2ANpIaN3pHPnVx/ZPHOnConE0TmyP6rguyzkZQ== +chevrotain@6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/chevrotain/-/chevrotain-6.5.0.tgz#dcbef415516b0af80fd423cc0d96b28d3f11374e" + integrity sha512-BwqQ/AgmKJ8jcMEjaSnfMybnKMgGTrtDKowfTP3pX4jwVy0kNjRsT/AP6h+wC3+3NC+X8X15VWBnTCQlX+wQFg== dependencies: regexp-to-ast "0.4.0" From a0cd147fbefbb30120f1f146aa3ce3c5a47389bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Dessoude?= Date: Tue, 24 Sep 2019 15:13:01 +0200 Subject: [PATCH 2/9] make tests pass --- .../java-parser/src/productions/classes.js | 27 +++++------- .../src/productions/expressions.js | 41 +++++++++---------- .../src/productions/packages-and-modules.js | 41 +++++++++---------- 3 files changed, 50 insertions(+), 59 deletions(-) diff --git a/packages/java-parser/src/productions/classes.js b/packages/java-parser/src/productions/classes.js index b13400fe..e5d46700 100644 --- a/packages/java-parser/src/productions/classes.js +++ b/packages/java-parser/src/productions/classes.js @@ -1,6 +1,6 @@ "use strict"; -const { isRecognitionException, tokenMatcher } = require("chevrotain"); +const { tokenMatcher } = require("chevrotain"); function defineRules($, t) { // https://docs.oracle.com/javase/specs/jls/se11/html/jls-8.html#jls-ClassDeclaration @@ -662,24 +662,19 @@ function defineRules($, t) { return false; } - try { - // The {classModifier} is a super grammar of the "interfaceModifier" - // So we must parse all the "{classModifier}" before we can distinguish - // between the alternatives. - $.MANY({ - GATE: () => - (tokenMatcher($.LA(1).tokenType, t.At) && - tokenMatcher($.LA(2).tokenType, t.Interface)) === false, - DEF: () => { + let continueLoop = true; + while (continueLoop) { + if ( + (tokenMatcher($.LA(1).tokenType, t.At) && + tokenMatcher($.LA(2).tokenType, t.Interface)) === false + ) { + try { $.SUBRULE($.classModifier); + } catch (e) { + continueLoop = false; } - }); - } catch (e) { - if (isRecognitionException(e)) { - // TODO: add original syntax error? - throw "Cannot Identify if the is a or an "; } else { - throw e; + continueLoop = false; } } diff --git a/packages/java-parser/src/productions/expressions.js b/packages/java-parser/src/productions/expressions.js index b1034cbc..d966a555 100644 --- a/packages/java-parser/src/productions/expressions.js +++ b/packages/java-parser/src/productions/expressions.js @@ -600,12 +600,10 @@ function defineRules($, t) { }); $.RULE("isCastExpression", () => { - return $.ACTION(() => { - if (this.BACKTRACK_LOOKAHEAD($.isPrimitiveCastExpression)) { - return true; - } - return this.BACKTRACK_LOOKAHEAD($.isReferenceTypeCastExpression); - }); + if (this.BACKTRACK_LOOKAHEAD($.isPrimitiveCastExpression)) { + return true; + } + return this.BACKTRACK_LOOKAHEAD($.isReferenceTypeCastExpression); }); $.RULE("isPrimitiveCastExpression", () => { @@ -616,21 +614,19 @@ function defineRules($, t) { return true; }); - let firstForUnaryExpressionNotPlusMinus = undefined; $.RULE("isReferenceTypeCastExpression", () => { - if (firstForUnaryExpressionNotPlusMinus === undefined) { - const firstUnaryExpressionNotPlusMinus = this.computeContentAssist( - "unaryExpressionNotPlusMinus", - [] - ); - const nextTokTypes = firstUnaryExpressionNotPlusMinus.map( - x => x.nextTokenType - ); - // uniq - firstForUnaryExpressionNotPlusMinus = nextTokTypes.filter( - (v, i, a) => a.indexOf(v) === i - ); - } + const firstUnaryExpressionNotPlusMinus = this.computeContentAssist( + "unaryExpressionNotPlusMinus", + [] + ); + const nextTokTypes = firstUnaryExpressionNotPlusMinus.map( + x => x.nextTokenType + ); + // uniq + const firstForUnaryExpressionNotPlusMinus = nextTokTypes.filter( + (v, i, a) => a.indexOf(v) === i + ); + $.CONSUME(t.LBrace); $.SUBRULE($.referenceType); $.MANY(() => { @@ -665,10 +661,11 @@ function defineRules($, t) { } // in the middle of a "classReferenceType" - $.OPTION2(() => { + try { $.CONSUME(t.Dot); $.SUBRULE($.classOrInterfaceType); - }); + // eslint-disable-next-line no-empty + } catch (e) {} const firstTokTypeAfterRefType = this.LA(1).tokenType; return tokenMatcher(firstTokTypeAfterRefType, t.ColonColon); diff --git a/packages/java-parser/src/productions/packages-and-modules.js b/packages/java-parser/src/productions/packages-and-modules.js index b048f009..11e87cdb 100644 --- a/packages/java-parser/src/productions/packages-and-modules.js +++ b/packages/java-parser/src/productions/packages-and-modules.js @@ -1,5 +1,5 @@ "use strict"; -const { isRecognitionException, tokenMatcher, EOF } = require("chevrotain"); +const { tokenMatcher, EOF } = require("chevrotain"); function defineRules($, t) { // https://docs.oracle.com/javase/specs/jls/se11/html/jls-7.html#CompilationUnit @@ -222,31 +222,30 @@ function defineRules($, t) { return false; }); - try { - // the "{importDeclaration}" is a common prefix - $.MANY(() => { + let continueLoop1 = true; + while (continueLoop1) { + if ( + tokenMatcher($.LA(1).tokenType, t.Semicolon) || + tokenMatcher($.LA(1).tokenType, t.Import) + ) { $.SUBRULE2($.importDeclaration); - }); + } else { + continueLoop1 = false; + } + } - $.MANY2({ - // To avoid ambiguity with @interface ("AnnotationTypeDeclaration" vs "Annotaion") - GATE: () => - (tokenMatcher($.LA(1).tokenType, t.At) && - tokenMatcher($.LA(2).tokenType, t.Interface)) === false, - DEF: () => { - $.SUBRULE($.annotation); - } - }); - } catch (e) { - // This means we had a syntax error in the imports or annotations - // So we can't keep parsing deep enough to make the decision - if (isRecognitionException(e)) { - // TODO: add original syntax error? - throw "Cannot Identify if the source code is an OrdinaryCompilationUnit or ModularCompilationUnit"; + let continueLoop2 = true; + while (continueLoop2) { + if ( + tokenMatcher($.LA(1).tokenType, t.At) && + !tokenMatcher($.LA(2).tokenType, t.Interface) + ) { + $.SUBRULE($.annotation); } else { - throw e; + continueLoop2 = false; } } + const nextTokenType = this.LA(1).tokenType; return ( tokenMatcher(nextTokenType, t.Open) || From 40e81a14074d3f8ae2e1a7793304a7b507ce0e5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Dessoude?= Date: Tue, 24 Sep 2019 15:33:49 +0200 Subject: [PATCH 3/9] update printer with following Chevrotain update --- packages/prettier-plugin-java/src/printers/expressions.js | 2 +- packages/prettier-plugin-java/src/printers/prettier-builder.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/prettier-plugin-java/src/printers/expressions.js b/packages/prettier-plugin-java/src/printers/expressions.js index 07c03b56..ee7004a8 100644 --- a/packages/prettier-plugin-java/src/printers/expressions.js +++ b/packages/prettier-plugin-java/src/printers/expressions.js @@ -170,7 +170,7 @@ class ExpressionsPrettierVisitor { for (let i = 0; i < subgroup.length; i++) { const token = subgroup[i]; const shiftOperator = isShiftOperator(subgroup, i); - if (token.tokenType.tokenName === "Instanceof") { + if (token.tokenType.name === "Instanceof") { currentSegment.push( rejectAndJoin(" ", [ctx.Instanceof[0], referenceType.shift()]) ); diff --git a/packages/prettier-plugin-java/src/printers/prettier-builder.js b/packages/prettier-plugin-java/src/printers/prettier-builder.js index 97baec11..e9bcc567 100644 --- a/packages/prettier-plugin-java/src/printers/prettier-builder.js +++ b/packages/prettier-plugin-java/src/printers/prettier-builder.js @@ -25,7 +25,7 @@ function getImageWithComments(token) { if (element.startLine !== token.startLine) { arr.push(concat(formatComment(element))); arr.push(hardLineWithoutBreakParent); - } else if (element.tokenType.tokenName === "LineComment") { + } else if (element.tokenType.name === "LineComment") { // Do not add extra space in case of empty statement const separator = token.image === "" ? "" : " "; arr.push( From c3d349f20d087ab8f08bad1a05a8816f25b9ed2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Dessoude?= Date: Wed, 25 Sep 2019 16:20:22 +0200 Subject: [PATCH 4/9] only wrap BACKTRACK_LOOKAHEAD in ACTION --- packages/java-parser/src/parser.js | 34 +++++++++--------- .../src/productions/blocks-and-statements.js | 5 +-- .../java-parser/src/productions/classes.js | 4 +-- .../src/productions/expressions.js | 36 +++++++------------ .../java-parser/src/productions/interfaces.js | 15 ++++---- .../src/productions/packages-and-modules.js | 10 +++--- 6 files changed, 49 insertions(+), 55 deletions(-) diff --git a/packages/java-parser/src/parser.js b/packages/java-parser/src/parser.js index 93c757fd..6bb76602 100644 --- a/packages/java-parser/src/parser.js +++ b/packages/java-parser/src/parser.js @@ -172,23 +172,25 @@ class JavaParser extends Parser { } BACKTRACK_LOOKAHEAD(production, errValue = false) { - this.isBackTrackingStack.push(1); - // TODO: "saveRecogState" does not handle the occurrence stack - const orgState = this.saveRecogState(); - try { - // hack to enable outputting none CST values from grammar rules. - this.outputCst = false; - return production.call(this); - } catch (e) { - if (isRecognitionException(e)) { - return errValue; + return this.ACTION(() => { + this.isBackTrackingStack.push(1); + // TODO: "saveRecogState" does not handle the occurrence stack + const orgState = this.saveRecogState(); + try { + // hack to enable outputting none CST values from grammar rules. + this.outputCst = false; + return production.call(this); + } catch (e) { + if (isRecognitionException(e)) { + return errValue; + } + throw e; + } finally { + this.outputCst = true; + this.reloadRecogState(orgState); + this.isBackTrackingStack.pop(); } - throw e; - } finally { - this.outputCst = true; - this.reloadRecogState(orgState); - this.isBackTrackingStack.pop(); - } + }); } setIgnoredComments(comments) { diff --git a/packages/java-parser/src/productions/blocks-and-statements.js b/packages/java-parser/src/productions/blocks-and-statements.js index ce23886a..baacea97 100644 --- a/packages/java-parser/src/productions/blocks-and-statements.js +++ b/packages/java-parser/src/productions/blocks-and-statements.js @@ -25,9 +25,10 @@ function defineRules($, t) { // https://docs.oracle.com/javase/specs/jls/se11/html/jls-14.html#jls-BlockStatement $.RULE("blockStatement", () => { - const isLocalVariableDeclaration = $.ACTION(() => - this.BACKTRACK_LOOKAHEAD($.isLocalVariableDeclaration) + const isLocalVariableDeclaration = this.BACKTRACK_LOOKAHEAD( + $.isLocalVariableDeclaration ); + $.OR([ { GATE: () => isLocalVariableDeclaration, diff --git a/packages/java-parser/src/productions/classes.js b/packages/java-parser/src/productions/classes.js index e5d46700..0d548be9 100644 --- a/packages/java-parser/src/productions/classes.js +++ b/packages/java-parser/src/productions/classes.js @@ -108,8 +108,8 @@ function defineRules($, t) { // https://docs.oracle.com/javase/specs/jls/se11/html/jls-8.html#jls-ClassBodyDeclaration $.RULE("classBodyDeclaration", () => { - const nextRuleType = $.ACTION(() => - $.BACKTRACK_LOOKAHEAD($.identifyClassBodyDeclarationType) + const nextRuleType = $.BACKTRACK_LOOKAHEAD( + $.identifyClassBodyDeclarationType ); $.OR([ diff --git a/packages/java-parser/src/productions/expressions.js b/packages/java-parser/src/productions/expressions.js index d966a555..8c1aabea 100644 --- a/packages/java-parser/src/productions/expressions.js +++ b/packages/java-parser/src/productions/expressions.js @@ -203,14 +203,10 @@ function defineRules($, t) { }); $.RULE("primaryPrefix", () => { - const isCastExpression = $.ACTION(() => { - let isCastExpression = false; - if (tokenMatcher($.LA(1).tokenType, t.LBrace)) { - isCastExpression = this.BACKTRACK_LOOKAHEAD($.isCastExpression); - } - - return isCastExpression; - }); + let isCastExpression = false; + if (tokenMatcher($.LA(1).tokenType, t.LBrace)) { + isCastExpression = this.BACKTRACK_LOOKAHEAD($.isCastExpression); + } $.OR([ { ALT: () => $.SUBRULE($.literal) }, @@ -309,18 +305,14 @@ function defineRules($, t) { { ALT: () => $.CONSUME(t.Super) } ]); - const isRefTypeInMethodRef = $.ACTION(() => { - let isRefTypeInMethodRef = false; - // Performance optimization, only perform this backtracking when a '<' is found - // TODO: performance optimization evaluation: avoid doing this backtracking for every "<" encountered. - // we could do it once (using global state) per "fqnOrRefType" - // We could do it only once for - if (tokenMatcher($.LA(1).tokenType, t.Less)) { - isRefTypeInMethodRef = this.BACKTRACK_LOOKAHEAD($.isRefTypeInMethodRef); - } - - return isRefTypeInMethodRef; - }); + let isRefTypeInMethodRef = false; + // Performance optimization, only perform this backtracking when a '<' is found + // TODO: performance optimization evaluation: avoid doing this backtracking for every "<" encountered. + // we could do it once (using global state) per "fqnOrRefType" + // We could do it only once for + if (tokenMatcher($.LA(1).tokenType, t.Less)) { + isRefTypeInMethodRef = this.BACKTRACK_LOOKAHEAD($.isRefTypeInMethodRef); + } $.OPTION2({ NAME: "$classTypeArguments", @@ -381,9 +373,7 @@ function defineRules($, t) { unqualifiedClassInstanceCreationExpression: 2 }; $.RULE("newExpression", () => { - const type = $.ACTION(() => - this.BACKTRACK_LOOKAHEAD($.identifyNewExpressionType) - ); + const type = this.BACKTRACK_LOOKAHEAD($.identifyNewExpressionType); $.OR([ { diff --git a/packages/java-parser/src/productions/interfaces.js b/packages/java-parser/src/productions/interfaces.js index d1210939..989ebdab 100644 --- a/packages/java-parser/src/productions/interfaces.js +++ b/packages/java-parser/src/productions/interfaces.js @@ -68,9 +68,10 @@ function defineRules($, t) { // https://docs.oracle.com/javase/specs/jls/se11/html/jls-9.html#jls-InterfaceMemberDeclaration $.RULE("interfaceMemberDeclaration", () => { - const detectedType = $.ACTION(() => - this.BACKTRACK_LOOKAHEAD($.identifyInterfaceBodyDeclarationType) + const detectedType = this.BACKTRACK_LOOKAHEAD( + $.identifyInterfaceBodyDeclarationType ); + $.OR([ { GATE: () => detectedType === InterfaceBodyTypes.constantDeclaration, @@ -167,9 +168,10 @@ function defineRules($, t) { // https://docs.oracle.com/javase/specs/jls/se11/html/jls-9.html#jls-InterfaceMemberDeclaration $.RULE("annotationTypeMemberDeclaration", () => { - const detectedType = $.ACTION(() => - this.BACKTRACK_LOOKAHEAD($.identifyAnnotationBodyDeclarationType) + const detectedType = this.BACKTRACK_LOOKAHEAD( + $.identifyAnnotationBodyDeclarationType ); + $.OR([ { GATE: () => @@ -271,9 +273,10 @@ function defineRules($, t) { // https://docs.oracle.com/javase/specs/jls/se11/html/jls-9.html#jls-ElementValue $.RULE("elementValue", () => { - const isSimpleElementValueAnnotation = $.ACTION(() => - this.BACKTRACK_LOOKAHEAD($.isSimpleElementValueAnnotation) + const isSimpleElementValueAnnotation = this.BACKTRACK_LOOKAHEAD( + $.isSimpleElementValueAnnotation ); + $.OR([ // Spec Deviation: "conditionalExpression" replaced with "expression" // Because we cannot differentiate between the two using fixed lookahead. diff --git a/packages/java-parser/src/productions/packages-and-modules.js b/packages/java-parser/src/productions/packages-and-modules.js index 11e87cdb..0f3d8af1 100644 --- a/packages/java-parser/src/productions/packages-and-modules.js +++ b/packages/java-parser/src/productions/packages-and-modules.js @@ -5,9 +5,8 @@ function defineRules($, t) { // https://docs.oracle.com/javase/specs/jls/se11/html/jls-7.html#CompilationUnit $.RULE("compilationUnit", () => { // custom optimized backtracking lookahead logic - const isModule = $.ACTION(() => - $.BACKTRACK_LOOKAHEAD($.isModuleCompilationUnit) - ); + const isModule = $.BACKTRACK_LOOKAHEAD($.isModuleCompilationUnit); + $.OR([ { GATE: () => isModule === false, @@ -99,9 +98,8 @@ function defineRules($, t) { // https://docs.oracle.com/javase/specs/jls/se11/html/jls-7.html#jls-TypeDeclaration $.RULE("typeDeclaration", () => { // TODO: consider extracting the prefix modifiers here to avoid backtracking - const isClassDeclaration = $.ACTION(() => - this.BACKTRACK_LOOKAHEAD($.isClassDeclaration) - ); + const isClassDeclaration = this.BACKTRACK_LOOKAHEAD($.isClassDeclaration); + $.OR([ { GATE: () => isClassDeclaration, From 43dbd200dd32494aaa6835ab0a7b5e2c5ff69d11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Dessoude?= Date: Wed, 25 Sep 2019 16:43:21 +0200 Subject: [PATCH 5/9] revert changes on isModuleCompilationUnit --- .../src/productions/packages-and-modules.js | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/packages/java-parser/src/productions/packages-and-modules.js b/packages/java-parser/src/productions/packages-and-modules.js index 0f3d8af1..69118b26 100644 --- a/packages/java-parser/src/productions/packages-and-modules.js +++ b/packages/java-parser/src/productions/packages-and-modules.js @@ -1,5 +1,5 @@ "use strict"; -const { tokenMatcher, EOF } = require("chevrotain"); +const { isRecognitionException, tokenMatcher, EOF } = require("chevrotain"); function defineRules($, t) { // https://docs.oracle.com/javase/specs/jls/se11/html/jls-7.html#CompilationUnit @@ -220,27 +220,29 @@ function defineRules($, t) { return false; }); - let continueLoop1 = true; - while (continueLoop1) { - if ( - tokenMatcher($.LA(1).tokenType, t.Semicolon) || - tokenMatcher($.LA(1).tokenType, t.Import) - ) { + try { + // the "{importDeclaration}" is a common prefix + $.MANY(() => { $.SUBRULE2($.importDeclaration); - } else { - continueLoop1 = false; - } - } + }); - let continueLoop2 = true; - while (continueLoop2) { - if ( - tokenMatcher($.LA(1).tokenType, t.At) && - !tokenMatcher($.LA(2).tokenType, t.Interface) - ) { - $.SUBRULE($.annotation); + $.MANY2({ + // To avoid ambiguity with @interface ("AnnotationTypeDeclaration" vs "Annotaion") + GATE: () => + (tokenMatcher($.LA(1).tokenType, t.At) && + tokenMatcher($.LA(2).tokenType, t.Interface)) === false, + DEF: () => { + $.SUBRULE($.annotation); + } + }); + } catch (e) { + // This means we had a syntax error in the imports or annotations + // So we can't keep parsing deep enough to make the decision + if (isRecognitionException(e)) { + // TODO: add original syntax error? + throw "Cannot Identify if the source code is an OrdinaryCompilationUnit or ModularCompilationUnit"; } else { - continueLoop2 = false; + throw e; } } From 89d25fafb253e2010b57c9c1cad4869547b439d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Dessoude?= Date: Thu, 26 Sep 2019 10:07:56 +0200 Subject: [PATCH 6/9] refactor function to not exit early during recording phase --- .../java-parser/src/productions/classes.js | 35 ++++++++++++------- .../src/productions/expressions.js | 14 +++++--- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/packages/java-parser/src/productions/classes.js b/packages/java-parser/src/productions/classes.js index 0d548be9..3b2d87ba 100644 --- a/packages/java-parser/src/productions/classes.js +++ b/packages/java-parser/src/productions/classes.js @@ -1,6 +1,6 @@ "use strict"; -const { tokenMatcher } = require("chevrotain"); +const { isRecognitionException, tokenMatcher } = require("chevrotain"); function defineRules($, t) { // https://docs.oracle.com/javase/specs/jls/se11/html/jls-8.html#jls-ClassDeclaration @@ -653,31 +653,42 @@ function defineRules($, t) { }); $.RULE("isClassDeclaration", () => { + let isEmptyTypeDeclaration = false; + if ( $.OPTION(() => { $.CONSUME(t.Semicolon); }) ) { // an empty "TypeDeclaration" - return false; + isEmptyTypeDeclaration = true; } - let continueLoop = true; - while (continueLoop) { - if ( - (tokenMatcher($.LA(1).tokenType, t.At) && - tokenMatcher($.LA(2).tokenType, t.Interface)) === false - ) { - try { + try { + // The {classModifier} is a super grammar of the "interfaceModifier" + // So we must parse all the "{classModifier}" before we can distinguish + // between the alternatives. + $.MANY({ + GATE: () => + (tokenMatcher($.LA(1).tokenType, t.At) && + tokenMatcher($.LA(2).tokenType, t.Interface)) === false, + DEF: () => { $.SUBRULE($.classModifier); - } catch (e) { - continueLoop = false; } + }); + } catch (e) { + if (isRecognitionException(e)) { + // TODO: add original syntax error? + throw "Cannot Identify if the is a or an "; } else { - continueLoop = false; + throw e; } } + if (isEmptyTypeDeclaration) { + return false; + } + const nextTokenType = this.LA(1).tokenType; return ( tokenMatcher(nextTokenType, t.Class) || diff --git a/packages/java-parser/src/productions/expressions.js b/packages/java-parser/src/productions/expressions.js index 8c1aabea..5156f65f 100644 --- a/packages/java-parser/src/productions/expressions.js +++ b/packages/java-parser/src/productions/expressions.js @@ -633,6 +633,7 @@ function defineRules($, t) { }); $.RULE("isRefTypeInMethodRef", () => { + let result = undefined; $.SUBRULE($.typeArguments); // arrayType @@ -642,20 +643,23 @@ function defineRules($, t) { const firstTokTypeAfterTypeArgs = this.LA(1).tokenType; if (tokenMatcher(firstTokTypeAfterTypeArgs, t.ColonColon)) { - return true; + result = true; } // we must be at the end of a "referenceType" if "dims" were encountered // So there is not point to check farther else if (hasDims) { - return false; + result = false; } // in the middle of a "classReferenceType" - try { + $.OPTION2(() => { $.CONSUME(t.Dot); $.SUBRULE($.classOrInterfaceType); - // eslint-disable-next-line no-empty - } catch (e) {} + }); + + if (result !== undefined) { + return result; + } const firstTokTypeAfterRefType = this.LA(1).tokenType; return tokenMatcher(firstTokTypeAfterRefType, t.ColonColon); From c7f2dda8156235b5a4e1a159e55b7ce3fe04b76b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Dessoude?= Date: Thu, 26 Sep 2019 13:14:27 +0200 Subject: [PATCH 7/9] remove ignoredIssues config --- packages/java-parser/src/parser.js | 91 ------------------- .../src/productions/blocks-and-statements.js | 14 ++- .../src/productions/expressions.js | 13 ++- .../java-parser/src/productions/interfaces.js | 25 +++-- .../productions/types-values-and-variables.js | 37 ++++---- 5 files changed, 54 insertions(+), 126 deletions(-) diff --git a/packages/java-parser/src/parser.js b/packages/java-parser/src/parser.js index 6bb76602..d82775d8 100644 --- a/packages/java-parser/src/parser.js +++ b/packages/java-parser/src/parser.js @@ -41,97 +41,6 @@ class JavaParser extends Parser { // TODO: performance: maxLookahead = 1 may be faster, but could we refactor the grammar to it? // and more importantly, do we want to? maxLookahead: 2, - // ambiguities resolved by backtracking - ignoredIssues: { - binaryExpression: { - OR: true - }, - lambdaParameterType: { - OR: true - }, - annotation: { - OR: true - }, - localVariableType: { - OR: true - }, - annotationTypeMemberDeclaration: { - OR: true - }, - typeDeclaration: { - OR: true - }, - typeArgument: { - OR: true - }, - type: { - OR: true - }, - referenceType: { - OR: true - }, - compilationUnit: { - OR: true - }, - classBodyDeclaration: { - OR: true - }, - classMemberDeclaration: { - OR: true - }, - unannReferenceType: { - OR: true - }, - formalParameter: { - OR: true - }, - interfaceMemberDeclaration: { - OR: true - }, - blockStatement: { - OR: true - }, - forStatement: { - OR: true - }, - newExpression: { - OR: true - }, - arrayCreationExpression: { - OR: true, - OR2: true - }, - expression: { - OR: true - }, - lambdaParameterList: { - OR: true - }, - lambdaParameter: { - OR: true - }, - primaryPrefix: { - OR: true - }, - castExpression: { - OR: true - }, - referenceTypeCastExpression: { - OR: true - }, - elementValue: { - OR: true - }, - resource: { - OR: true - }, - forInit: { - OR: true - }, - interfaceDeclaration: { - OR: true - } - }, nodeLocationTracking: "full" }); diff --git a/packages/java-parser/src/productions/blocks-and-statements.js b/packages/java-parser/src/productions/blocks-and-statements.js index baacea97..6fbfdc0a 100644 --- a/packages/java-parser/src/productions/blocks-and-statements.js +++ b/packages/java-parser/src/productions/blocks-and-statements.js @@ -29,12 +29,15 @@ function defineRules($, t) { $.isLocalVariableDeclaration ); + const isClassDeclaration = this.BACKTRACK_LOOKAHEAD($.isClassDeclaration); + $.OR([ { GATE: () => isLocalVariableDeclaration, ALT: () => $.SUBRULE($.localVariableDeclarationStatement) }, { + GATE: () => isClassDeclaration, ALT: () => $.SUBRULE($.classDeclaration) }, { ALT: () => $.SUBRULE($.statement) } @@ -58,10 +61,13 @@ function defineRules($, t) { // https://docs.oracle.com/javase/specs/jls/se11/html/jls-14.html#jls-LocalVariableType $.RULE("localVariableType", () => { - $.OR([ - { ALT: () => $.SUBRULE($.unannType) }, - { ALT: () => $.CONSUME(t.Var) } - ]); + $.OR({ + DEF: [ + { ALT: () => $.SUBRULE($.unannType) }, + { ALT: () => $.CONSUME(t.Var) } + ], + IGNORE_AMBIGUITIES: true + }); }); // https://docs.oracle.com/javase/specs/jls/se11/html/jls-14.html#jls-Statement diff --git a/packages/java-parser/src/productions/expressions.js b/packages/java-parser/src/productions/expressions.js index 5156f65f..510e9ebe 100644 --- a/packages/java-parser/src/productions/expressions.js +++ b/packages/java-parser/src/productions/expressions.js @@ -92,10 +92,13 @@ function defineRules($, t) { }); $.RULE("lambdaParameterType", () => { - $.OR([ - { ALT: () => $.SUBRULE($.unannType) }, - { ALT: () => $.CONSUME(t.Var) } - ]); + $.OR({ + DEF: [ + { ALT: () => $.SUBRULE($.unannType) }, + { ALT: () => $.CONSUME(t.Var) } + ], + IGNORE_AMBIGUITIES: true + }); }); $.RULE("lambdaBody", () => { @@ -166,6 +169,8 @@ function defineRules($, t) { } }, { + // Remove ambiguity with first OR option + GATE: () => !tokenMatcher($.LA(1).tokenType, t.Instanceof), ALT: () => { $.CONSUME(t.BinaryOperator); $.SUBRULE3($.unaryExpression); diff --git a/packages/java-parser/src/productions/interfaces.js b/packages/java-parser/src/productions/interfaces.js index 989ebdab..8255ae28 100644 --- a/packages/java-parser/src/productions/interfaces.js +++ b/packages/java-parser/src/productions/interfaces.js @@ -241,17 +241,22 @@ function defineRules($, t) { // https://docs.oracle.com/javase/specs/jls/se11/html/jls-9.html#jls-MarkerAnnotation $.OPTION(() => { $.CONSUME(t.LBrace); - $.OR([ - // normal annotation - https://docs.oracle.com/javase/specs/jls/se11/html/jls-9.html#jls-NormalAnnotation - { ALT: () => $.SUBRULE($.elementValuePairList) }, - // Single Element Annotation - https://docs.oracle.com/javase/specs/jls/se11/html/jls-9.html#jls-SingleElementAnnotation - { ALT: () => $.SUBRULE($.elementValue) }, - { - ALT: () => { - /* empty normal annotation contents */ + $.OR({ + DEF: [ + // normal annotation - https://docs.oracle.com/javase/specs/jls/se11/html/jls-9.html#jls-NormalAnnotation + { ALT: () => $.SUBRULE($.elementValuePairList) }, + // Single Element Annotation - https://docs.oracle.com/javase/specs/jls/se11/html/jls-9.html#jls-SingleElementAnnotation + { + ALT: () => $.SUBRULE($.elementValue) + }, + { + ALT: () => { + /* empty normal annotation contents */ + } } - } - ]); + ], + IGNORE_AMBIGUITIES: true + }); $.CONSUME(t.RBrace); }); }); diff --git a/packages/java-parser/src/productions/types-values-and-variables.js b/packages/java-parser/src/productions/types-values-and-variables.js index 3d793d9b..d9f747c6 100644 --- a/packages/java-parser/src/productions/types-values-and-variables.js +++ b/packages/java-parser/src/productions/types-values-and-variables.js @@ -56,24 +56,27 @@ function defineRules($, t) { }); // Spec Deviation: The array type "dims" suffix was extracted to this rule // to avoid backtracking for performance reasons. - $.OR([ - { - ALT: () => { - $.SUBRULE($.primitiveType); - $.SUBRULE($.dims); + $.OR({ + DEF: [ + { + ALT: () => { + $.SUBRULE($.primitiveType); + $.SUBRULE($.dims); + } + }, + { + // Spec Deviation: "typeVariable" alternative is missing because + // it is included in "classOrInterfaceType" + ALT: () => { + $.SUBRULE($.classOrInterfaceType); + $.OPTION(() => { + $.SUBRULE2($.dims); + }); + } } - }, - { - // Spec Deviation: "typeVariable" alternative is missing because - // it is included in "classOrInterfaceType" - ALT: () => { - $.SUBRULE($.classOrInterfaceType); - $.OPTION(() => { - $.SUBRULE2($.dims); - }); - } - } - ]); + ], + IGNORE_AMBIGUITIES: true // annotation prefix was extracted to remove ambiguities + }); }); // https://docs.oracle.com/javase/specs/jls/se11/html/jls-4.html#jls-ClassOrInterfaceType From 5a620d9c8caa554f33aba8d136fb9bff359eae94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Dessoude?= Date: Fri, 27 Sep 2019 11:49:47 +0200 Subject: [PATCH 8/9] remove gate in binaryExpression --- .../src/productions/expressions.js | 108 +++++++++--------- 1 file changed, 55 insertions(+), 53 deletions(-) diff --git a/packages/java-parser/src/productions/expressions.js b/packages/java-parser/src/productions/expressions.js index 510e9ebe..2118051d 100644 --- a/packages/java-parser/src/productions/expressions.js +++ b/packages/java-parser/src/productions/expressions.js @@ -122,61 +122,63 @@ function defineRules($, t) { $.RULE("binaryExpression", () => { $.SUBRULE($.unaryExpression); $.MANY(() => { - $.OR([ - { - ALT: () => { - $.CONSUME(t.Instanceof); - $.SUBRULE($.referenceType); - } - }, - { - ALT: () => { - $.CONSUME(t.AssignmentOperator); - $.SUBRULE2($.expression); - } - }, - // This is an example of why Java does not have a well designed grammar - // See: https://manas.tech/blog/2008/10/12/why-java-generics-dont-have-problems-with-right-shift-operator.html - // TODO: ensure the LT/GT sequences have no whitespace between each other. - { - // TODO: this is a bug in Chevrotain lookahead calculation. the "BinaryOperator" token can match "Less" or "Greater" - // as well, but because it is a **token Category** Chevrotain does not understand it need to looks two tokens ahead. - GATE: () => - tokenMatcher($.LA(2).tokenType, t.Less) || - tokenMatcher($.LA(2).tokenType, t.Greater), - ALT: () => { - $.OR2([ - { - GATE: () => $.LA(1).startOffset + 1 === $.LA(2).startOffset, - ALT: () => { - $.CONSUME(t.Less); - $.CONSUME2(t.Less); - } - }, - { - GATE: () => $.LA(1).startOffset + 1 === $.LA(2).startOffset, - ALT: () => { - $.CONSUME(t.Greater); - $.CONSUME2(t.Greater); - $.OPTION({ - GATE: () => $.LA(0).startOffset + 1 === $.LA(1).startOffset, - DEF: () => $.CONSUME3(t.Greater) - }); + $.OR({ + DEF: [ + { + ALT: () => { + $.CONSUME(t.Instanceof); + $.SUBRULE($.referenceType); + } + }, + { + ALT: () => { + $.CONSUME(t.AssignmentOperator); + $.SUBRULE2($.expression); + } + }, + // This is an example of why Java does not have a well designed grammar + // See: https://manas.tech/blog/2008/10/12/why-java-generics-dont-have-problems-with-right-shift-operator.html + // TODO: ensure the LT/GT sequences have no whitespace between each other. + { + // TODO: this is a bug in Chevrotain lookahead calculation. the "BinaryOperator" token can match "Less" or "Greater" + // as well, but because it is a **token Category** Chevrotain does not understand it need to looks two tokens ahead. + GATE: () => + tokenMatcher($.LA(2).tokenType, t.Less) || + tokenMatcher($.LA(2).tokenType, t.Greater), + ALT: () => { + $.OR2([ + { + GATE: () => $.LA(1).startOffset + 1 === $.LA(2).startOffset, + ALT: () => { + $.CONSUME(t.Less); + $.CONSUME2(t.Less); + } + }, + { + GATE: () => $.LA(1).startOffset + 1 === $.LA(2).startOffset, + ALT: () => { + $.CONSUME(t.Greater); + $.CONSUME2(t.Greater); + $.OPTION({ + GATE: () => + $.LA(0).startOffset + 1 === $.LA(1).startOffset, + DEF: () => $.CONSUME3(t.Greater) + }); + } } - } - ]); - $.SUBRULE2($.unaryExpression); - } - }, - { - // Remove ambiguity with first OR option - GATE: () => !tokenMatcher($.LA(1).tokenType, t.Instanceof), - ALT: () => { - $.CONSUME(t.BinaryOperator); - $.SUBRULE3($.unaryExpression); + ]); + $.SUBRULE2($.unaryExpression); + } + }, + { + ALT: () => { + $.CONSUME(t.BinaryOperator); + $.SUBRULE3($.unaryExpression); + } } - } - ]); + ], + IGNORE_AMBIGUITIES: true // the ambiguity between 1 and 4 options is resolved by the order (instanceOf is first) + }); }); }); From 4a708402b28c1faddb5cf656fefbbabc479cfa6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Dessoude?= Date: Fri, 27 Sep 2019 16:23:40 +0200 Subject: [PATCH 9/9] optimization by extracting logic to compute firstForUnaryExpressionNotPlusMinus --- packages/java-parser/src/parser.js | 4 +++ .../src/productions/expressions.js | 29 ++++++++++--------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/packages/java-parser/src/parser.js b/packages/java-parser/src/parser.js index d82775d8..c8f6dec5 100644 --- a/packages/java-parser/src/parser.js +++ b/packages/java-parser/src/parser.js @@ -68,7 +68,11 @@ class JavaParser extends Parser { blocksStatements.defineRules.call(this, $, t); expressions.defineRules.call(this, $, t); + this.firstForUnaryExpressionNotPlusMinus = []; this.performSelfAnalysis(); + this.firstForUnaryExpressionNotPlusMinus = expressions.computeFirstForUnaryExpressionNotPlusMinus.call( + this + ); } // hack to turn off CST building side effects during backtracking diff --git a/packages/java-parser/src/productions/expressions.js b/packages/java-parser/src/productions/expressions.js index 2118051d..83b27e56 100644 --- a/packages/java-parser/src/productions/expressions.js +++ b/packages/java-parser/src/productions/expressions.js @@ -612,18 +612,6 @@ function defineRules($, t) { }); $.RULE("isReferenceTypeCastExpression", () => { - const firstUnaryExpressionNotPlusMinus = this.computeContentAssist( - "unaryExpressionNotPlusMinus", - [] - ); - const nextTokTypes = firstUnaryExpressionNotPlusMinus.map( - x => x.nextTokenType - ); - // uniq - const firstForUnaryExpressionNotPlusMinus = nextTokTypes.filter( - (v, i, a) => a.indexOf(v) === i - ); - $.CONSUME(t.LBrace); $.SUBRULE($.referenceType); $.MANY(() => { @@ -633,7 +621,7 @@ function defineRules($, t) { const firstTokTypeAfterRBrace = this.LA(1).tokenType; return ( - firstForUnaryExpressionNotPlusMinus.find(tokType => + this.firstForUnaryExpressionNotPlusMinus.find(tokType => tokenMatcher(firstTokTypeAfterRBrace, tokType) ) !== undefined ); @@ -673,6 +661,19 @@ function defineRules($, t) { }); } +function computeFirstForUnaryExpressionNotPlusMinus() { + const firstUnaryExpressionNotPlusMinus = this.computeContentAssist( + "unaryExpressionNotPlusMinus", + [] + ); + const nextTokTypes = firstUnaryExpressionNotPlusMinus.map( + x => x.nextTokenType + ); + // uniq + return nextTokTypes.filter((v, i, a) => a.indexOf(v) === i); +} + module.exports = { - defineRules + defineRules, + computeFirstForUnaryExpressionNotPlusMinus };