From 300af58774059bcb2b32acaa4546ba551bedc334 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Wed, 29 Dec 2010 23:39:23 +0300 Subject: [PATCH] Binary operations implemented. Some bugs are caused by incorrect treatment of semantic EOLs when calling rollbackTo() on a marker --- grammar/src/expressions.grm | 164 +++++++---- grammar/src/match.grm | 4 - idea/src/org/jetbrains/jet/JetNodeTypes.java | 7 + .../lang/parsing/JetExpressionParsing.java | 268 +++++++++++++++--- idea/src/org/jetbrains/jet/lexer/Jet.flex | 3 +- .../org/jetbrains/jet/lexer/JetTokens.java | 1 + 6 files changed, 342 insertions(+), 105 deletions(-) diff --git a/grammar/src/expressions.grm b/grammar/src/expressions.grm index 357a78e56ad..710c5548f81 100644 --- a/grammar/src/expressions.grm +++ b/grammar/src/expressions.grm @@ -1,12 +1,96 @@ +/* + +Decreasing precedence: + memberAccessOperation + postfixUnaryOperation + prefixUnaryOperation + multiplicativeOperation + additiveOperation + ".." + SimpleName + "?:" + namedInfixOrTypeOperation + comparisonOperation + equalityOperation + "&&" + "||" + "match" + "->" + assignmentOperator + +*/ + + expression - : attributes expression + : arrowTupleExpression (assignmentOperator arrowTupleExpression)? + ; + +arrowTupleExpression + : match ("->" match)* + ; + +match + : conjunction ("match" "{" matchEntry+ "}")* + ; + +disjunction + : conjunction ("||" conjunction)* + ; + +conjunction + : equalityComparison ("&&" equalityComparison)* + ; + +equalityComparison + : comparison (equalityOperation comparison)* + ; + +comparison + : namedInfixOrTypeExpression (comparisonOperation namedInfixOrTypeExpression)* + ; + +namedInfixOrTypeExpression + : elvisOperation (namedInfixOrTypeOperation elvisOperation)* + ; + +elvisOperation + : infixFunctionCall ("?:" infixFunctionCall)* + ; + +infixFunctionCall + : rangeExpression (SimpleName rangeExpression)* + ; + +rangeExpression + : additiveExpression (".." additiveExpression)* + ; + +additiveExpression + : multiplicativeExpression (additiveOperation multiplicativeExpression)* + ; + +multiplicativeExpression + : prefixUnaryExpression (multiplicativeOperation prefixUnaryExpression)* + ; + +prefixUnaryExpression + : prefixUnaryOperation postfixUnaryExpression + ; + +postfixUnaryExpression + : memberAccess postfixUnaryOperation + ; + +memberAccess + : atomicExpression memberAccessOperation atomicExpression + ; + +atomicExpression : "(" expression ")" // see tupleLiteral : literalConstant : functionLiteral : tupleLiteral : "this" ("<" type ">")? - : expressionWithPrecedences - : match : if : try : "typeof" "(" expression ")" @@ -15,7 +99,7 @@ expression : declaration : jump : loop - // block is syntactically equivalent to a functionLiteral with no parameters + : SimpleName ; literalConstant @@ -36,63 +120,28 @@ declaration : typedef ; -expressionWithPrecedences // See the precedence table, everything associates to the left - : memberLiteral - : memberAccessExpression - : infixFunctionCall - : binOpExpression - : assignment - : unOpExpression - : castExpression - : typingExpression - ; - -memberLiteral - : expression? '#' SimpleName - ; - -memberAccessExpression - : (expression accessOp)? (memberAccess | expression) - ; - -accessOp - : "." - : "?." - ; - -typingExpression - : expression typingOperation type - ; - -typingOperation - : "as" - : "is" - : "isnot" - : ":" - ; - -binOpExpression - : expression binaryOperation expression // see priorities - ; - -binaryOperation // Decreasing precedence +memberAccessOperation : "." : "?." : "#" - // unary + ; + +multiplicativeOperation : "*" : "/" : "%" + ; + +additiveOperation : "+" : "-" - // No << >> >>> - : ".." - : SimpleName - : "?:" + ; + +namedInfixOrTypeOperation : "in" : "is" : "!in" : "!is" : "as" : ":" + ; + +comparisonOperation : "<" : ">" : ">=" : "<=" + ; + +equalityOperation : "!=" : "==" : "===" : "!==" - // No | & ^ ~ - : "&&" - : "||" - : "match" - : "->" - : assignmentOperator ; assignmentOperator @@ -113,10 +162,13 @@ prefixUnaryOperation : "-" : "+" : "++" : "--" : "!" // No ~ + : attributes // mandatory ; postfixUnaryOperation : "++" : "--" + : valueArguments + : arrayAccess ; functionCall @@ -179,7 +231,7 @@ fieldOrPropertyAccess ; arrayAccess - : "[" expression "]" + : "[" expression{","} "]" ; objectLiteral diff --git a/grammar/src/match.grm b/grammar/src/match.grm index d18d5067511..5eb56fe1c61 100644 --- a/grammar/src/match.grm +++ b/grammar/src/match.grm @@ -1,7 +1,3 @@ -match - : expression "match" "{" matchEntry+ "}" - ; - matchEntry : attributes "case" pattern ("if" "(" expression ")")? "=>" expression // TODO: Consider other options than "=>" ; diff --git a/idea/src/org/jetbrains/jet/JetNodeTypes.java b/idea/src/org/jetbrains/jet/JetNodeTypes.java index 35d8d5ce02b..e467c62e6ba 100644 --- a/idea/src/org/jetbrains/jet/JetNodeTypes.java +++ b/idea/src/org/jetbrains/jet/JetNodeTypes.java @@ -90,6 +90,13 @@ public interface JetNodeTypes { JetNodeType RECEIVER_TYPE = new JetNodeType("RECEIVER_TYPE"); JetNodeType FUNCTION_LITERAL = new JetNodeType("FUNCTION_LITERAL"); JetNodeType ANNOTATED_EXPRESSION = new JetNodeType("ANNOTATED_EXPRESSION"); +// JetNodeType SIMPLE_NAME = new JetNodeType("SIMPLE_NAME"); + JetNodeType BINARY_EXPRESSION = new JetNodeType("BINARY_EXPRESSION"); + JetNodeType PREFIX_EXPRESSION = new JetNodeType("PREFIX_EXPRESSION"); + JetNodeType POSTFIX_EXPRESSION = new JetNodeType("POSTFIX_EXPRESSION"); + JetNodeType CALL_EXPRESSION = new JetNodeType("CALL_EXPRESSION"); + JetNodeType ARRAY_ACCESS_EXPRESSION = new JetNodeType("ARRAY_ACCESS_EXPRESSION"); + JetNodeType INDICES = new JetNodeType("INDICES"); IElementType NAMESPACE_NAME = new JetNodeType("NAMESPACE_NAME"); } diff --git a/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java b/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java index 0ba93c33965..4fba0256880 100644 --- a/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java +++ b/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java @@ -4,6 +4,7 @@ import com.intellij.lang.PsiBuilder; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import org.jetbrains.jet.JetNodeType; +import org.jetbrains.jet.lexer.JetTokens; import static org.jetbrains.jet.JetNodeTypes.*; import static org.jetbrains.jet.lexer.JetTokens.*; @@ -19,6 +20,71 @@ public class JetExpressionParsing extends AbstractJetParsing { myJetParsing = jetParsing; } + @SuppressWarnings({"UnusedDeclaration"}) + private enum Priority { + MEMBER_ACCESS(DOT, HASH, SAFE_ACCESS) { + @Override + public void parseHigherPriority(JetExpressionParsing parsing) { + parsing.parseAtomicExpression(); + } + }, + + POSTFIX(PLUSPLUS, MINUSMINUS), // valueArguments : arrayAccess + + PREFIX(MINUS, PLUS, MINUSMINUS, PLUSPLUS, EXCL) { // attributes + + @Override + public void parseHigherPriority(JetExpressionParsing parsing) { + throw new IllegalStateException("Don't call this method"); + } + }, + + MULTIPLICATIVE(MUL, DIV, PERC) { + @Override + public void parseHigherPriority(JetExpressionParsing parsing) { + parsing.parsePrefixExpression(); + } + }, + + ADDITIVE(PLUS, MINUS), + RANGE(JetTokens.RANGE), + SIMPLE_NAME(IDENTIFIER), + ELVIS(JetTokens.ELVIS), + NAMED_INFIX_OR_TYPE(IN_KEYWORD, NOT_IN, IS_KEYWORD, NOT_IS, AS_KEYWORD, COLON), + COMPARISON(LT, GT, LTEQ, GTEQ), + EQUALITY(EQEQ, EXCLEQ, EQEQEQ, EXCLEQEQEQ), + CONJUNCTION(ANDAND), + DISJUNCTION(OROR), + MATCH(MATCH_KEYWORD), + ARROW(JetTokens.ARROW), + ASSIGNMENT(EQ, PLUSEQ, MINUSEQ, MULTEQ, DIVEQ, PERCEQ) + ; + + static { + Priority[] values = Priority.values(); + for (Priority priority : values) { + int ordinal = priority.ordinal(); + priority.higher = ordinal > 0 ? values[ordinal - 1] : null; + } + } + + private Priority higher; + private final TokenSet operations; + + Priority(IElementType... operations ) { + this.operations = TokenSet.create(operations); + } + + public void parseHigherPriority(JetExpressionParsing parsing) { + assert higher != null; + parsing.parseBinaryExpression(higher); + } + + public final TokenSet getOperations() { + return operations; + } + } + /* * expression * : attributes expression @@ -29,7 +95,6 @@ public class JetExpressionParsing extends AbstractJetParsing { * : "null" * : "this" ("<" type ">")? * : expressionWithPrecedences - * : match * : if * : try * : "typeof" "(" expression ")" @@ -42,6 +107,86 @@ public class JetExpressionParsing extends AbstractJetParsing { * ; */ public void parseExpression() { + parseBinaryExpression(Priority.ASSIGNMENT); + } + + private void parseBinaryExpression(Priority priority) { +// System.out.println(priority.name() + " at " + tt()); + PsiBuilder.Marker expression = mark(); + + priority.parseHigherPriority(this); + + while (!myBuilder.eolInLastWhitespace() && atSet(priority.getOperations())) { + advance(); // operation + priority.parseHigherPriority(this); + expression.done(BINARY_EXPRESSION); + expression = expression.precede(); + } + + expression.drop(); + } + + private void parsePrefixExpression() { +// System.out.println("pre at " + tt()); + if (at(LBRACKET)) { + if (!parseLocalDeclaration()) { + PsiBuilder.Marker attributes = mark(); + myJetParsing.parseAttributeList(); + parsePostfixExpression(); + attributes.done(ANNOTATED_EXPRESSION); + } + } else if (atSet(Priority.PREFIX.getOperations())) { + PsiBuilder.Marker expression = mark(); + advance(); // operation + parsePostfixExpression(); + expression.done(PREFIX_EXPRESSION); + } else { + parsePostfixExpression(); + } + } + + private void parsePostfixExpression() { +// System.out.println("post at " + tt()); + + PsiBuilder.Marker expression = mark(); + parseBinaryExpression(Priority.MEMBER_ACCESS); + if (myBuilder.eolInLastWhitespace()) { + expression.drop(); + } else if (at(LBRACKET)) { + parseArrayAccess(); + expression.done(ARRAY_ACCESS_EXPRESSION); + } else if (atSet(Priority.POSTFIX.getOperations())) { + advance(); // operation + expression.done(POSTFIX_EXPRESSION); + } else if (at(LPAR)) { + parseValueArgumentList(); + expression.done(CALL_EXPRESSION); + } else { + expression.drop(); + } + } + + /* + * atomicExpression + * : tupleLiteral // or parenthesized expression + * : "this" ("<" type ">")? + * : "typeof" "(" expression ")" + * : "new" constructorInvocation // TODO: Do we need "new"?, see factory methods + * : objectLiteral + * : jump + * : if + * : try + * : loop + * : literalConstant + * : functionLiteral + * : declaration + * : SimpleName + * ; + */ + private void parseAtomicExpression() { +// System.out.println("atom at " + tt()); + + if (at(LPAR)) { parseParenthesizedExpressionOrTuple(); } @@ -112,33 +257,61 @@ public class JetExpressionParsing extends AbstractJetParsing { VAR_KEYWORD, TYPE_KEYWORD, DECOMPOSER_KEYWORD)) { parseLocalDeclaration(); } - else if (at(LBRACKET)) { - if (!parseLocalDeclaration()) { - PsiBuilder.Marker attributes = mark(); - myJetParsing.parseAttributeList(); - parseExpression(); - attributes.done(ANNOTATED_EXPRESSION); - } - } else if (at(IDENTIFIER)) { if (JetParsing.MODIFIER_KEYWORD_MAP.containsKey(myBuilder.getTokenText())) { if (!parseLocalDeclaration()) { - expect(IDENTIFIER, "[Internal error: should never occur]"); - // TODO + parseSimpleName(); } } else { - expect(IDENTIFIER, "[Internal error: should never occur]"); - // TODO + parseSimpleName(); } } else if (at(LBRACE)) { parseFunctionLiteral(); } else { + // TODO errorAndAdvance("Expecting an expression"); } + } - // TODO: Binary operations + + /* + * arrayAccess + * : "[" expression{","} "]" + * ; + */ + private void parseArrayAccess() { + assert at(LBRACKET); + + PsiBuilder.Marker indices = mark(); + + advance(); // LBRACKET + + while (true) { + if (at(COMMA)) errorAndAdvance("Expecting an index expression"); + if (at(RBRACKET)) { + error("Expecting an index expression"); + break; + } + parseExpression(); + if (!at(COMMA)) break; + advance(); // COMMA + } + + expect(RBRACKET, "Expecting ']'"); + + indices.done(INDICES); + } + + /* + * SimpleName + */ + private void parseSimpleName() { + assert at(IDENTIFIER); +// PsiBuilder.Marker simpleName = mark(); + advance(); // IDENTIFIER +// simpleName.done(SIMPLE_NAME); } /* @@ -202,36 +375,7 @@ public class JetExpressionParsing extends AbstractJetParsing { } if (at(LPAR)) { - PsiBuilder.Marker list = mark(); - expect(LPAR, "Expecting a parameter list in parentheses (...)", TokenSet.create(DOUBLE_ARROW, COLON)); - - if (!at(RPAR)) { - while (true) { - if (at(COMMA)) errorAndAdvance("Expecting a parameter declaration"); - - PsiBuilder.Marker parameter = mark(); - int parameterNamePos = matchTokenStreamPredicate(new LastBefore(new At(IDENTIFIER), new AtSet(COMMA, RPAR, COLON, DOUBLE_ARROW))); - createTruncatedBuilder(parameterNamePos).parseModifierList(); - - expect(IDENTIFIER, "Expecting parameter declaration"); - - if (at(COLON)) { - advance(); // COLON - myJetParsing.parseTypeRef(); - } - parameter.done(VALUE_PARAMETER); - if (!at(COMMA)) break; - advance(); // COMMA - - if (at(RPAR)) { - error("Expecting a parameter declaration"); - break; - } - } - } - - expect(RPAR, "Expecting ')", TokenSet.create(DOUBLE_ARROW, COLON)); - list.done(VALUE_PARAMETER_LIST); + parseFunctionLiteralParameterList(); if (at(COLON)) { advance(); // COLON @@ -273,6 +417,42 @@ public class JetExpressionParsing extends AbstractJetParsing { literal.done(FUNCTION_LITERAL); } + /* + * "(" (modifiers SimpleName (":" type)?){","} ")" + */ + private void parseFunctionLiteralParameterList() { + PsiBuilder.Marker list = mark(); + expect(LPAR, "Expecting a parameter list in parentheses (...)", TokenSet.create(DOUBLE_ARROW, COLON)); + + if (!at(RPAR)) { + while (true) { + if (at(COMMA)) errorAndAdvance("Expecting a parameter declaration"); + + PsiBuilder.Marker parameter = mark(); + int parameterNamePos = matchTokenStreamPredicate(new LastBefore(new At(IDENTIFIER), new AtSet(COMMA, RPAR, COLON, DOUBLE_ARROW))); + createTruncatedBuilder(parameterNamePos).parseModifierList(); + + expect(IDENTIFIER, "Expecting parameter declaration"); + + if (at(COLON)) { + advance(); // COLON + myJetParsing.parseTypeRef(); + } + parameter.done(VALUE_PARAMETER); + if (!at(COMMA)) break; + advance(); // COMMA + + if (at(RPAR)) { + error("Expecting a parameter declaration"); + break; + } + } + } + + expect(RPAR, "Expecting ')", TokenSet.create(DOUBLE_ARROW, COLON)); + list.done(VALUE_PARAMETER_LIST); + } + /* * expressions * : expression{SEMI} SEMI? diff --git a/idea/src/org/jetbrains/jet/lexer/Jet.flex b/idea/src/org/jetbrains/jet/lexer/Jet.flex index 608b1e821a1..ccd3b5993f3 100644 --- a/idea/src/org/jetbrains/jet/lexer/Jet.flex +++ b/idea/src/org/jetbrains/jet/lexer/Jet.flex @@ -135,6 +135,7 @@ RAW_STRING_LITERAL = {THREE_QUO} {QUO_STRING_CHAR}* {THREE_QUO}? "-=" { return JetTokens.MINUSEQ ; } "->" { return JetTokens.ARROW ; } "=>" { return JetTokens.DOUBLE_ARROW; } + ".." { return JetTokens.RANGE ; } "[" { return JetTokens.LBRACKET ; } "]" { return JetTokens.RBRACKET ; } "{" { return JetTokens.LBRACE ; } @@ -153,9 +154,9 @@ RAW_STRING_LITERAL = {THREE_QUO} {QUO_STRING_CHAR}* {THREE_QUO}? "?" { return JetTokens.QUEST ; } ":" { return JetTokens.COLON ; } ";" { return JetTokens.SEMICOLON ; } - ".." { return JetTokens.RANGE ; } "=" { return JetTokens.EQ ; } "," { return JetTokens.COMMA ; } + "#" { return JetTokens.HASH ; } . { return TokenType.BAD_CHARACTER; } diff --git a/idea/src/org/jetbrains/jet/lexer/JetTokens.java b/idea/src/org/jetbrains/jet/lexer/JetTokens.java index 0e30f5bad1c..c088c1c807b 100644 --- a/idea/src/org/jetbrains/jet/lexer/JetTokens.java +++ b/idea/src/org/jetbrains/jet/lexer/JetTokens.java @@ -99,6 +99,7 @@ public interface JetTokens { JetToken MINUSEQ = new JetToken("MINUSEQ"); JetToken NOT_IN = new JetToken("NOT_IN"); JetToken NOT_IS = new JetToken("NOT_IS"); + JetToken HASH = new JetToken("HASH"); JetToken COMMA = new JetToken("COMMA");