From 72d3a0e47194d958c9f6519c6b24acd6a7d52960 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Mon, 26 Jul 2010 20:54:07 +0400 Subject: [PATCH] New Java parser (work in progress) --- .../lang/java/parser/DeclarationParser.java | 78 +++++++++++++++++-- .../declarations/FieldMulti.txt | 22 ++++++ .../declarations/FieldSimple.txt | 18 +++++ .../declarations/MissingInitializer.txt | 16 ++++ .../MissingInitializerExpression.txt | 16 ++++ .../declarations/MultiLineUnclosed.txt | 23 ++++++ .../declarations/UnclosedBracket.txt | 15 ++++ .../declarations/UnclosedComma.txt | 15 ++++ .../declarations/UnclosedSemicolon.txt | 14 ++++ .../parser/partial/DeclarationParserTest.java | 9 +++ 10 files changed, 220 insertions(+), 6 deletions(-) create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/FieldMulti.txt create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/FieldSimple.txt create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/MissingInitializer.txt create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/MissingInitializerExpression.txt create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/MultiLineUnclosed.txt create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/UnclosedBracket.txt create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/UnclosedComma.txt create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/UnclosedSemicolon.txt diff --git a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java index 183accdbf72b..0357b1e11cfe 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java @@ -303,8 +303,7 @@ public class DeclarationParser { } else { if (typeParams != null) { - final PsiBuilder.Marker error = typeParams.precede(); - error.errorBefore(JavaErrorMessages.message("unexpected.token"), type); + typeParams.precede().errorBefore(JavaErrorMessages.message("unexpected.token"), type); } builder.error(JavaErrorMessages.message("expected.identifier")); declaration.drop(); @@ -322,7 +321,10 @@ public class DeclarationParser { } } - return parseFieldOrLocalVariable(builder, declaration); + if (typeParams != null) { + typeParams.precede().errorBefore(JavaErrorMessages.message("unexpected.token"), type); + } + return parseFieldOrLocalVariable(builder, declaration, context); } @NotNull @@ -367,9 +369,73 @@ public class DeclarationParser { } @Nullable - private static PsiBuilder.Marker parseFieldOrLocalVariable(final PsiBuilder builder, final PsiBuilder.Marker declaration) { - // todo: implement - throw new UnsupportedOperationException(builder.toString() + declaration); + private static PsiBuilder.Marker parseFieldOrLocalVariable(final PsiBuilder builder, final PsiBuilder.Marker declaration, + final Context context) { + final IElementType varType; + if (context == Context.CLASS || context == Context.ANNOTATION_INTERFACE) { + varType = JavaElementType.FIELD; + } + else if (context == Context.CODE_BLOCK) { + varType = JavaElementType.LOCAL_VARIABLE; + } + else { + LOG.error("Unexpected context: " + context); + declaration.drop(); + return null; + } + + PsiBuilder.Marker variable = declaration; + boolean openMarker = true; + boolean eatSemicolon = true; + boolean expectSemicolon = true; + while (true) { + while (expect(builder, JavaTokenType.LBRACKET)) { + if (!expect(builder, JavaTokenType.RBRACKET)) { + error(builder, JavaErrorMessages.message("expected.rbracket")); + expectSemicolon = false; + break; + } + } + + if (expect(builder, JavaTokenType.EQ)) { + final PsiBuilder.Marker expr = ExpressionParser.parse(builder); + if (expr == null) { + error(builder, JavaErrorMessages.message("expected.expression")); + expectSemicolon = false; + break; + } + } + + if (builder.getTokenType() == JavaTokenType.COMMA) { + variable.done(varType); + builder.advanceLexer(); + variable = builder.mark(); + } + else { + break; + } + + if (!expect(builder, JavaTokenType.IDENTIFIER)) { + variable.drop(); + error(builder, JavaErrorMessages.message("expected.identifier")); + openMarker = false; + eatSemicolon = false; + break; + } + } + + if (eatSemicolon) { + if (!expect(builder, JavaTokenType.SEMICOLON) && expectSemicolon) { + error(builder, JavaErrorMessages.message("expected.semicolon")); + } + // todo: special treatment - see DeclarationParserTest.testMultiLineUnclosed() + } + + if (openMarker) { + variable.done(varType); + } + + return declaration; } @Nullable diff --git a/java/java-tests/testData/psi/parser-partial/declarations/FieldMulti.txt b/java/java-tests/testData/psi/parser-partial/declarations/FieldMulti.txt new file mode 100644 index 000000000000..22138d62d7bf --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/FieldMulti.txt @@ -0,0 +1,22 @@ +PsiJavaFile:FieldMulti.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiField:field1 + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:field1('field1') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiLiteralExpression:0 + PsiJavaToken:INTEGER_LITERAL('0') + PsiJavaToken:COMMA(',') + PsiWhiteSpace(' ') + PsiField:field2 + PsiIdentifier:field2('field2') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/declarations/FieldSimple.txt b/java/java-tests/testData/psi/parser-partial/declarations/FieldSimple.txt new file mode 100644 index 000000000000..59dc28ffe43c --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/FieldSimple.txt @@ -0,0 +1,18 @@ +PsiJavaFile:FieldSimple.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiField:field + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:field('field') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiLiteralExpression:0 + PsiJavaToken:INTEGER_LITERAL('0') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/declarations/MissingInitializer.txt b/java/java-tests/testData/psi/parser-partial/declarations/MissingInitializer.txt new file mode 100644 index 000000000000..ad9b3b26e18c --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/MissingInitializer.txt @@ -0,0 +1,16 @@ +PsiJavaFile:MissingInitializer.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiField:field + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:field('field') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiErrorElement:Expression expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/declarations/MissingInitializerExpression.txt b/java/java-tests/testData/psi/parser-partial/declarations/MissingInitializerExpression.txt new file mode 100644 index 000000000000..e7dac65bea9c --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/MissingInitializerExpression.txt @@ -0,0 +1,16 @@ +PsiJavaFile:MissingInitializerExpression.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiField:field + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:field('field') + PsiJavaToken:EQ('=') + PsiErrorElement:Expression expected + + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/MultiLineUnclosed.txt b/java/java-tests/testData/psi/parser-partial/declarations/MultiLineUnclosed.txt new file mode 100644 index 000000000000..d44d8913422d --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/MultiLineUnclosed.txt @@ -0,0 +1,23 @@ +PsiJavaFile:MultiLineUnclosed.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiErrorElement:Identifier expected + + PsiWhiteSpace(' \n ') + PsiField:o + PsiModifierList: + + PsiTypeElement:Object + PsiJavaCodeReferenceElement:Object + PsiIdentifier:Object('Object') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:o('o') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/UnclosedBracket.txt b/java/java-tests/testData/psi/parser-partial/declarations/UnclosedBracket.txt new file mode 100644 index 000000000000..e17119cb9b42 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/UnclosedBracket.txt @@ -0,0 +1,15 @@ +PsiJavaFile:UnclosedBracket.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiField:field + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:field('field') + PsiJavaToken:LBRACKET('[') + PsiErrorElement:']' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/declarations/UnclosedComma.txt b/java/java-tests/testData/psi/parser-partial/declarations/UnclosedComma.txt new file mode 100644 index 000000000000..83f9e32e72d9 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/UnclosedComma.txt @@ -0,0 +1,15 @@ +PsiJavaFile:UnclosedComma.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiField:field + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:field('field') + PsiJavaToken:COMMA(',') + PsiErrorElement:Identifier expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/UnclosedSemicolon.txt b/java/java-tests/testData/psi/parser-partial/declarations/UnclosedSemicolon.txt new file mode 100644 index 000000000000..f185e9852b30 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/UnclosedSemicolon.txt @@ -0,0 +1,14 @@ +PsiJavaFile:UnclosedSemicolon.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiField:field + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:field('field') + PsiErrorElement:';' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java index 2b5e8486967c..7d516c955c89 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java @@ -35,6 +35,15 @@ public class DeclarationParserTest extends JavaParsingTestCase { public void testEnumBody4() { doParserTest("{ RED(0), GREEN(1), BLUE(2); }", false, true); } public void testEnumBody5() { doParserTest("{ @ANNOTATION A(10) }", false, true); } + public void testFieldSimple() { doParserTest("{ int field = 0; }", false, false); } + public void testFieldMulti() { doParserTest("{ int field1 = 0, field2; }", false, false); } + public void testUnclosedBracket() { doParserTest("{ int field[ }", false, false); } + public void testMissingInitializer() { doParserTest("{ int field = }", false, false); } + public void testUnclosedComma() { doParserTest("{ int field, }", false, false); } + public void testUnclosedSemicolon() { doParserTest("{ int field }", false, false); } + public void testMissingInitializerExpression() { doParserTest("{ int field=; }", false, false); } + //public void testMultiLineUnclosed() { doParserTest("{ int \n Object o; }", false, false); } // todo: implement + private void doParserTest(final String text, final boolean isAnnotation, final boolean isEnum) { doParserTest(text, new Parser() { public void parse(final PsiBuilder builder) {