diff --git a/packages/java-parser/src/productions/packages-and-modules.js b/packages/java-parser/src/productions/packages-and-modules.js index 65a463dc..d4ce3003 100644 --- a/packages/java-parser/src/productions/packages-and-modules.js +++ b/packages/java-parser/src/productions/packages-and-modules.js @@ -3,7 +3,15 @@ const { isRecognitionException, tokenMatcher, EOF } = require("chevrotain"); const { classBodyTypes } = require("./utils/class-body-types"); function defineRules($, t) { - // https://docs.oracle.com/javase/specs/jls/se16/html/jls-7.html#CompilationUnit + + /** + * Spec Deviation: As OrdinaryCompilationUnit and UnnamedClassCompilationUnit + * both can have multiple class or interface declarations, both were combined + * in the ordinaryCompilationUnit rule + * + * https://docs.oracle.com/javase/specs/jls/se21/html/jls-7.html#jls-7.3 + * https://docs.oracle.com/javase/specs/jls/se21/preview/specs/unnamed-classes-instance-main-methods-jls.html + */ $.RULE("compilationUnit", () => { // custom optimized backtracking lookahead logic const isModule = $.BACKTRACK_LOOKAHEAD($.isModuleCompilationUnit); @@ -96,18 +104,41 @@ function defineRules($, t) { ]); }); - // https://docs.oracle.com/javase/specs/jls/se16/html/jls-7.html#jls-TypeDeclaration + + /** + * Spec Deviation: As OrdinaryCompilationUnit and UnnamedClassCompilationUnit + * both can have multiple class or interface declarations, both were combined + * in the ordinaryCompilationUnit rule + * + * As a result, the typeDeclaration combine TopLevelClassOrInterfaceDeclaration and includes fields and method declarations as well + * to handle unnamed class compilation unit + * + * https://docs.oracle.com/javase/specs/jls/se21/html/jls-7.html#jls-TopLevelClassOrInterfaceDeclaration + * https://docs.oracle.com/javase/specs/jls/se21/preview/specs/unnamed-classes-instance-main-methods-jls.html + */ $.RULE("typeDeclaration", () => { // TODO: consider extracting the prefix modifiers here to avoid backtracking - const isClassDeclaration = this.BACKTRACK_LOOKAHEAD($.isClassDeclaration); + const nextRuleType = $.BACKTRACK_LOOKAHEAD( + $.identifyClassBodyDeclarationType + ); $.OR([ + { ALT: () => $.CONSUME(t.Semicolon) }, { - GATE: () => isClassDeclaration, + GATE: () => nextRuleType === classBodyTypes.classDeclaration, ALT: () => $.SUBRULE($.classDeclaration) }, - { ALT: () => $.SUBRULE($.interfaceDeclaration) }, - { ALT: () => $.CONSUME(t.Semicolon) } + { + GATE: () => nextRuleType === classBodyTypes.interfaceDeclaration, + ALT: () => $.SUBRULE($.interfaceDeclaration) + }, + { + GATE: () => nextRuleType === classBodyTypes.fieldDeclaration, + ALT: () => $.SUBRULE($.fieldDeclaration) + }, + { + ALT: () => $.SUBRULE($.methodDeclaration) + } ]); }); @@ -227,46 +258,6 @@ function defineRules($, t) { ]); }); - $.RULE("unnamedClassCompilationUnit", () => { - $.MANY(() => $.SUBRULE($.importDeclaration)); - - const nextRuleType = $.BACKTRACK_LOOKAHEAD( - $.identifyClassBodyDeclarationType - ); - $.MANY1({ - GATE: () => nextRuleType !== classBodyTypes.methodDeclaration, - DEF: () => $.SUBRULE($.classMemberDeclarationNoMethod, { - ARGS: [nextRuleType] - }) - }); - $.SUBRULE($.methodDeclaration); - $.MANY2(() => { - const nextRuleType = $.BACKTRACK_LOOKAHEAD($.identifyClassBodyDeclarationType); - $.SUBRULE($.classMemberDeclaration, { - ARGS: [nextRuleType] - }); - }); - }); - - $.RULE("classMemberDeclarationNoMethod", nextRuleType => { - $.OR([ - { - GATE: () => nextRuleType === classBodyTypes.fieldDeclaration, - ALT: () => $.SUBRULE($.fieldDeclaration) - }, - { - GATE: () => nextRuleType === classBodyTypes.classDeclaration, - ALT: () => $.SUBRULE($.classDeclaration) - }, - { ALT: () => $.CONSUME(t.Semicolon) }, - { - GATE: () => nextRuleType === classBodyTypes.interfaceDeclaration, - ALT: () => $.SUBRULE($.interfaceDeclaration) - } - ]); - - }); - $.RULE("isModuleCompilationUnit", () => { $.OPTION(() => { $.SUBRULE($.packageDeclaration); diff --git a/packages/java-parser/test/compilation-unit/unnamed-class-compilation-unit-spec.js b/packages/java-parser/test/compilation-unit/unnamed-class-compilation-unit-spec.js index 9fc6cd6d..44ba6be9 100644 --- a/packages/java-parser/test/compilation-unit/unnamed-class-compilation-unit-spec.js +++ b/packages/java-parser/test/compilation-unit/unnamed-class-compilation-unit-spec.js @@ -10,6 +10,7 @@ describe("Unnamed Class Compilation Unit", () => { System.out.println("Hello, World!"); } `; + javaParser.parse(input, "compilationUnit"); expect(() => javaParser.parse(input, "compilationUnit")).to.not.throw(); }); @@ -19,7 +20,7 @@ describe("Unnamed Class Compilation Unit", () => { System.out.println("Hello, World!"); } `; - expect(() => javaParser.parse(input, "unnamedClassCompilationUnit")).to.not.throw(); + expect(() => javaParser.parse(input, "compilationUnit")).to.not.throw(); }); it("should handle UnnamedClassCompilationUnit with fields", () => { @@ -32,7 +33,7 @@ describe("Unnamed Class Compilation Unit", () => { System.out.println(hourra); } `; - expect(() => javaParser.parse(input, "unnamedClassCompilationUnit")).to.not.throw(); + expect(() => javaParser.parse(input, "compilationUnit")).to.not.throw(); }); it("should handle UnnamedClassCompilationUnit with class declaration", () => { @@ -43,7 +44,7 @@ describe("Unnamed Class Compilation Unit", () => { System.out.println(Test.greetings()); } `; - expect(() => javaParser.parse(input, "unnamedClassCompilationUnit")).to.not.throw(); + expect(() => javaParser.parse(input, "compilationUnit")).to.not.throw(); }); it("should handle UnnamedClassCompilationUnit with interface declaration", () => { @@ -54,7 +55,7 @@ describe("Unnamed Class Compilation Unit", () => { System.out.println(Test.greetings()); } `; - expect(() => javaParser.parse(input, "unnamedClassCompilationUnit")).to.not.throw(); + expect(() => javaParser.parse(input, "compilationUnit")).to.not.throw(); }); it("should handle UnnamedClassCompilationUnit with semicolons", () => { @@ -65,7 +66,7 @@ describe("Unnamed Class Compilation Unit", () => { System.out.println("Hello World!"); } `; - expect(() => javaParser.parse(input, "unnamedClassCompilationUnit")).to.not.throw(); + expect(() => javaParser.parse(input, "compilationUnit")).to.not.throw(); }); it("should handle UnnamedClassCompilationUnit with class member declarations", () => { @@ -77,7 +78,7 @@ describe("Unnamed Class Compilation Unit", () => { } `; - expect(() => javaParser.parse(input, "unnamedClassCompilationUnit")).to.not.throw(); + expect(() => javaParser.parse(input, "compilationUnit")).to.not.throw(); }); it("should handle UnnamedClassCompilationUnit with imports", () => { @@ -90,6 +91,6 @@ describe("Unnamed Class Compilation Unit", () => { } `; - expect(() => javaParser.parse(input, "unnamedClassCompilationUnit")).to.not.throw(); + expect(() => javaParser.parse(input, "compilationUnit")).to.not.throw(); }); });