diff options
author | Stephan Unverwerth <s.unverwerth@gmx.de> | 2020-03-12 23:02:41 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-03-14 00:11:24 +0100 |
commit | 15d5b2d29e5b375a7b0601e7c456ac49868c606d (patch) | |
tree | 7c0d4da55a7565f5d3ca54eedd441b26f800f80a /Libraries/LibJS/Parser.cpp | |
parent | f347dd5c5e261fc8c77917508e47680dbd166be5 (diff) | |
download | serenity-15d5b2d29e5b375a7b0601e7c456ac49868c606d.zip |
LibJS: Add operator precedence parsing
Obey precedence and associativity rules when parsing expressions
with chained operators.
Diffstat (limited to 'Libraries/LibJS/Parser.cpp')
-rw-r--r-- | Libraries/LibJS/Parser.cpp | 199 |
1 files changed, 164 insertions, 35 deletions
diff --git a/Libraries/LibJS/Parser.cpp b/Libraries/LibJS/Parser.cpp index ebbae3c475..64a9763d38 100644 --- a/Libraries/LibJS/Parser.cpp +++ b/Libraries/LibJS/Parser.cpp @@ -25,14 +25,142 @@ */ #include "Parser.h" +#include <AK/HashMap.h> #include <AK/StdLibExtras.h> #include <stdio.h> namespace JS { + +static HashMap<TokenType, int> g_operator_precedence; + Parser::Parser(Lexer lexer) : m_lexer(move(lexer)) , m_current_token(m_lexer.next()) { + if (g_operator_precedence.is_empty()) { + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence + g_operator_precedence.set(TokenType::Period, 20); + g_operator_precedence.set(TokenType::BracketOpen, 20); + g_operator_precedence.set(TokenType::ParenOpen, 20); + g_operator_precedence.set(TokenType::QuestionMarkPeriod, 20); + + g_operator_precedence.set(TokenType::New, 19); + + g_operator_precedence.set(TokenType::PlusPlus, 18); + g_operator_precedence.set(TokenType::MinusMinus, 18); + + g_operator_precedence.set(TokenType::ExclamationMark, 17); + g_operator_precedence.set(TokenType::Tilde, 17); + g_operator_precedence.set(TokenType::Typeof, 17); + g_operator_precedence.set(TokenType::Void, 17); + g_operator_precedence.set(TokenType::Delete, 17); + g_operator_precedence.set(TokenType::Await, 17); + + g_operator_precedence.set(TokenType::DoubleAsterisk, 16); + + g_operator_precedence.set(TokenType::Asterisk, 15); + g_operator_precedence.set(TokenType::Slash, 15); + g_operator_precedence.set(TokenType::Percent, 15); + + g_operator_precedence.set(TokenType::Plus, 14); + g_operator_precedence.set(TokenType::Minus, 14); + + g_operator_precedence.set(TokenType::ShiftLeft, 13); + g_operator_precedence.set(TokenType::ShiftRight, 13); + g_operator_precedence.set(TokenType::UnsignedShiftRight, 13); + + g_operator_precedence.set(TokenType::LessThan, 12); + g_operator_precedence.set(TokenType::LessThanEquals, 12); + g_operator_precedence.set(TokenType::GreaterThan, 12); + g_operator_precedence.set(TokenType::GreaterThanEquals, 12); + g_operator_precedence.set(TokenType::In, 12); + g_operator_precedence.set(TokenType::Instanceof, 12); + + g_operator_precedence.set(TokenType::EqualsEquals, 11); + g_operator_precedence.set(TokenType::ExclamationMarkEquals, 11); + g_operator_precedence.set(TokenType::EqualsEqualsEquals, 11); + g_operator_precedence.set(TokenType::ExclamationMarkEqualsEquals, 11); + + g_operator_precedence.set(TokenType::Ampersand, 10); + + g_operator_precedence.set(TokenType::Caret, 9); + + g_operator_precedence.set(TokenType::Pipe, 8); + + g_operator_precedence.set(TokenType::DoubleQuestionMark, 7); + + g_operator_precedence.set(TokenType::DoubleAmpersand, 6); + + g_operator_precedence.set(TokenType::DoublePipe, 5); + + g_operator_precedence.set(TokenType::QuestionMark, 4); + + g_operator_precedence.set(TokenType::Equals, 3); + g_operator_precedence.set(TokenType::PlusEquals, 3); + g_operator_precedence.set(TokenType::MinusEquals, 3); + g_operator_precedence.set(TokenType::AsteriskAsteriskEquals, 3); + g_operator_precedence.set(TokenType::AsteriskEquals, 3); + g_operator_precedence.set(TokenType::SlashEquals, 3); + g_operator_precedence.set(TokenType::PercentEquals, 3); + g_operator_precedence.set(TokenType::ShiftLeftEquals, 3); + g_operator_precedence.set(TokenType::ShiftRightEquals, 3); + g_operator_precedence.set(TokenType::UnsignedShiftRightEquals, 3); + g_operator_precedence.set(TokenType::PipeEquals, 3); + + g_operator_precedence.set(TokenType::Yield, 2); + + g_operator_precedence.set(TokenType::Comma, 1); + } +} + +int Parser::operator_precedence(TokenType type) const +{ + auto it = g_operator_precedence.find(type); + if (it == g_operator_precedence.end()) { + fprintf(stderr, "No precedence for operator %s\n", Token::name(type)); + ASSERT_NOT_REACHED(); + return -1; + } + + return it->value; +} + +Associativity Parser::operator_associativity(TokenType type) const +{ + switch (type) { + case TokenType::Period: + case TokenType::BracketOpen: + case TokenType::ParenOpen: + case TokenType::QuestionMarkPeriod: + case TokenType::Asterisk: + case TokenType::Slash: + case TokenType::Percent: + case TokenType::Plus: + case TokenType::Minus: + case TokenType::ShiftLeft: + case TokenType::ShiftRight: + case TokenType::UnsignedShiftRight: + case TokenType::LessThan: + case TokenType::LessThanEquals: + case TokenType::GreaterThan: + case TokenType::GreaterThanEquals: + case TokenType::In: + case TokenType::Instanceof: + case TokenType::EqualsEquals: + case TokenType::ExclamationMarkEquals: + case TokenType::EqualsEqualsEquals: + case TokenType::ExclamationMarkEqualsEquals: + case TokenType::Ampersand: + case TokenType::Caret: + case TokenType::Pipe: + case TokenType::DoubleQuestionMark: + case TokenType::DoubleAmpersand: + case TokenType::DoublePipe: + case TokenType::Comma: + return Associativity::Left; + default: + return Associativity::Right; + } } NonnullOwnPtr<Program> Parser::parse_program() @@ -54,7 +182,7 @@ NonnullOwnPtr<Program> Parser::parse_program() NonnullOwnPtr<Statement> Parser::parse_statement() { if (match_expression()) { - return make<JS::ExpressionStatement>(parse_expression()); + return make<JS::ExpressionStatement>(parse_expression(0)); } switch (m_current_token.type()) { @@ -83,7 +211,7 @@ NonnullOwnPtr<Expression> Parser::parse_primary_expression() switch (m_current_token.type()) { case TokenType::ParenOpen: { consume(TokenType::ParenOpen); - auto expression = parse_expression(); + auto expression = parse_expression(0); consume(TokenType::ParenClose); return expression; } @@ -113,68 +241,75 @@ NonnullOwnPtr<ObjectExpression> Parser::parse_object_expression() return make<ObjectExpression>(); } -NonnullOwnPtr<Expression> Parser::parse_expression() +NonnullOwnPtr<Expression> Parser::parse_expression(int min_precedence, Associativity associativity) { auto expression = parse_primary_expression(); while (match_secondary_expression()) { - expression = parse_secondary_expression(move(expression)); + int new_precedence = operator_precedence(m_current_token.type()); + if (new_precedence < min_precedence) + break; + if (new_precedence == min_precedence && associativity == Associativity::Left) + break; + + Associativity new_associativity = operator_associativity(m_current_token.type()); + expression = parse_secondary_expression(move(expression), new_precedence, new_associativity); } return expression; } -NonnullOwnPtr<Expression> Parser::parse_secondary_expression(NonnullOwnPtr<Expression> lhs) +NonnullOwnPtr<Expression> Parser::parse_secondary_expression(NonnullOwnPtr<Expression> lhs, int min_precedence, Associativity associativity) { switch (m_current_token.type()) { case TokenType::Plus: consume(); - return make<BinaryExpression>(BinaryOp::Plus, move(lhs), parse_expression()); + return make<BinaryExpression>(BinaryOp::Plus, move(lhs), parse_expression(min_precedence, associativity)); case TokenType::PlusEquals: consume(); - return make<AssignmentExpression>(AssignmentOp::AdditionAssignment, move(lhs), parse_expression()); + return make<AssignmentExpression>(AssignmentOp::AdditionAssignment, move(lhs), parse_expression(min_precedence, associativity)); case TokenType::Minus: consume(); - return make<BinaryExpression>(BinaryOp::Minus, move(lhs), parse_expression()); + return make<BinaryExpression>(BinaryOp::Minus, move(lhs), parse_expression(min_precedence, associativity)); case TokenType::MinusEquals: consume(); - return make<AssignmentExpression>(AssignmentOp::SubtractionAssignment, move(lhs), parse_expression()); + return make<AssignmentExpression>(AssignmentOp::SubtractionAssignment, move(lhs), parse_expression(min_precedence, associativity)); case TokenType::Asterisk: consume(); - return make<BinaryExpression>(BinaryOp::Asterisk, move(lhs), parse_expression()); + return make<BinaryExpression>(BinaryOp::Asterisk, move(lhs), parse_expression(min_precedence, associativity)); case TokenType::AsteriskEquals: consume(); - return make<AssignmentExpression>(AssignmentOp::MultiplicationAssignment, move(lhs), parse_expression()); + return make<AssignmentExpression>(AssignmentOp::MultiplicationAssignment, move(lhs), parse_expression(min_precedence, associativity)); case TokenType::Slash: consume(); - return make<BinaryExpression>(BinaryOp::Slash, move(lhs), parse_expression()); + return make<BinaryExpression>(BinaryOp::Slash, move(lhs), parse_expression(min_precedence, associativity)); case TokenType::SlashEquals: consume(); - return make<AssignmentExpression>(AssignmentOp::DivisionAssignment, move(lhs), parse_expression()); + return make<AssignmentExpression>(AssignmentOp::DivisionAssignment, move(lhs), parse_expression(min_precedence, associativity)); case TokenType::GreaterThan: consume(); - return make<BinaryExpression>(BinaryOp::GreaterThan, move(lhs), parse_expression()); + return make<BinaryExpression>(BinaryOp::GreaterThan, move(lhs), parse_expression(min_precedence, associativity)); case TokenType::GreaterThanEquals: consume(); - return make<BinaryExpression>(BinaryOp::GreaterThanEquals, move(lhs), parse_expression()); + return make<BinaryExpression>(BinaryOp::GreaterThanEquals, move(lhs), parse_expression(min_precedence, associativity)); case TokenType::LessThan: consume(); - return make<BinaryExpression>(BinaryOp::LessThan, move(lhs), parse_expression()); + return make<BinaryExpression>(BinaryOp::LessThan, move(lhs), parse_expression(min_precedence, associativity)); case TokenType::LessThanEquals: consume(); - return make<BinaryExpression>(BinaryOp::LessThanEquals, move(lhs), parse_expression()); + return make<BinaryExpression>(BinaryOp::LessThanEquals, move(lhs), parse_expression(min_precedence, associativity)); case TokenType::EqualsEqualsEquals: consume(); - return make<BinaryExpression>(BinaryOp::TypedEquals, move(lhs), parse_expression()); + return make<BinaryExpression>(BinaryOp::TypedEquals, move(lhs), parse_expression(min_precedence, associativity)); case TokenType::ExclamationMarkEqualsEquals: consume(); - return make<BinaryExpression>(BinaryOp::TypedInequals, move(lhs), parse_expression()); + return make<BinaryExpression>(BinaryOp::TypedInequals, move(lhs), parse_expression(min_precedence, associativity)); case TokenType::ParenOpen: return parse_call_expression(move(lhs)); case TokenType::Equals: consume(); - return make<AssignmentExpression>(AssignmentOp::Assignment, move(lhs), parse_expression()); + return make<AssignmentExpression>(AssignmentOp::Assignment, move(lhs), parse_expression(min_precedence, associativity)); case TokenType::Period: consume(); - return make<MemberExpression>(move(lhs), parse_expression()); + return make<MemberExpression>(move(lhs), parse_expression(min_precedence, associativity)); case TokenType::PlusPlus: consume(); return make<UpdateExpression>(UpdateOp::Increment, move(lhs)); @@ -196,7 +331,7 @@ NonnullOwnPtr<CallExpression> Parser::parse_call_expression(NonnullOwnPtr<Expres NonnullOwnPtrVector<Expression> arguments; while (match_expression()) { - arguments.append(parse_expression()); + arguments.append(parse_expression(0)); if (!match(TokenType::Comma)) break; consume(); @@ -204,20 +339,14 @@ NonnullOwnPtr<CallExpression> Parser::parse_call_expression(NonnullOwnPtr<Expres consume(TokenType::ParenClose); - // FIXME: Allow lhs expression instead of just a string - if (lhs->is_identifier()) { - return make<CallExpression>(static_cast<Identifier*>(lhs.ptr())->string(), move(arguments)); - } - - m_has_errors = true; - return make<CallExpression>("***ERROR***"); + return make<CallExpression>(move(lhs), move(arguments)); } NonnullOwnPtr<ReturnStatement> Parser::parse_return_statement() { consume(TokenType::Return); if (match_expression()) { - return make<ReturnStatement>(parse_expression()); + return make<ReturnStatement>(parse_expression(0)); } return make<ReturnStatement>(nullptr); } @@ -283,7 +412,7 @@ NonnullOwnPtr<VariableDeclaration> Parser::parse_variable_declaration() OwnPtr<Expression> initializer; if (match(TokenType::Equals)) { consume(); - initializer = parse_expression(); + initializer = parse_expression(0); } return make<VariableDeclaration>(make<Identifier>(name), move(initializer), declaration_type); } @@ -313,7 +442,7 @@ NonnullOwnPtr<ForStatement> Parser::parse_for_statement() case TokenType::Semicolon: break; default: - test = parse_expression(); + test = parse_expression(0); break; } @@ -324,7 +453,7 @@ NonnullOwnPtr<ForStatement> Parser::parse_for_statement() case TokenType::Semicolon: break; default: - update = parse_expression(); + update = parse_expression(0); break; } @@ -405,9 +534,9 @@ bool Parser::done() const Token Parser::consume() { - auto oldToken = m_current_token; + auto old_token = m_current_token; m_current_token = m_lexer.next(); - return oldToken; + return old_token; } Token Parser::consume(TokenType type) |