From 3d218521348b4557bd2361c0ea00510344b1abc0 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Thu, 16 Dec 2010 23:16:32 +0300 Subject: [PATCH] Properties. Recovery needs to be fixed --- grammar/src/class_members.grm | 16 +- idea/src/org/jetbrains/jet/JetNodeTypes.java | 3 +- .../jet/lang/parsing/AbstractJetParsing.java | 20 ++- .../jet/lang/parsing/JetParsing.java | 159 +++++++++++++++++- idea/testData/psi/Properties.jet | 28 +++ idea/testData/psi/Properties_ERR.jet | 17 ++ .../jetbrains/jet/parsing/JetParsingTest.java | 2 + 7 files changed, 218 insertions(+), 27 deletions(-) create mode 100644 idea/testData/psi/Properties.jet create mode 100644 idea/testData/psi/Properties_ERR.jet diff --git a/grammar/src/class_members.grm b/grammar/src/class_members.grm index 210d3bea0d7..3011aa36ded 100644 --- a/grammar/src/class_members.grm +++ b/grammar/src/class_members.grm @@ -67,7 +67,7 @@ initializers ; block - : "{" expression "}" + : "{" (expression ";"?)* "}" ; decomposer // TODO: consider other names @@ -75,11 +75,7 @@ decomposer // TODO: consider other names ; function - : modifiers "fun" (type ".")? functionRest - ; - -functionRest - : attributes SimpleName typeParameters? functionParameters (":" type)? functionBody? + : modifiers "fun" (type ".")? attributes/*for receiver type*/ typeParameters? functionParameters (":" type)? functionBody? ; functionBody @@ -88,12 +84,8 @@ functionBody ; property - : modifiers ("val" | "var") (type ".")? propertyRest - ; - -propertyRest - : SimpleName (":" type)? ("=" expression)? - "{" getter? setter? "}" + : modifiers ("val" | "var") attributes (type ".")? SimpleName (":" type)? ("=" expression)? + "{" getter? setter? "}" ; getter diff --git a/idea/src/org/jetbrains/jet/JetNodeTypes.java b/idea/src/org/jetbrains/jet/JetNodeTypes.java index bf7c89af2d3..a3bfe6f4498 100644 --- a/idea/src/org/jetbrains/jet/JetNodeTypes.java +++ b/idea/src/org/jetbrains/jet/JetNodeTypes.java @@ -45,7 +45,8 @@ public interface JetNodeTypes { JetNodeType VALUE_PARAMETER = new JetNodeType("VALUE_PARAMETER"); JetNodeType FUNCTION_TYPE = new JetNodeType("FUNCTION_TYPE"); JetNodeType DECOMPOSER_PROPERTY_LIST = new JetNodeType("DECOMPOSER_PROPERTY_LIST"); - + JetNodeType RECEIVER_TYPE_ATTRIBUTES = new JetNodeType("RECEIVER_TYPE_ATTRIBUTES"); + JetNodeType PROPERTY_GETTER = new JetNodeType("PROPERTY_GETTER"); 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 42ebfcd1967..2c51919f8a1 100644 --- a/idea/src/org/jetbrains/jet/lang/parsing/AbstractJetParsing.java +++ b/idea/src/org/jetbrains/jet/lang/parsing/AbstractJetParsing.java @@ -1,6 +1,7 @@ package org.jetbrains.jet.lang.parsing; import com.intellij.lang.PsiBuilder; +import com.intellij.psi.TokenType; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import org.jetbrains.jet.lexer.JetKeywordToken; @@ -42,7 +43,7 @@ import static org.jetbrains.jet.lexer.JetTokens.*; } protected void errorWithRecovery(String message, TokenSet recoverySet) { - JetToken tt = tt(); + IElementType tt = tt(); if (recoverySet == null || recoverySet.contains(tt) || (recoverySet.contains(EOL_OR_SEMICOLON) && (tt == SEMICOLON || myBuilder.eolInLastWhitespace()))) { @@ -68,12 +69,15 @@ import static org.jetbrains.jet.lexer.JetTokens.*; myBuilder.advanceLexer(); } - protected JetToken tt() { - return (JetToken) myBuilder.getTokenType(); + protected IElementType tt() { + IElementType tokenType = myBuilder.getTokenType(); + // TODO: review + if (tokenType == TokenType.BAD_CHARACTER) errorAndAdvance("Bad character"); + return tokenType; } protected boolean at(final IElementType expectation) { - JetToken token = tt(); + IElementType token = tt(); if (token == expectation) return true; if (expectation == EOL_OR_SEMICOLON) { if (token == SEMICOLON) return true; @@ -93,7 +97,7 @@ import static org.jetbrains.jet.lexer.JetTokens.*; PsiBuilder.Marker tmp = mark(); for (int i = 0; i < k; i++) advance(); - JetToken tt = tt(); + IElementType tt = tt(); tmp.rollbackTo(); return tt; } @@ -108,4 +112,10 @@ import static org.jetbrains.jet.lexer.JetTokens.*; } } + protected void errorUntil(String mesage, TokenSet tokenSet) { + PsiBuilder.Marker error = mark(); + skipUntil(tokenSet); + error.error(mesage); + } + } diff --git a/idea/src/org/jetbrains/jet/lang/parsing/JetParsing.java b/idea/src/org/jetbrains/jet/lang/parsing/JetParsing.java index b201eac04ea..653b9522a4b 100644 --- a/idea/src/org/jetbrains/jet/lang/parsing/JetParsing.java +++ b/idea/src/org/jetbrains/jet/lang/parsing/JetParsing.java @@ -171,7 +171,7 @@ public class JetParsing extends AbstractJetParsing { PsiBuilder.Marker decl = mark(); parseModifierList(); - JetToken keywordToken = tt(); + IElementType keywordToken = tt(); JetNodeType declType = null; if (keywordToken == NAMESPACE_KEYWORD) { declType = parseNamespaceBlock(); @@ -471,14 +471,97 @@ public class JetParsing extends AbstractJetParsing { /* * property - * : modifiers ("val" | "var") (type ".")? propertyRest + * : modifiers ("val" | "var") attributes (type ".")? SimpleName (":" type)? ("=" expression)? + * ("{" getter? setter? "}")? * ; */ private JetNodeType parseProperty() { - advance(); // TODO + assert at(VAL_KEYWORD) || at(VAR_KEYWORD); + + advance(); // VAL_KEYWORD or VAR_KEYWORD + + PsiBuilder.Marker receiverTypeAttributes = mark(); + parseAttributeList(); + + TokenSet propertyNameFollow = TokenSet.create(COLON, EQ, LBRACE, EOL_OR_SEMICOLON); + if (at(IDENTIFIER) && propertyNameFollow.contains(lookahead(1))) { + // val [a] name = foo + receiverTypeAttributes.done(RECEIVER_TYPE_ATTRIBUTES); + advance(); // IDENTIFIER + } + else { + receiverTypeAttributes.rollbackTo(); + if (!TYPE_REF_FIRST.contains(tt())) { + errorAndAdvance("Expecting receiver type or property name"); + } + else { + // TODO: if this type is annotated with an attribute, and it is a single identifier, it is a error (fun [a] foo()) + parseTypeRef(); + // The property name may appear as the last section of the type + if (at(DOT)) { + advance(); // DOT + expect(IDENTIFIER, "Expecting property name", propertyNameFollow); + } + } + } + + if (at(COLON)) { + advance(); // COLON + parseTypeRef(); + } + + if (at(EQ)) { + advance(); // EQ + myExpressionParsing.parseExpression(); + } + + if (!at(EOL_OR_SEMICOLON) && at(LBRACE)) { + advance(); // LBRACE + + // TODO: review + // TODO: $field = foo or something like this + parsePropertyGetterOrSetter(); + if (!at(RBRACE)) parsePropertyGetterOrSetter(); + + if (!at(RBRACE)) { + errorUntil("Expecting '}'", TokenSet.create(RBRACE)); + } + + expect(RBRACE, "Expecting '}'"); + } + + consumeIf(SEMICOLON); + return PROPERTY; } + /* + * getter + * : modifiers + * ( "get" "(" ")" + * | + * "set" "(" modifiers parameter ")" + * ) functionBody + * ; + */ + private void parsePropertyGetterOrSetter() { + PsiBuilder.Marker getter = mark(); + + parseModifierList(); + + if (!at(GET_KEYWORD) && !at(SET_KEYWORD)) { + errorWithRecovery("Expecting 'get' or 'set'", TokenSet.create(LPAR, RBRACE)); + } + else { + advance(); // GET_KEYWORD or SET_KEYWORD + } + parseValueParameterList(false); + + parseFunctionBody(); + + getter.done(PROPERTY_GETTER); + } + /* * function * : modifiers "fun" (type ".")? functionRest @@ -493,6 +576,43 @@ public class JetParsing extends AbstractJetParsing { return FUN; } + /* + * functionBody + * : block + * : "=" expression + * ; + */ + private void parseFunctionBody() { + if (at(LBRACE)) { + parseBlock(); + } + else if (at(EQ)) { + advance(); // EQ + myExpressionParsing.parseExpression(); + } + else { + error("Expecting function body"); + } + } + + /* + * block + * : "{" (expression ";"?)* "}" + * ; + */ + private void parseBlock() { + assert at(LBRACE); + + advance(); // LBRACE + + while (!eof() && !at(RBRACE)) { + myExpressionParsing.parseExpression(); + consumeIf(SEMICOLON); + } + + expect(RBRACE, "Expecting '}"); + } + /* * extension * : modifiers "extension" SimpleName? typeParameters? "for" type classBody? // properties cannot be lazy, cannot have backing fields @@ -830,14 +950,39 @@ public class JetParsing extends AbstractJetParsing { * ; */ private void parseFunctionTypeContents() { + parseValueParameterList(true); + + expect(COLON, "Expecting ':' followed by a return type", TYPE_REF_FIRST); + + parseTypeRef(); + } + + /* + * functionParameters + * : "(" functionParameter{","}? ")" // default values + * ; + * + * functionParameter + * : modifiers functionParameterRest + * ; + * + * functionParameterRest + * : parameter ("=" expression)? + * ; + */ + private void parseValueParameterList(boolean isFunctionTypeContents) { PsiBuilder.Marker parameters = mark(); expect(LPAR, "Expecting '("); if (!at(RPAR)) { while (true) { if (!parseValueParameter()) { - parseModifierList(); // lazy, out, ref - parseTypeRef(); + if (isFunctionTypeContents) { + parseModifierList(); // lazy, out, ref + parseTypeRef(); + } else { + errorAndAdvance("Expecting a parameter declaration"); + } } if (!at(COMMA)) break; advance(); // COMMA @@ -845,10 +990,6 @@ public class JetParsing extends AbstractJetParsing { } expect(RPAR, "Expecting ')'"); parameters.done(VALUE_PARAMETER_LIST); - - expect(COLON, "Expecting ':' followed by a return type", TYPE_REF_FIRST); - - parseTypeRef(); } /* diff --git a/idea/testData/psi/Properties.jet b/idea/testData/psi/Properties.jet new file mode 100644 index 00000000000..99c5123e36b --- /dev/null +++ b/idea/testData/psi/Properties.jet @@ -0,0 +1,28 @@ + val foo + val [a] foo + val foo.bar + val foo.bar.{() : ()}.foo + + val foo : T + val [a] foo = bar + val foo.bar { + get() {} + set() = foo + } + val foo.bar.{() : ()}.foo = foo { + get() {} + set() {} + } + + val foo.bar.{() : ()}.foo : bar = foo { + [a] public get() {} + virtual set(a : b) {} + } + + val foo.bar.{() : ()}.foo : bar = foo { + virtual set(a : b) {} + } + + val foo.bar.{() : ()}.foo : bar = foo { + [a] public get() {} + } diff --git a/idea/testData/psi/Properties_ERR.jet b/idea/testData/psi/Properties_ERR.jet new file mode 100644 index 00000000000..5065b4aab7e --- /dev/null +++ b/idea/testData/psi/Properties_ERR.jet @@ -0,0 +1,17 @@ +val foo : +val [a foo = foo +val foo.bar. +val [a] foo : = bar +val foo.bar { + public () {} + () = foo +} +val foo.bar.{() : ()}.foo = foo { + dfget() {} + set) {} +} +val foo.bar.{() : ()}.foo = foo { + get(foo) {} + set() {} + set() {} +} diff --git a/idea/tests/org/jetbrains/jet/parsing/JetParsingTest.java b/idea/tests/org/jetbrains/jet/parsing/JetParsingTest.java index 0f17e93d343..67f087c2549 100644 --- a/idea/tests/org/jetbrains/jet/parsing/JetParsingTest.java +++ b/idea/tests/org/jetbrains/jet/parsing/JetParsingTest.java @@ -46,4 +46,6 @@ public class JetParsingTest extends ParsingTestCase { public void testFunctionTypes_ERR() throws Exception {doTest(true);} public void testDecomposers() throws Exception {doTest(true);} public void testDecomposers_ERR() throws Exception {doTest(true);} + public void testProperties() throws Exception {doTest(true);} + public void testProperties_ERR() throws Exception {doTest(true);} }