diff --git a/completion/context.cpp b/completion/context.cpp --- a/completion/context.cpp +++ b/completion/context.cpp @@ -528,6 +528,7 @@ case Parser::Token_MOD_ASSIGN: case Parser::Token_MUL: case Parser::Token_MUL_ASSIGN: + case Parser::Token_NULL_COALESCE: case Parser::Token_OBJECT_CAST: case Parser::Token_OPEN_TAG_WITH_ECHO: case Parser::Token_OR_ASSIGN: @@ -540,6 +541,7 @@ case Parser::Token_SEMICOLON: case Parser::Token_SL: case Parser::Token_SL_ASSIGN: + case Parser::Token_SPACESHIP: case Parser::Token_SR: case Parser::Token_SR_ASSIGN: case Parser::Token_START_HEREDOC: diff --git a/parser/php.g b/parser/php.g --- a/parser/php.g +++ b/parser/php.g @@ -269,6 +269,7 @@ BIT_AND ("&"), BIT_OR("|"), BIT_XOR ("^"), SL ("<<"), SR (">>"), MUL("*"), DIV("/"), MOD ("%"), TILDE ("~"), DOLLAR ("$"), EXP ("**"), ELLIPSIS ("..."), + NULL_COALESCE ("??"), SPACESHIP ("<=>"), LOGICAL_OR ("logical or"), LOGICAL_AND ("logical and"), LOGICAL_XOR ("logical xor") ;; -- literals and identifiers: @@ -336,12 +337,13 @@ --right print --right = += -= *= /= .= %= &= |= ^= <<= >>= assignment --left ? : ternary +--right ?? comparison --left || logical --left && logical --left | bitwise --left ^ bitwise --left & bitwise and references ---non-associative == != === !== comparison +--non-associative == != === !== <=> comparison --non-associative < <= > >= comparison --left << >> bitwise --left + - . arithmetic and string @@ -423,13 +425,16 @@ :] -> assignmentExpressionCheckIfVariable ;; -expression=booleanOrExpression +expression=nullCoalesceExpression ( QUESTION (ifExpression=expr|0) COLON elseExpression=conditionalExpression | 0 ) -> conditionalExpression ;; + #expression=booleanOrExpression @ NULL_COALESCE +-> nullCoalesceExpression ;; + #expression=booleanAndExpression @ BOOLEAN_OR -> booleanOrExpression ;; @@ -449,7 +454,7 @@ (#additionalExpression=equalityExpressionRest)* -> equalityExpression ;; - ( IS_EQUAL | IS_NOT_EQUAL | IS_IDENTICAL | IS_NOT_IDENTICAL ) + ( IS_EQUAL | IS_NOT_EQUAL | IS_IDENTICAL | IS_NOT_IDENTICAL | SPACESHIP ) expression=relationalExpression -> equalityExpressionRest ;; diff --git a/parser/phplexer.cpp b/parser/phplexer.cpp --- a/parser/phplexer.cpp +++ b/parser/phplexer.cpp @@ -345,8 +345,13 @@ } } } else if ((it + 1)->unicode() == '=') { - m_curpos++; - token = Parser::Token_IS_SMALLER_OR_EQUAL; + if ((it + 2)->unicode() == '>') { + m_curpos += 2; + token = Parser::Token_SPACESHIP; + } else { + m_curpos++; + token = Parser::Token_IS_SMALLER_OR_EQUAL; + } } else if ((it + 1)->unicode() == '>') { m_curpos++; token = Parser::Token_IS_NOT_EQUAL; @@ -383,6 +388,9 @@ token = Parser::Token_CLOSE_TAG; m_curpos++; while (state() != HtmlState) popState(); + } else if ((it + 1)->unicode() == '?') { + token = Parser::Token_NULL_COALESCE; + m_curpos++; } else { token = Parser::Token_QUESTION; } diff --git a/parser/test/lexertest.h b/parser/test/lexertest.h --- a/parser/test/lexertest.h +++ b/parser/test/lexertest.h @@ -70,6 +70,8 @@ void testExponentiation(); void testExceptionFinally(); void testEllipsis(); + void testSpaceship(); + void testNullCoalesce(); protected: TokenStream* tokenize(const QString& unit, bool debug = false, int initialState = Lexer::HtmlState); diff --git a/parser/test/lexertest.cpp b/parser/test/lexertest.cpp --- a/parser/test/lexertest.cpp +++ b/parser/test/lexertest.cpp @@ -547,6 +547,42 @@ COMPARE_TOKEN(ts, 10, Parser::Token_RBRACE, 1, 24, 1, 24); } +void LexerTest::testSpaceship() +{ + QScopedPointer ts(tokenize(QStringLiteral(" 'b';"), true)); + QCOMPARE((int)ts->size(), 11); + + COMPARE_TOKEN(ts, 0, Parser::Token_OPEN_TAG, 0, 0, 0, 5); + COMPARE_TOKEN(ts, 1, Parser::Token_VARIABLE, 1, 0, 1, 1); + COMPARE_TOKEN(ts, 2, Parser::Token_WHITESPACE, 1, 2, 1, 2); + COMPARE_TOKEN(ts, 3, Parser::Token_ASSIGN, 1, 3, 1, 3); + COMPARE_TOKEN(ts, 4, Parser::Token_WHITESPACE, 1, 4, 1, 4); + COMPARE_TOKEN(ts, 5, Parser::Token_CONSTANT_ENCAPSED_STRING, 1, 5, 1, 8); + COMPARE_TOKEN(ts, 6, Parser::Token_WHITESPACE, 1, 9, 1, 9); + COMPARE_TOKEN(ts, 7, Parser::Token_SPACESHIP, 1, 10, 1, 12); + COMPARE_TOKEN(ts, 8, Parser::Token_WHITESPACE, 1, 13, 1, 13); + COMPARE_TOKEN(ts, 9, Parser::Token_CONSTANT_ENCAPSED_STRING, 1, 14, 1, 16); + COMPARE_TOKEN(ts, 10, Parser::Token_SEMICOLON, 1, 17, 1, 17); +} + +void LexerTest::testNullCoalesce() +{ + QScopedPointer ts(tokenize(QStringLiteral("size(), 11); + + COMPARE_TOKEN(ts, 0, Parser::Token_OPEN_TAG, 0, 0, 0, 5); + COMPARE_TOKEN(ts, 1, Parser::Token_VARIABLE, 1, 0, 1, 1); + COMPARE_TOKEN(ts, 2, Parser::Token_WHITESPACE, 1, 2, 1, 2); + COMPARE_TOKEN(ts, 3, Parser::Token_ASSIGN, 1, 3, 1, 3); + COMPARE_TOKEN(ts, 4, Parser::Token_WHITESPACE, 1, 4, 1, 4); + COMPARE_TOKEN(ts, 5, Parser::Token_STRING, 1, 5, 1, 8); + COMPARE_TOKEN(ts, 6, Parser::Token_WHITESPACE, 1, 9, 1, 9); + COMPARE_TOKEN(ts, 7, Parser::Token_NULL_COALESCE, 1, 10, 1, 11); + COMPARE_TOKEN(ts, 8, Parser::Token_WHITESPACE, 1, 12, 1, 12); + COMPARE_TOKEN(ts, 9, Parser::Token_STRING, 1, 13, 1, 16); + COMPARE_TOKEN(ts, 10, Parser::Token_SEMICOLON, 1, 17, 1, 17); +} + TokenStream* LexerTest::tokenize(const QString& unit, bool debug, int initialState) { TokenStream* tokenStream = new TokenStream;