From beded22df54843607112f357c8445ea5f174ecd2 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Thu, 16 Dec 2010 21:59:48 +0300 Subject: [PATCH] Decomposers --- grammar/src/class_members.grm | 2 +- idea/src/org/jetbrains/jet/JetNodeTypes.java | 1 + .../jet/lang/parsing/AbstractJetParsing.java | 16 +- .../lang/parsing/JetExpressionParsing.java | 51 +++++ .../jet/lang/parsing/JetParsing.java | 111 ++++++----- idea/testData/psi/Decomposers.jet | 6 + idea/testData/psi/Decomposers.txt | 103 ++++++++++ idea/testData/psi/Decomposers_ERR.jet | 10 + idea/testData/psi/Decomposers_ERR.txt | 177 ++++++++++++++++++ .../jetbrains/jet/parsing/JetParsingTest.java | 2 + 10 files changed, 426 insertions(+), 53 deletions(-) create mode 100644 idea/testData/psi/Decomposers.jet create mode 100644 idea/testData/psi/Decomposers.txt create mode 100644 idea/testData/psi/Decomposers_ERR.jet create mode 100644 idea/testData/psi/Decomposers_ERR.txt diff --git a/grammar/src/class_members.grm b/grammar/src/class_members.grm index 4fb85050718..210d3bea0d7 100644 --- a/grammar/src/class_members.grm +++ b/grammar/src/class_members.grm @@ -71,7 +71,7 @@ block ; decomposer // TODO: consider other names - : attributes "decomposer" (type ".")? SimpleName "(" (attributes SimpleName){","} ")" // Public properties only + : modifiers "decomposer" (type ".")? SimpleName? "(" (attributes SimpleName){","}? ")" // Public properties only ; function diff --git a/idea/src/org/jetbrains/jet/JetNodeTypes.java b/idea/src/org/jetbrains/jet/JetNodeTypes.java index b9948101c82..bf7c89af2d3 100644 --- a/idea/src/org/jetbrains/jet/JetNodeTypes.java +++ b/idea/src/org/jetbrains/jet/JetNodeTypes.java @@ -44,6 +44,7 @@ public interface JetNodeTypes { JetNodeType TUPLE_TYPE = new JetNodeType("TUPLE_TYPE"); JetNodeType VALUE_PARAMETER = new JetNodeType("VALUE_PARAMETER"); JetNodeType FUNCTION_TYPE = new JetNodeType("FUNCTION_TYPE"); + JetNodeType DECOMPOSER_PROPERTY_LIST = new JetNodeType("DECOMPOSER_PROPERTY_LIST"); IElementType NAMESPACE_NAME = new JetNodeType("NAMESPACE_NAME"); diff --git a/idea/src/org/jetbrains/jet/lang/parsing/AbstractJetParsing.java b/idea/src/org/jetbrains/jet/lang/parsing/AbstractJetParsing.java index 815ec4e0fcd..42ebfcd1967 100644 --- a/idea/src/org/jetbrains/jet/lang/parsing/AbstractJetParsing.java +++ b/idea/src/org/jetbrains/jet/lang/parsing/AbstractJetParsing.java @@ -9,11 +9,7 @@ import org.jetbrains.jet.lexer.JetToken; import static org.jetbrains.jet.lexer.JetTokens.*; /** - * Created by IntelliJ IDEA. - * User: user - * Date: 12/16/10 - * Time: 6:34 PM - * To change this template use File | Settings | File Templates. + * @author abreslav */ /*package*/ class AbstractJetParsing { protected final SemanticWitespaceAwarePsiBuilder myBuilder; @@ -57,10 +53,11 @@ import static org.jetbrains.jet.lexer.JetTokens.*; } } - protected void errorAndAdvance(String message) { + protected boolean errorAndAdvance(String message) { PsiBuilder.Marker err = mark(); advance(); // erroneous token err.error(message); + return false; } protected boolean eof() { @@ -104,4 +101,11 @@ import static org.jetbrains.jet.lexer.JetTokens.*; protected void consumeIf(JetToken token) { if (at(token)) advance(); // token } + + protected void skipUntil(TokenSet tokenSet) { + while (!eof() && !tokenSet.contains(tt())) { + advance(); + } + } + } diff --git a/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java b/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java index 4b2e695917b..043e07cfe4c 100644 --- a/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java +++ b/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java @@ -1,5 +1,13 @@ package org.jetbrains.jet.lang.parsing; +import com.intellij.lang.PsiBuilder; +import org.jetbrains.jet.JetNodeType; + +import static org.jetbrains.jet.JetNodeTypes.NAMED_ARGUMENT; +import static org.jetbrains.jet.JetNodeTypes.VALUE_ARGUMENT; +import static org.jetbrains.jet.JetNodeTypes.VALUE_ARGUMENT_LIST; +import static org.jetbrains.jet.lexer.JetTokens.*; + /** * @author abreslav */ @@ -36,4 +44,47 @@ public class JetExpressionParsing extends AbstractJetParsing { advance(); // TODO } + /* + * valueArguments + * : "(" (SimpleName "=")? ("out" | "ref")? expression{","} ")" + * ; + */ + public 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(); + JetNodeType type = VALUE_ARGUMENT; + if (at(IDENTIFIER) && lookahead(1) == EQ) { + advance(); // IDENTIFIER + advance(); // EQ + type = NAMED_ARGUMENT; + } + if (at(OUT_KEYWORD) || at(REF_KEYWORD)) advance(); // REF or OUT + parseExpression(); + argument.done(type); + } + + + } diff --git a/idea/src/org/jetbrains/jet/lang/parsing/JetParsing.java b/idea/src/org/jetbrains/jet/lang/parsing/JetParsing.java index 51854e7319a..b201eac04ea 100644 --- a/idea/src/org/jetbrains/jet/lang/parsing/JetParsing.java +++ b/idea/src/org/jetbrains/jet/lang/parsing/JetParsing.java @@ -16,7 +16,13 @@ import java.util.Map; import static org.jetbrains.jet.JetNodeTypes.*; import static org.jetbrains.jet.lexer.JetTokens.*; +/** + * @author max + * @author abreslav + */ public class JetParsing extends AbstractJetParsing { + // TODO: token sets to constants + private static final Map MODIFIER_KEYWORD_MAP = new HashMap(); static { for (IElementType softKeyword : MODIFIER_KEYWORDS.getTypes()) { @@ -200,11 +206,63 @@ public class JetParsing extends AbstractJetParsing { /* * decomposer - * : attributes "decomposer" (type ".")? SimpleName "(" (attributes SimpleName){","} ")" // Public properties only + * : modifiers "decomposer" (type ".")? SimpleName? "(" (attributes SimpleName){","}? ")" // Public properties only * ; */ private JetNodeType parseDecomposer() { - // TODO + assert at(DECOMPOSER_KEYWORD); + advance(); // DECOMPOSER_KEYWORD + + boolean extenstion; + if (!at(LPAR)) { + extenstion = true; + if (TYPE_REF_FIRST.contains(tt()) + && !(at(IDENTIFIER) && lookahead(1) == LPAR)) { + // TODO: if this type is annotated with an attribute, and it is a single identifier, it is a error (decomposer [a] foo()) + parseTypeRef(); + // The decomposer name may appear as the last section of the type + if (at(DOT)) { + advance(); // DOT + expect(IDENTIFIER, "Expecting decomposer name", TokenSet.create(LPAR)); + } + } + else { + consumeIf(IDENTIFIER); + } + } else { + extenstion = false; + } + + PsiBuilder.Marker properties = mark(); + + expect(LPAR, "Expecting a property list in parentheses '( ... )'"); + + if (!at(RPAR)) { + while (true) { + parseAttributeList(); + if (!expect(IDENTIFIER, "Expecting a property name", TokenSet.create(COMMA, RPAR))) { + skipUntil(TokenSet.create(COMMA, RPAR, EOL_OR_SEMICOLON)); + } + if (!at(COMMA)) { + if (at(RPAR) || at(EOL_OR_SEMICOLON)) break; + error("Expecting a property name or a closing ')'"); + skipUntil(TokenSet.create(COMMA, RPAR, EOL_OR_SEMICOLON)); + } + if (!at(COMMA)) break; + advance(); // COMMA + } + } + + expect(RPAR, "Expecting ')' to close a property list"); + + consumeIf(SEMICOLON); + + properties.done(DECOMPOSER_PROPERTY_LIST); + + if (at(DOT) && !extenstion) { + error("Cannot define an extension decomposer on a tuple"); + } + return DECOMPOSER; } @@ -295,51 +353,10 @@ public class JetParsing extends AbstractJetParsing { private void parseAttribute() { PsiBuilder.Marker attribute = mark(); parseUserType(); - if (at(LPAR)) parseValueArgumentList(); + if (at(LPAR)) myExpressionParsing.parseValueArgumentList(); attribute.done(ATTRIBUTE); } - /* - * 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(); - JetNodeType type = VALUE_ARGUMENT; - if (at(IDENTIFIER) && lookahead(1) == EQ) { - advance(); // IDENTIFIER - advance(); // EQ - type = NAMED_ARGUMENT; - } - if (at(OUT_KEYWORD) || at(REF_KEYWORD)) advance(); // REF or OUT - myExpressionParsing.parseExpression(); - argument.done(type); - } - /* * namespace * : "namespace" SimpleName{"."} "{" @@ -535,7 +552,7 @@ public class JetParsing extends AbstractJetParsing { delegator.done(DELEGATOR_BY); } else if (at(LPAR)) { - parseValueArgumentList(); + myExpressionParsing.parseValueArgumentList(); delegator.done(DELEGATOR_SUPER_CALL); } else { @@ -762,7 +779,9 @@ public class JetParsing extends AbstractJetParsing { if (!at(RPAR)) { while (true) { - if (at(COLON)) errorAndAdvance("Expecting a name for tuple entry"); + if (at(COLON)) { + errorAndAdvance("Expecting a name for tuple entry"); + } if (at(IDENTIFIER) && lookahead(1) == COLON) { PsiBuilder.Marker labeledEntry = mark(); diff --git a/idea/testData/psi/Decomposers.jet b/idea/testData/psi/Decomposers.jet new file mode 100644 index 00000000000..82fe053adb4 --- /dev/null +++ b/idea/testData/psi/Decomposers.jet @@ -0,0 +1,6 @@ +decomposer (a, a) +decomposer () +decomposer foo.{() : ()}.bar(a, a) +decomposer a.foobar() +[a] decomposer foobar([a] s) +decomposer foobar() diff --git a/idea/testData/psi/Decomposers.txt b/idea/testData/psi/Decomposers.txt new file mode 100644 index 00000000000..a9a3dae2a62 --- /dev/null +++ b/idea/testData/psi/Decomposers.txt @@ -0,0 +1,103 @@ +JetFile: Decomposers.jet + NAMESPACE + DECOMPOSER + PsiElement(decomposer)('decomposer') + PsiWhiteSpace(' ') + DECOMPOSER_PROPERTY_LIST + PsiElement(LPAR)('(') + PsiElement(IDENTIFIER)('a') + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('a') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n') + DECOMPOSER + PsiElement(decomposer)('decomposer') + PsiWhiteSpace(' ') + DECOMPOSER_PROPERTY_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n') + DECOMPOSER + PsiElement(decomposer)('decomposer') + PsiWhiteSpace(' ') + TYPE_REFERENCE + USER_TYPE + PsiElement(IDENTIFIER)('foo') + TYPE_ARGUMENT_LIST + PsiElement(LT)('<') + TYPE_REFERENCE + USER_TYPE + PsiElement(IDENTIFIER)('T') + PsiElement(GT)('>') + PsiElement(DOT)('.') + FUNCTION_TYPE + PsiElement(LBRACE)('{') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + PsiElement(COLON)(':') + PsiWhiteSpace(' ') + TYPE_REFERENCE + TUPLE_TYPE + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiElement(RBRACE)('}') + PsiElement(DOT)('.') + USER_TYPE + PsiElement(IDENTIFIER)('bar') + DECOMPOSER_PROPERTY_LIST + PsiElement(LPAR)('(') + PsiElement(IDENTIFIER)('a') + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('a') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n') + DECOMPOSER + PsiElement(decomposer)('decomposer') + PsiWhiteSpace(' ') + TYPE_REFERENCE + USER_TYPE + PsiElement(IDENTIFIER)('a') + PsiElement(DOT)('.') + USER_TYPE + PsiElement(IDENTIFIER)('foobar') + DECOMPOSER_PROPERTY_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n') + DECOMPOSER + MODIFIER_LIST + ATTRIBUTE_ANNOTATION + PsiElement(LBRACKET)('[') + ATTRIBUTE + USER_TYPE + USER_TYPE + PsiElement(IDENTIFIER)('a') + PsiElement(RBRACKET)(']') + PsiWhiteSpace(' ') + PsiElement(decomposer)('decomposer') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('foobar') + DECOMPOSER_PROPERTY_LIST + PsiElement(LPAR)('(') + ATTRIBUTE_ANNOTATION + PsiElement(LBRACKET)('[') + ATTRIBUTE + USER_TYPE + USER_TYPE + PsiElement(IDENTIFIER)('a') + PsiElement(RBRACKET)(']') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('s') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n') + DECOMPOSER + PsiElement(decomposer)('decomposer') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('foobar') + DECOMPOSER_PROPERTY_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') \ No newline at end of file diff --git a/idea/testData/psi/Decomposers_ERR.jet b/idea/testData/psi/Decomposers_ERR.jet new file mode 100644 index 00000000000..317cb3908a6 --- /dev/null +++ b/idea/testData/psi/Decomposers_ERR.jet @@ -0,0 +1,10 @@ +decomposer (a a) +decomposer foo.{() : ()}.bar(, a) +decomposer a.foobar) +[a] decomposer foobar([a] s, ) +decomposer foobar([a]) + + +decomposer (a, a). +decomposer (). +decomposer foo.{() : ()}.bar(a, a). diff --git a/idea/testData/psi/Decomposers_ERR.txt b/idea/testData/psi/Decomposers_ERR.txt new file mode 100644 index 00000000000..83777aa0d96 --- /dev/null +++ b/idea/testData/psi/Decomposers_ERR.txt @@ -0,0 +1,177 @@ +JetFile: Decomposers_ERR.jet + NAMESPACE + DECOMPOSER + PsiElement(decomposer)('decomposer') + PsiWhiteSpace(' ') + DECOMPOSER_PROPERTY_LIST + PsiElement(LPAR)('(') + PsiElement(IDENTIFIER)('a') + PsiErrorElement:Expecting a property name or a closing ')' + + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('a') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n') + DECOMPOSER + PsiElement(decomposer)('decomposer') + PsiWhiteSpace(' ') + TYPE_REFERENCE + USER_TYPE + PsiElement(IDENTIFIER)('foo') + TYPE_ARGUMENT_LIST + PsiElement(LT)('<') + TYPE_REFERENCE + USER_TYPE + PsiElement(IDENTIFIER)('T') + PsiElement(GT)('>') + PsiElement(DOT)('.') + FUNCTION_TYPE + PsiElement(LBRACE)('{') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + PsiElement(COLON)(':') + PsiWhiteSpace(' ') + TYPE_REFERENCE + TUPLE_TYPE + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiElement(RBRACE)('}') + PsiElement(DOT)('.') + USER_TYPE + PsiElement(IDENTIFIER)('bar') + DECOMPOSER_PROPERTY_LIST + PsiElement(LPAR)('(') + PsiErrorElement:Expecting a property name + + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('a') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n') + DECOMPOSER + PsiElement(decomposer)('decomposer') + PsiWhiteSpace(' ') + TYPE_REFERENCE + USER_TYPE + PsiElement(IDENTIFIER)('a') + PsiElement(DOT)('.') + USER_TYPE + PsiElement(IDENTIFIER)('foobar') + DECOMPOSER_PROPERTY_LIST + PsiErrorElement:Expecting a property list in parentheses '( ... )' + + PsiElement(RPAR)(')') + PsiWhiteSpace('\n') + DECOMPOSER + MODIFIER_LIST + ATTRIBUTE_ANNOTATION + PsiElement(LBRACKET)('[') + ATTRIBUTE + USER_TYPE + USER_TYPE + PsiElement(IDENTIFIER)('a') + PsiElement(RBRACKET)(']') + PsiWhiteSpace(' ') + PsiElement(decomposer)('decomposer') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('foobar') + DECOMPOSER_PROPERTY_LIST + PsiElement(LPAR)('(') + ATTRIBUTE_ANNOTATION + PsiElement(LBRACKET)('[') + ATTRIBUTE + USER_TYPE + USER_TYPE + PsiElement(IDENTIFIER)('a') + PsiElement(RBRACKET)(']') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('s') + PsiElement(COMMA)(',') + PsiErrorElement:Expecting a property name + + PsiWhiteSpace(' ') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n') + DECOMPOSER + PsiElement(decomposer)('decomposer') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('foobar') + DECOMPOSER_PROPERTY_LIST + PsiElement(LPAR)('(') + ATTRIBUTE_ANNOTATION + PsiElement(LBRACKET)('[') + ATTRIBUTE + USER_TYPE + USER_TYPE + PsiElement(IDENTIFIER)('a') + PsiElement(RBRACKET)(']') + PsiErrorElement:Expecting a property name + + PsiElement(RPAR)(')') + PsiWhiteSpace('\n\n\n') + DECOMPOSER + PsiElement(decomposer)('decomposer') + PsiWhiteSpace(' ') + DECOMPOSER_PROPERTY_LIST + PsiElement(LPAR)('(') + PsiElement(IDENTIFIER)('a') + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('a') + PsiElement(RPAR)(')') + PsiErrorElement:Cannot define an extension decomposer on a tuple + + PsiErrorElement:Expecting namespace or top level declaration + PsiElement(DOT)('.') + PsiWhiteSpace('\n') + DECOMPOSER + PsiElement(decomposer)('decomposer') + PsiWhiteSpace(' ') + DECOMPOSER_PROPERTY_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiErrorElement:Cannot define an extension decomposer on a tuple + + PsiErrorElement:Expecting namespace or top level declaration + PsiElement(DOT)('.') + PsiWhiteSpace('\n') + DECOMPOSER + PsiElement(decomposer)('decomposer') + PsiWhiteSpace(' ') + TYPE_REFERENCE + USER_TYPE + PsiElement(IDENTIFIER)('foo') + TYPE_ARGUMENT_LIST + PsiElement(LT)('<') + TYPE_REFERENCE + USER_TYPE + PsiElement(IDENTIFIER)('T') + PsiElement(GT)('>') + PsiElement(DOT)('.') + FUNCTION_TYPE + PsiElement(LBRACE)('{') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + PsiElement(COLON)(':') + PsiWhiteSpace(' ') + TYPE_REFERENCE + TUPLE_TYPE + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiElement(RBRACE)('}') + PsiElement(DOT)('.') + USER_TYPE + PsiElement(IDENTIFIER)('bar') + DECOMPOSER_PROPERTY_LIST + PsiElement(LPAR)('(') + PsiElement(IDENTIFIER)('a') + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('a') + PsiElement(RPAR)(')') + PsiErrorElement:Expecting namespace or top level declaration + PsiElement(DOT)('.') \ 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 f21f5a94fc3..0f17e93d343 100644 --- a/idea/tests/org/jetbrains/jet/parsing/JetParsingTest.java +++ b/idea/tests/org/jetbrains/jet/parsing/JetParsingTest.java @@ -44,4 +44,6 @@ public class JetParsingTest extends ParsingTestCase { public void testTupleTypes_ERR() throws Exception {doTest(true);} public void testFunctionTypes() throws Exception {doTest(true);} public void testFunctionTypes_ERR() throws Exception {doTest(true);} + public void testDecomposers() throws Exception {doTest(true);} + public void testDecomposers_ERR() throws Exception {doTest(true);} }