Parser: allow names to be specified for class objects

This commit is contained in:
Pavel V. Talanov
2015-01-19 19:38:06 +03:00
parent c29ba63a74
commit 2e837c6586
5 changed files with 101 additions and 14 deletions
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.JetNodeType;
import org.jetbrains.kotlin.JetNodeTypes; import org.jetbrains.kotlin.JetNodeTypes;
import org.jetbrains.kotlin.lexer.JetToken; import org.jetbrains.kotlin.lexer.JetToken;
import org.jetbrains.kotlin.lexer.JetTokens; import org.jetbrains.kotlin.lexer.JetTokens;
import org.jetbrains.kotlin.parsing.JetParsing.NameParsingMode;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
@@ -1318,7 +1319,7 @@ public class JetExpressionParsing extends AbstractJetParsing {
return null; return null;
} }
myJetParsing.parseObject(true, true); myJetParsing.parseObject(NameParsingMode.REQUIRED, true);
declType = OBJECT_DECLARATION; declType = OBJECT_DECLARATION;
} }
return declType; return declType;
@@ -1826,7 +1827,7 @@ public class JetExpressionParsing extends AbstractJetParsing {
public void parseObjectLiteral() { public void parseObjectLiteral() {
PsiBuilder.Marker literal = mark(); PsiBuilder.Marker literal = mark();
PsiBuilder.Marker declaration = mark(); PsiBuilder.Marker declaration = mark();
myJetParsing.parseObject(false, false); // Body is not optional because of foo(object : A, B) myJetParsing.parseObject(NameParsingMode.PROHIBITED, false); // Body is not optional because of foo(object : A, B)
declaration.done(OBJECT_DECLARATION); declaration.done(OBJECT_DECLARATION);
literal.done(OBJECT_LITERAL); literal.done(OBJECT_LITERAL);
} }
@@ -376,7 +376,7 @@ public class JetParsing extends AbstractJetParsing {
declType = parseTypeAlias(); declType = parseTypeAlias();
} }
else if (keywordToken == OBJECT_KEYWORD) { else if (keywordToken == OBJECT_KEYWORD) {
parseObject(true, true); parseObject(NameParsingMode.REQUIRED, true);
declType = OBJECT_DECLARATION; declType = OBJECT_DECLARATION;
} }
@@ -551,6 +551,12 @@ public class JetParsing extends AbstractJetParsing {
annotation.done(ANNOTATION_ENTRY); annotation.done(ANNOTATION_ENTRY);
} }
public enum NameParsingMode {
REQUIRED,
ALLOWED,
PROHIBITED;
}
/* /*
* class * class
* : modifiers ("class" | "trait") SimpleName * : modifiers ("class" | "trait") SimpleName
@@ -565,7 +571,7 @@ public class JetParsing extends AbstractJetParsing {
* : "object" SimpleName? ":" delegationSpecifier{","}? classBody? * : "object" SimpleName? ":" delegationSpecifier{","}? classBody?
* ; * ;
*/ */
IElementType parseClassOrObject(final boolean object, boolean named, boolean optionalBody, boolean enumClass) { IElementType parseClassOrObject(boolean object, NameParsingMode nameParsingMode, boolean optionalBody, boolean enumClass) {
if (object) { if (object) {
assert _at(OBJECT_KEYWORD); assert _at(OBJECT_KEYWORD);
} }
@@ -574,15 +580,23 @@ public class JetParsing extends AbstractJetParsing {
} }
advance(); // CLASS_KEYWORD, TRAIT_KEYWORD or OBJECT_KEYWORD advance(); // CLASS_KEYWORD, TRAIT_KEYWORD or OBJECT_KEYWORD
if (named) { if (nameParsingMode == NameParsingMode.REQUIRED) {
OptionalMarker marker = new OptionalMarker(object); OptionalMarker marker = new OptionalMarker(object);
expect(IDENTIFIER, "Name expected", CLASS_NAME_RECOVERY_SET); expect(IDENTIFIER, "Name expected", CLASS_NAME_RECOVERY_SET);
marker.done(OBJECT_DECLARATION_NAME); marker.done(OBJECT_DECLARATION_NAME);
} }
else { else {
assert object : "Must be an object to be nameless";
if (at(IDENTIFIER)) { if (at(IDENTIFIER)) {
assert object : "Must be an object to be nameless"; if (nameParsingMode == NameParsingMode.PROHIBITED) {
errorAndAdvance("An object expression cannot bind a name"); errorAndAdvance("An object expression cannot bind a name");
}
else {
assert nameParsingMode == NameParsingMode.ALLOWED;
PsiBuilder.Marker marker = mark();
advance();
marker.done(OBJECT_DECLARATION_NAME);
}
} }
} }
@@ -595,7 +609,7 @@ public class JetParsing extends AbstractJetParsing {
boolean hasConstructorModifiers = parseModifierList(PRIMARY_CONSTRUCTOR_MODIFIER_LIST, REGULAR_ANNOTATIONS_ONLY_WITH_BRACKETS); boolean hasConstructorModifiers = parseModifierList(PRIMARY_CONSTRUCTOR_MODIFIER_LIST, REGULAR_ANNOTATIONS_ONLY_WITH_BRACKETS);
// Some modifiers found, but no parentheses following: class has already ended, and we are looking at something else // Some modifiers found, but no parentheses following: class has already ended, and we are looking at something else
if (hasConstructorModifiers && !atSet(LPAR, LBRACE, COLON) ) { if (hasConstructorModifiers && !atSet(LPAR, LBRACE, COLON)) {
beforeConstructorModifiers.rollbackTo(); beforeConstructorModifiers.rollbackTo();
constructorModifiersMarker.drop(); constructorModifiersMarker.drop();
return object ? OBJECT_DECLARATION : CLASS; return object ? OBJECT_DECLARATION : CLASS;
@@ -616,7 +630,7 @@ public class JetParsing extends AbstractJetParsing {
} }
constructorModifiersMarker.error("Constructors are not allowed for objects"); constructorModifiersMarker.error("Constructors are not allowed for objects");
if (at(COLON) ) { if (at(COLON)) {
advance(); // COLON advance(); // COLON
parseDelegationSpecifierList(); parseDelegationSpecifierList();
} }
@@ -643,11 +657,11 @@ public class JetParsing extends AbstractJetParsing {
} }
IElementType parseClass(boolean enumClass) { IElementType parseClass(boolean enumClass) {
return parseClassOrObject(false, true, true, enumClass); return parseClassOrObject(false, NameParsingMode.REQUIRED, true, enumClass);
} }
void parseObject(boolean named, boolean optionalBody) { void parseObject(NameParsingMode nameParsingMode, boolean optionalBody) {
parseClassOrObject(true, named, optionalBody, false); parseClassOrObject(true, nameParsingMode, optionalBody, false);
} }
/* /*
@@ -803,7 +817,7 @@ public class JetParsing extends AbstractJetParsing {
declType = parseTypeAlias(); declType = parseTypeAlias();
} }
else if (keywordToken == OBJECT_KEYWORD) { else if (keywordToken == OBJECT_KEYWORD) {
parseObject(true, true); parseObject(NameParsingMode.REQUIRED, true);
declType = OBJECT_DECLARATION; declType = OBJECT_DECLARATION;
} }
else if (keywordToken == LBRACE) { else if (keywordToken == LBRACE) {
@@ -872,7 +886,7 @@ public class JetParsing extends AbstractJetParsing {
advance(); // CLASS_KEYWORD advance(); // CLASS_KEYWORD
PsiBuilder.Marker objectDeclaration = mark(); PsiBuilder.Marker objectDeclaration = mark();
parseObject(false, true); parseObject(NameParsingMode.ALLOWED, true);
closeDeclarationWithCommentBinders(objectDeclaration, OBJECT_DECLARATION, true); closeDeclarationWithCommentBinders(objectDeclaration, OBJECT_DECLARATION, true);
return CLASS_OBJECT; return CLASS_OBJECT;
} }
+11
View File
@@ -0,0 +1,11 @@
class A {
class object Default
class object B
class object C {}
class object
object C
}
@@ -0,0 +1,55 @@
JetFile: NamedClassObject.kt
PACKAGE_DIRECTIVE
<empty list>
CLASS
PsiElement(class)('class')
PsiWhiteSpace(' ')
PsiElement(IDENTIFIER)('A')
PsiWhiteSpace(' ')
CLASS_BODY
PsiElement(LBRACE)('{')
PsiWhiteSpace('\n ')
CLASS_OBJECT
PsiElement(class)('class')
PsiWhiteSpace(' ')
OBJECT_DECLARATION
PsiElement(object)('object')
PsiWhiteSpace(' ')
OBJECT_DECLARATION_NAME
PsiElement(IDENTIFIER)('Default')
PsiWhiteSpace('\n\n ')
CLASS_OBJECT
PsiElement(class)('class')
PsiWhiteSpace(' ')
OBJECT_DECLARATION
PsiElement(object)('object')
PsiWhiteSpace(' ')
OBJECT_DECLARATION_NAME
PsiElement(IDENTIFIER)('B')
PsiWhiteSpace('\n\n ')
CLASS_OBJECT
PsiElement(class)('class')
PsiWhiteSpace(' ')
OBJECT_DECLARATION
PsiElement(object)('object')
PsiWhiteSpace(' ')
OBJECT_DECLARATION_NAME
PsiElement(IDENTIFIER)('C')
PsiWhiteSpace(' ')
CLASS_BODY
PsiElement(LBRACE)('{')
PsiElement(RBRACE)('}')
PsiWhiteSpace('\n\n ')
CLASS_OBJECT
PsiElement(class)('class')
PsiWhiteSpace(' ')
OBJECT_DECLARATION
PsiElement(object)('object')
PsiWhiteSpace('\n\n ')
OBJECT_DECLARATION
PsiElement(object)('object')
PsiWhiteSpace(' ')
OBJECT_DECLARATION_NAME
PsiElement(IDENTIFIER)('C')
PsiWhiteSpace('\n')
PsiElement(RBRACE)('}')
@@ -370,6 +370,12 @@ public class JetParsingTestGenerated extends AbstractJetParsingTest {
doParsingTest(fileName); doParsingTest(fileName);
} }
@TestMetadata("NamedClassObject.kt")
public void testNamedClassObject() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/NamedClassObject.kt");
doParsingTest(fileName);
}
@TestMetadata("NestedComments.kt") @TestMetadata("NestedComments.kt")
public void testNestedComments() throws Exception { public void testNestedComments() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/NestedComments.kt"); String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/NestedComments.kt");