From 86fcba88d070c5ad69cc501ce12c009f8b5d8ac5 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Wed, 15 Dec 2010 19:50:21 +0300 Subject: [PATCH] Attributes --- grammar/src/types.grm | 2 +- idea/src/org/jetbrains/jet/JetNodeTypes.java | 6 + .../jet/lang/parsing/JetParsing.java | 152 +++++++++++- idea/testData/psi/Attributes.jet | 38 +++ idea/testData/psi/Attributes.txt | 196 +++++++++++++++ idea/testData/psi/Attributes_ERR.jet | 40 ++++ idea/testData/psi/Attributes_ERR.txt | 226 ++++++++++++++++++ .../jetbrains/jet/parsing/JetParsingTest.java | 2 + 8 files changed, 654 insertions(+), 8 deletions(-) create mode 100644 idea/testData/psi/Attributes.jet create mode 100644 idea/testData/psi/Attributes.txt create mode 100644 idea/testData/psi/Attributes_ERR.jet create mode 100644 idea/testData/psi/Attributes_ERR.txt diff --git a/grammar/src/types.grm b/grammar/src/types.grm index 1dde86e2822..91e18e42a87 100644 --- a/grammar/src/types.grm +++ b/grammar/src/types.grm @@ -14,7 +14,7 @@ type ; userType - : (SimpleName ".")* simpleUserType{"."} + : /*(SimpleName ".")* */ simpleUserType{"."} ; simpleUserType diff --git a/idea/src/org/jetbrains/jet/JetNodeTypes.java b/idea/src/org/jetbrains/jet/JetNodeTypes.java index 4c0cb563ae3..68b417df53f 100644 --- a/idea/src/org/jetbrains/jet/JetNodeTypes.java +++ b/idea/src/org/jetbrains/jet/JetNodeTypes.java @@ -32,6 +32,12 @@ public interface JetNodeTypes { JetNodeType IMPORTED = new JetNodeType("IMPORTED"); JetNodeType NAMESPACE_BODY = new JetNodeType("NAMESPACE_BODY"); JetNodeType MODIFIER_LIST = new JetNodeType("MODIFIER_LIST"); + JetNodeType ATTRIBUTE_ANNOTATION = new JetNodeType("ATTRIBUTE_ANNOTATION"); + JetNodeType ATTRIBUTE = new JetNodeType("ATTRIBUTE"); + JetNodeType USER_TYPE = new JetNodeType("USER_TYPE"); + JetNodeType TYPE_ARGUMENT_LIST = new JetNodeType("TYPE_ARGUMENT_LIST"); + JetNodeType VALUE_ARGUMENT_LIST = new JetNodeType("VALUE_ARGUMENT_LIST"); + JetNodeType VALUE_ARGUMENT = new JetNodeType("VALUE_ARGUMENT"); IElementType NAMESPACE_NAME = new JetNodeType("NAMESPACE_NAME"); diff --git a/idea/src/org/jetbrains/jet/lang/parsing/JetParsing.java b/idea/src/org/jetbrains/jet/lang/parsing/JetParsing.java index 0e16032ee38..b6e298964db 100644 --- a/idea/src/org/jetbrains/jet/lang/parsing/JetParsing.java +++ b/idea/src/org/jetbrains/jet/lang/parsing/JetParsing.java @@ -207,22 +207,17 @@ public class JetParsing { /* * modifier - * : "abstract" * : "virtual" * : "enum" * : "open" * : "attribute" * : "override" - * : "virtual" * : "abstract" * : "private" * : "protected" * : "public" * : "internal" - * : "in" - * : "out" * : "lazy" - * : "ref" */ private boolean parseModifierSoftKeyword() { if (!at(IDENTIFIER)) return false; @@ -236,11 +231,16 @@ public class JetParsing { return false; } + /* + * (modifier | attribute)* + */ private void parseModifierList() { PsiBuilder.Marker list = mark(); boolean empty = true; - while (true) { - if (MODIFIER_KEYWORDS.contains(tt())) { + while (!eof()) { + if (at(LBRACKET)) { + parseAttributeAnnotation(); + } else if (MODIFIER_KEYWORDS.contains(tt())) { advance(); } else { @@ -255,6 +255,143 @@ public class JetParsing { } } + /* + * attributeAnnotation + * : "[" attribute{","} "]" + * ; + */ + private void parseAttributeAnnotation() { + assert at(LBRACKET); + PsiBuilder.Marker annotation = mark(); + + advance(); // LBRACKET + + while (true) { + if (at(IDENTIFIER)) { + parseAttribute(); + if (!at(COMMA)) break; + advance(); // COMMA + } + else { + error("Expecting a comma-separated list of attributes"); + break; + } + } + + expect(RBRACKET, "Expecting ']' to close an attribute annotation"); + + annotation.done(ATTRIBUTE_ANNOTATION); + } + + /* + * attribute + * // : SimpleName{"."} valueArguments? + * [for recovery: userType valueArguments?] + * ; + */ + private void parseAttribute() { + PsiBuilder.Marker attribute = mark(); + parseUserType(); + if (at(LPAR)) parseValueArgumentList(); + attribute.done(ATTRIBUTE); + } + + /* + * userType + * : simpleUserType{"."} + * ; + * + * simpleUserType + * : SimpleName ("<" (optionalProjection type){","} ">")? + * ; + */ + private void parseUserType() { + PsiBuilder.Marker userType = mark(); + + while (true) { + parseSimpleUserType(); + if (!at(DOT)) break; + advance(); // DOT + } + + userType.done(USER_TYPE); + } + + /* + * simpleUserType + * : SimpleName ("<" (optionalProjection type){","} ">")? + * ; + */ + private void parseSimpleUserType() { + PsiBuilder.Marker type = mark(); + + expect(IDENTIFIER, "Type name expected", TokenSet.create(LT)); + parseTypeArgumentList(); + + type.done(USER_TYPE); + } + + /* + * (optionalProjection type){","} + */ + private void parseTypeArgumentList() { + if (!at(LT)) return; + + PsiBuilder.Marker list = mark(); + + advance(); // LT + + while (true) { + parseModifierList(); + parseTypeRef(); + if (!at(COMMA)) break; + advance(); // COMMA + } + + expect(GT, "Expecting a '>'"); + + list.done(TYPE_ARGUMENT_LIST); + } + + /* + * valueArguments + * : "(" (SimpleName "=")? ("out" | "ref")? expression{","} ")" + * ; + */ + private void parseValueArgumentList() { + assert at(LPAR); + + PsiBuilder.Marker list = mark(); + + advance(); // LPAR + + if (!at(RPAR)) { + while (true) { + parseValueArgument(); + if (!at(COMMA)) break; + advance(); // COMMA + } + } + + expect(RPAR, "Expecting ')'"); + + list.done(VALUE_ARGUMENT_LIST); + } + + /* + * (SimpleName "=")? ("out" | "ref")? expression + */ + private void parseValueArgument() { + PsiBuilder.Marker argument = mark(); + if (at(IDENTIFIER) && lookahead(1) == EQ) { + advance(); // IDENTIFIER + advance(); // EQ + } + if (at(OUT_KEYWORD) || at(REF_KEYWORD)) advance(); // REF or OUT + parseExpression(); + argument.done(VALUE_ARGUMENT); + } + /* * namespace * : "namespace" SimpleName{"."} "{" @@ -351,6 +488,7 @@ public class JetParsing { private void parseDelegationSpecifierList() { PsiBuilder.Marker list = mark(); + while (true) { parseDelegationSpecifier(); if (!at(COMMA)) break; diff --git a/idea/testData/psi/Attributes.jet b/idea/testData/psi/Attributes.jet new file mode 100644 index 00000000000..c23b914b932 --- /dev/null +++ b/idea/testData/psi/Attributes.jet @@ -0,0 +1,38 @@ +namespace foo.bar.goo + +abstract +virtual +enum +open +attribute +override +virtual +abstract +private +protected +public +internal +lazy [foo(a, b),ina,foo.bar.goo.doo.foo.foo] +[df] +in +[sdfsdf] +out +ref + class Bar { +} diff --git a/idea/testData/psi/Attributes.txt b/idea/testData/psi/Attributes.txt new file mode 100644 index 00000000000..40871595cfc --- /dev/null +++ b/idea/testData/psi/Attributes.txt @@ -0,0 +1,196 @@ +JetFile: Attributes.jet + NAMESPACE + PsiElement(namespace)('namespace') + PsiWhiteSpace(' ') + NAMESPACE_NAME + PsiElement(IDENTIFIER)('foo') + PsiElement(DOT)('.') + PsiElement(IDENTIFIER)('bar') + PsiElement(DOT)('.') + PsiElement(IDENTIFIER)('goo') + PsiWhiteSpace('\n\n') + CLASS + MODIFIER_LIST + PsiElement(abstract)('abstract') + PsiWhiteSpace('\n') + PsiElement(virtual)('virtual') + PsiWhiteSpace('\n') + PsiElement(enum)('enum') + PsiWhiteSpace('\n') + PsiElement(open)('open') + PsiWhiteSpace('\n') + PsiElement(attribute)('attribute') + PsiWhiteSpace('\n') + PsiElement(override)('override') + PsiWhiteSpace('\n') + PsiElement(virtual)('virtual') + PsiWhiteSpace('\n') + PsiElement(abstract)('abstract') + PsiWhiteSpace('\n') + PsiElement(private)('private') + PsiWhiteSpace('\n') + PsiElement(protected)('protected') + PsiWhiteSpace('\n') + PsiElement(public)('public') + PsiWhiteSpace('\n') + PsiElement(internal)('internal') + PsiWhiteSpace('\n') + PsiElement(lazy)('lazy') + PsiWhiteSpace(' ') + ATTRIBUTE_ANNOTATION + PsiElement(LBRACKET)('[') + ATTRIBUTE + USER_TYPE + USER_TYPE + PsiElement(IDENTIFIER)('foo') + TYPE_ARGUMENT_LIST + PsiElement(LT)('<') + PsiElement(IDENTIFIER)('A') + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('B') + PsiElement(GT)('>') + VALUE_ARGUMENT_LIST + PsiElement(LPAR)('(') + VALUE_ARGUMENT + PsiElement(IDENTIFIER)('a') + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + VALUE_ARGUMENT + PsiElement(IDENTIFIER)('b') + PsiElement(RPAR)(')') + PsiElement(COMMA)(',') + ATTRIBUTE + USER_TYPE + USER_TYPE + PsiElement(IDENTIFIER)('ina') + PsiElement(COMMA)(',') + ATTRIBUTE + USER_TYPE + USER_TYPE + PsiElement(IDENTIFIER)('foo') + PsiElement(DOT)('.') + USER_TYPE + PsiElement(IDENTIFIER)('bar') + PsiElement(DOT)('.') + USER_TYPE + PsiElement(IDENTIFIER)('goo') + PsiElement(DOT)('.') + USER_TYPE + PsiElement(IDENTIFIER)('doo') + TYPE_ARGUMENT_LIST + PsiElement(LT)('<') + PsiElement(IDENTIFIER)('f') + PsiElement(GT)('>') + PsiElement(DOT)('.') + USER_TYPE + PsiElement(IDENTIFIER)('foo') + TYPE_ARGUMENT_LIST + PsiElement(LT)('<') + PsiElement(IDENTIFIER)('bar') + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('goo') + PsiElement(GT)('>') + PsiElement(DOT)('.') + USER_TYPE + PsiElement(IDENTIFIER)('foo') + PsiElement(RBRACKET)(']') + PsiWhiteSpace('\n') + ATTRIBUTE_ANNOTATION + PsiElement(LBRACKET)('[') + ATTRIBUTE + USER_TYPE + USER_TYPE + PsiElement(IDENTIFIER)('df') + PsiElement(RBRACKET)(']') + PsiWhiteSpace('\n') + PsiElement(in)('in') + PsiWhiteSpace('\n') + ATTRIBUTE_ANNOTATION + PsiElement(LBRACKET)('[') + ATTRIBUTE + USER_TYPE + USER_TYPE + PsiElement(IDENTIFIER)('sdfsdf') + PsiElement(RBRACKET)(']') + PsiWhiteSpace('\n') + PsiElement(out)('out') + PsiWhiteSpace('\n') + PsiElement(ref)('ref') + PsiWhiteSpace('\n ') + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('Bar') + TYPE_PARAMETER_LIST + PsiElement(LT)('<') + TYPE_PARAMETER + MODIFIER_LIST + PsiElement(abstract)('abstract') + PsiWhiteSpace('\n') + PsiElement(virtual)('virtual') + PsiWhiteSpace('\n') + PsiElement(enum)('enum') + PsiWhiteSpace('\n') + PsiElement(open)('open') + PsiWhiteSpace('\n') + PsiElement(attribute)('attribute') + PsiWhiteSpace('\n') + PsiElement(override)('override') + PsiWhiteSpace('\n') + PsiElement(virtual)('virtual') + PsiWhiteSpace('\n') + PsiElement(abstract)('abstract') + PsiWhiteSpace('\n') + ATTRIBUTE_ANNOTATION + PsiElement(LBRACKET)('[') + ATTRIBUTE + USER_TYPE + USER_TYPE + PsiElement(IDENTIFIER)('sdfsd') + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + ATTRIBUTE + USER_TYPE + USER_TYPE + PsiElement(IDENTIFIER)('sdfsd') + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + ATTRIBUTE + USER_TYPE + USER_TYPE + PsiElement(IDENTIFIER)('a') + PsiElement(DOT)('.') + USER_TYPE + PsiElement(IDENTIFIER)('b') + PsiElement(DOT)('.') + USER_TYPE + PsiElement(IDENTIFIER)('f') + PsiElement(DOT)('.') + USER_TYPE + PsiElement(IDENTIFIER)('c') + PsiElement(RBRACKET)(']') + PsiWhiteSpace('\n') + PsiElement(private)('private') + PsiWhiteSpace('\n') + PsiElement(protected)('protected') + PsiWhiteSpace('\n') + PsiElement(public)('public') + PsiWhiteSpace('\n') + PsiElement(internal)('internal') + PsiWhiteSpace('\n') + PsiElement(lazy)('lazy') + PsiWhiteSpace('\n') + PsiElement(in)('in') + PsiWhiteSpace('\n') + PsiElement(out)('out') + PsiWhiteSpace('\n') + PsiElement(ref)('ref') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('T') + PsiElement(GT)('>') + PsiWhiteSpace(' ') + CLASS_BODY + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n') + PsiElement(RBRACE)('}') \ No newline at end of file diff --git a/idea/testData/psi/Attributes_ERR.jet b/idea/testData/psi/Attributes_ERR.jet new file mode 100644 index 00000000000..812d5792e1c --- /dev/null +++ b/idea/testData/psi/Attributes_ERR.jet @@ -0,0 +1,40 @@ +namespace foo.bar.goo + +abstract +virtual +enum +open +attribute +override +virtual +abstract +[] +private +protected +public +internal +lazy [foo(a, b), ina,foo.bar.goo.doo.foo.foo] +[df] +in +[sdfsdf,] +[s,fd d] +out +ref + class Bar { +} diff --git a/idea/testData/psi/Attributes_ERR.txt b/idea/testData/psi/Attributes_ERR.txt new file mode 100644 index 00000000000..aca4d9c69fa --- /dev/null +++ b/idea/testData/psi/Attributes_ERR.txt @@ -0,0 +1,226 @@ +JetFile: Attributes_ERR.jet + NAMESPACE + PsiElement(namespace)('namespace') + PsiWhiteSpace(' ') + NAMESPACE_NAME + PsiElement(IDENTIFIER)('foo') + PsiElement(DOT)('.') + PsiElement(IDENTIFIER)('bar') + PsiElement(DOT)('.') + PsiElement(IDENTIFIER)('goo') + PsiWhiteSpace('\n\n') + MODIFIER_LIST + PsiElement(abstract)('abstract') + PsiWhiteSpace('\n') + PsiElement(virtual)('virtual') + PsiWhiteSpace('\n') + PsiElement(enum)('enum') + PsiWhiteSpace('\n') + PsiElement(open)('open') + PsiWhiteSpace('\n') + PsiElement(attribute)('attribute') + PsiWhiteSpace('\n') + PsiElement(override)('override') + PsiWhiteSpace('\n') + PsiElement(virtual)('virtual') + PsiWhiteSpace('\n') + PsiElement(abstract)('abstract') + PsiWhiteSpace('\n') + ATTRIBUTE_ANNOTATION + PsiElement(LBRACKET)('[') + PsiErrorElement:Expecting a comma-separated list of attributes + + PsiElement(RBRACKET)(']') + PsiWhiteSpace('\n') + PsiElement(private)('private') + PsiWhiteSpace('\n') + PsiElement(protected)('protected') + PsiWhiteSpace('\n') + PsiElement(public)('public') + PsiWhiteSpace('\n') + PsiElement(internal)('internal') + PsiWhiteSpace('\n') + PsiElement(lazy)('lazy') + PsiWhiteSpace(' ') + ATTRIBUTE_ANNOTATION + PsiElement(LBRACKET)('[') + ATTRIBUTE + USER_TYPE + USER_TYPE + PsiElement(IDENTIFIER)('foo') + TYPE_ARGUMENT_LIST + PsiElement(LT)('<') + PsiElement(IDENTIFIER)('A') + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('B') + PsiElement(GT)('>') + VALUE_ARGUMENT_LIST + PsiElement(LPAR)('(') + VALUE_ARGUMENT + PsiElement(IDENTIFIER)('a') + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + VALUE_ARGUMENT + PsiElement(IDENTIFIER)('b') + PsiElement(RPAR)(')') + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + ATTRIBUTE + USER_TYPE + USER_TYPE + PsiElement(IDENTIFIER)('ina') + PsiElement(COMMA)(',') + ATTRIBUTE + USER_TYPE + USER_TYPE + PsiElement(IDENTIFIER)('foo') + PsiElement(DOT)('.') + USER_TYPE + PsiElement(IDENTIFIER)('bar') + PsiElement(DOT)('.') + USER_TYPE + PsiElement(IDENTIFIER)('goo') + PsiElement(DOT)('.') + USER_TYPE + PsiElement(IDENTIFIER)('doo') + TYPE_ARGUMENT_LIST + PsiElement(LT)('<') + PsiElement(IDENTIFIER)('f') + PsiElement(GT)('>') + PsiElement(DOT)('.') + USER_TYPE + PsiElement(IDENTIFIER)('foo') + TYPE_ARGUMENT_LIST + PsiElement(LT)('<') + PsiElement(IDENTIFIER)('bar') + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('goo') + PsiElement(GT)('>') + PsiElement(DOT)('.') + USER_TYPE + PsiElement(IDENTIFIER)('foo') + PsiElement(RBRACKET)(']') + PsiWhiteSpace('\n') + ATTRIBUTE_ANNOTATION + PsiElement(LBRACKET)('[') + ATTRIBUTE + USER_TYPE + USER_TYPE + PsiElement(IDENTIFIER)('df') + PsiElement(RBRACKET)(']') + PsiWhiteSpace('\n') + PsiElement(in)('in') + PsiWhiteSpace('\n') + ATTRIBUTE_ANNOTATION + PsiElement(LBRACKET)('[') + ATTRIBUTE + USER_TYPE + USER_TYPE + PsiElement(IDENTIFIER)('sdfsdf') + PsiElement(COMMA)(',') + PsiErrorElement:Expecting a comma-separated list of attributes + + PsiElement(RBRACKET)(']') + PsiWhiteSpace('\n') + ATTRIBUTE_ANNOTATION + PsiElement(LBRACKET)('[') + ATTRIBUTE + USER_TYPE + USER_TYPE + PsiElement(IDENTIFIER)('s') + PsiElement(COMMA)(',') + ATTRIBUTE + USER_TYPE + USER_TYPE + PsiElement(IDENTIFIER)('fd') + PsiErrorElement:Expecting ']' to close an attribute annotation + + PsiWhiteSpace(' ') + PsiErrorElement:Expecting namespace or top level declaration + PsiElement(IDENTIFIER)('d') + PsiErrorElement:Expecting namespace or top level declaration + PsiElement(RBRACKET)(']') + PsiWhiteSpace('\n') + CLASS + MODIFIER_LIST + PsiElement(out)('out') + PsiWhiteSpace('\n') + PsiElement(ref)('ref') + PsiWhiteSpace('\n ') + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('Bar') + TYPE_PARAMETER_LIST + PsiElement(LT)('<') + TYPE_PARAMETER + MODIFIER_LIST + PsiElement(abstract)('abstract') + PsiWhiteSpace('\n') + PsiElement(virtual)('virtual') + PsiWhiteSpace('\n') + PsiElement(enum)('enum') + PsiWhiteSpace('\n') + PsiElement(open)('open') + PsiWhiteSpace('\n') + PsiElement(attribute)('attribute') + PsiWhiteSpace('\n') + PsiElement(override)('override') + PsiWhiteSpace('\n') + PsiElement(virtual)('virtual') + PsiWhiteSpace('\n') + PsiElement(abstract)('abstract') + PsiWhiteSpace('\n') + ATTRIBUTE_ANNOTATION + PsiElement(LBRACKET)('[') + ATTRIBUTE + USER_TYPE + USER_TYPE + PsiElement(IDENTIFIER)('sdfsd') + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + ATTRIBUTE + USER_TYPE + USER_TYPE + PsiElement(IDENTIFIER)('sdfsd') + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + ATTRIBUTE + USER_TYPE + USER_TYPE + PsiElement(IDENTIFIER)('a') + PsiElement(DOT)('.') + USER_TYPE + PsiElement(IDENTIFIER)('b') + PsiElement(DOT)('.') + USER_TYPE + PsiElement(IDENTIFIER)('f') + PsiElement(DOT)('.') + USER_TYPE + PsiElement(IDENTIFIER)('c') + PsiElement(RBRACKET)(']') + PsiWhiteSpace('\n') + PsiElement(private)('private') + PsiWhiteSpace('\n') + PsiElement(protected)('protected') + PsiWhiteSpace('\n') + PsiElement(public)('public') + PsiWhiteSpace('\n') + PsiElement(internal)('internal') + PsiWhiteSpace('\n') + PsiElement(lazy)('lazy') + PsiWhiteSpace('\n') + PsiElement(in)('in') + PsiWhiteSpace('\n') + PsiElement(out)('out') + PsiWhiteSpace('\n') + PsiElement(ref)('ref') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('T') + PsiElement(GT)('>') + PsiWhiteSpace(' ') + CLASS_BODY + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n') + PsiElement(RBRACE)('}') \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/parsing/JetParsingTest.java b/idea/tests/org/jetbrains/jet/parsing/JetParsingTest.java index 359df8c6ca0..673b9ed8212 100644 --- a/idea/tests/org/jetbrains/jet/parsing/JetParsingTest.java +++ b/idea/tests/org/jetbrains/jet/parsing/JetParsingTest.java @@ -35,4 +35,6 @@ public class JetParsingTest extends ParsingTestCase { public void testNamespaceBlock_ERR() throws Exception {doTest(true);} public void testNamespaceBlock() throws Exception {doTest(true);} public void testSimpleModifiers() throws Exception {doTest(true);} + public void testAttributes() throws Exception {doTest(true);} + public void testAttributes_ERR() throws Exception {doTest(true);} }