New Java parser (multiline hack)

This commit is contained in:
Roman Shevchenko
2010-08-03 12:03:13 +04:00
parent d13ea31903
commit e3d8d23be2
4 changed files with 70 additions and 27 deletions
@@ -24,6 +24,7 @@ import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.ILazyParseableElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -43,6 +44,9 @@ public class DeclarationParser {
private static final TokenSet APPEND_TO_METHOD_SET = TokenSet.create(
JavaTokenType.IDENTIFIER, JavaTokenType.COMMA, JavaTokenType.THROWS_KEYWORD);
private static final String WHITESPACES = "\n\r \t";
private static final String LINE_ENDS = "\n\r";
private DeclarationParser() { }
public static void parseClassBodyWithBraces(final PsiBuilder builder, final boolean isAnnotation, final boolean isEnum) {
@@ -209,6 +213,7 @@ public class DeclarationParser {
}
final PsiBuilder.Marker declaration = builder.mark();
final int declarationStart = builder.getCurrentOffset();
final Pair<PsiBuilder.Marker, Boolean> modListInfo = parseModifierList(builder);
final PsiBuilder.Marker modList = modListInfo.first;
@@ -225,7 +230,7 @@ public class DeclarationParser {
else if (ElementType.CLASS_KEYWORD_BIT_SET.contains(builder.getTokenType())) {
final PsiBuilder.Marker root = parseClassFromKeyword(builder, declaration, false);
if (context == Context.FILE) {
// todo: append following declarations to root
// todo: append following declarations to root (?)
boolean declarationsAfterEnd = false;
while (builder.getTokenType() != null && builder.getTokenType() != JavaTokenType.RBRACE) {
@@ -344,7 +349,7 @@ public class DeclarationParser {
if (typeParams != null) {
typeParams.precede().errorBefore(JavaErrorMessages.message("unexpected.token"), type);
}
return parseFieldOrLocalVariable(builder, declaration, context);
return parseFieldOrLocalVariable(builder, declaration, declarationStart, context);
}
@NotNull
@@ -538,7 +543,7 @@ public class DeclarationParser {
@Nullable
private static PsiBuilder.Marker parseFieldOrLocalVariable(final PsiBuilder builder, final PsiBuilder.Marker declaration,
final Context context) {
final int declarationStart, final Context context) {
final IElementType varType;
if (context == Context.CLASS || context == Context.ANNOTATION_INTERFACE) {
varType = JavaElementType.FIELD;
@@ -553,46 +558,66 @@ public class DeclarationParser {
}
PsiBuilder.Marker variable = declaration;
boolean openMarker = true;
boolean unclosed = false;
boolean eatSemicolon = true;
boolean expectSemicolon = true;
boolean shouldRollback;
boolean openMarker = true;
while (true) {
shouldRollback = true;
if (!eatBrackets(builder)) {
expectSemicolon = false;
unclosed = true;
}
if (expect(builder, JavaTokenType.EQ)) {
final PsiBuilder.Marker expr = ExpressionParser.parse(builder);
if (expr == null) {
if (expr != null) {
shouldRollback = false;
}
else {
error(builder, JavaErrorMessages.message("expected.expression"));
expectSemicolon = false;
unclosed = true;
break;
}
}
if (builder.getTokenType() == JavaTokenType.COMMA) {
variable.done(varType);
builder.advanceLexer();
variable = builder.mark();
}
else {
if (builder.getTokenType() != JavaTokenType.COMMA) break;
variable.done(varType);
builder.advanceLexer();
if (builder.getTokenType() != JavaTokenType.IDENTIFIER) {
error(builder, JavaErrorMessages.message("expected.identifier"));
unclosed = true;
eatSemicolon = false;
openMarker = false;
break;
}
if (!expect(builder, JavaTokenType.IDENTIFIER)) {
variable.drop();
error(builder, JavaErrorMessages.message("expected.identifier"));
openMarker = false;
eatSemicolon = false;
break;
}
variable = builder.mark();
builder.advanceLexer();
}
if (eatSemicolon) {
if (!expect(builder, JavaTokenType.SEMICOLON) && expectSemicolon) {
if (builder.getTokenType() == JavaTokenType.SEMICOLON && eatSemicolon) {
builder.advanceLexer();
}
else {
// special treatment (see DeclarationParserTest.testMultiLineUnclosed())
if (!builder.eof() && shouldRollback) {
final CharSequence text = builder.getOriginalText();
final int spaceEnd = builder.getCurrentOffset();
final int spaceStart = CharArrayUtil.shiftBackward(text, spaceEnd-1, WHITESPACES);
final int lineStart = CharArrayUtil.shiftBackwardUntil(text, spaceEnd, LINE_ENDS);
if (declarationStart < lineStart && lineStart < spaceStart) {
final int newBufferEnd = CharArrayUtil.shiftForward(text, lineStart, WHITESPACES);
declaration.rollbackTo();
return parse(stoppingBuilder(builder, newBufferEnd), context);
}
}
if (!unclosed) {
error(builder, JavaErrorMessages.message("expected.semicolon"));
}
// todo: special treatment - see DeclarationParserTest.testMultiLineUnclosed()
}
if (openMarker) {
@@ -107,6 +107,25 @@ public class JavaParserUtil {
}
return (braceCount == 0 ? null : tokenType);
}
@Override
public boolean eof() {
return braceCount == 0 || super.eof();
}
};
}
public static PsiBuilder stoppingBuilder(final PsiBuilder builder, final int stopAt) {
return new PsiBuilderAdapter(builder) {
@Override
public IElementType getTokenType() {
return getCurrentOffset() < stopAt ? super.getTokenType() : null;
}
@Override
public boolean eof() {
return getCurrentOffset() < stopAt || super.eof();
}
};
}
@@ -7,7 +7,7 @@ PsiJavaFile:MultiLineUnclosed.java
PsiKeyword:int('int')
PsiErrorElement:Identifier expected
<empty list>
PsiWhiteSpace(' \n ')
PsiWhiteSpace(' \n ')
PsiField:o
PsiModifierList:
<empty list>
@@ -21,7 +21,6 @@ import com.intellij.lang.java.parser.JavaParsingTestCase;
import com.intellij.pom.java.LanguageLevel;
// todo: fix parser and uncomment tests
public class DeclarationParserTest extends JavaParsingTestCase {
public DeclarationParserTest() {
super("parser-partial/declarations");
@@ -78,7 +77,7 @@ public class DeclarationParserTest extends JavaParsingTestCase {
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); }
public void testMultiLineUnclosed() { doParserTest("{ int \n Object o; }", false, false); }
public void testMethodNormal0() { doParserTest("{ void f() {} }", false, false); }
public void testMethodNormal1() { doParserTest("{ void f(); }", false, false); }