1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-28 07:15:07 +00:00

LibJS: Add operator precedence parsing

Obey precedence and associativity rules when parsing expressions
with chained operators.
This commit is contained in:
Stephan Unverwerth 2020-03-12 23:02:41 +01:00 committed by Andreas Kling
parent f347dd5c5e
commit 15d5b2d29e
8 changed files with 281 additions and 53 deletions

View file

@ -0,0 +1,2 @@
var foo = 1 + 2 * 3 - 4 / 5;
foo.bar();

View file

@ -54,7 +54,7 @@ Value ExpressionStatement::execute(Interpreter& interpreter) const
Value CallExpression::execute(Interpreter& interpreter) const Value CallExpression::execute(Interpreter& interpreter) const
{ {
auto callee = interpreter.get_variable(name()); auto callee = m_callee->execute(interpreter);
ASSERT(callee.is_object()); ASSERT(callee.is_object());
ASSERT(callee.as_object()->is_function()); ASSERT(callee.as_object()->is_function());
auto* function = static_cast<Function*>(callee.as_object()); auto* function = static_cast<Function*>(callee.as_object());
@ -304,9 +304,8 @@ void UnaryExpression::dump(int indent) const
void CallExpression::dump(int indent) const void CallExpression::dump(int indent) const
{ {
print_indent(indent); ASTNode::dump(indent);
printf("%s '%s'\n", class_name(), name().characters()); m_callee->dump(indent + 1);
for (auto& argument : m_arguments) for (auto& argument : m_arguments)
argument.dump(indent + 1); argument.dump(indent + 1);
} }

View file

@ -398,8 +398,8 @@ private:
class CallExpression : public Expression { class CallExpression : public Expression {
public: public:
explicit CallExpression(String name, NonnullOwnPtrVector<Expression> arguments = {}) explicit CallExpression(NonnullOwnPtr<Expression> callee, NonnullOwnPtrVector<Expression> arguments = {})
: m_name(move(name)) : m_callee(move(callee))
, m_arguments(move(arguments)) , m_arguments(move(arguments))
{ {
} }
@ -407,12 +407,10 @@ public:
virtual Value execute(Interpreter&) const override; virtual Value execute(Interpreter&) const override;
virtual void dump(int indent) const override; virtual void dump(int indent) const override;
const String& name() const { return m_name; }
private: private:
virtual const char* class_name() const override { return "CallExpression"; } virtual const char* class_name() const override { return "CallExpression"; }
String m_name; NonnullOwnPtr<Expression> m_callee;
const NonnullOwnPtrVector<Expression> m_arguments; const NonnullOwnPtrVector<Expression> m_arguments;
}; };

View file

@ -42,31 +42,41 @@ Lexer::Lexer(StringView source)
, m_current_token(TokenType::Eof, StringView(nullptr), StringView(nullptr)) , m_current_token(TokenType::Eof, StringView(nullptr), StringView(nullptr))
{ {
if (s_keywords.is_empty()) { if (s_keywords.is_empty()) {
s_keywords.set("true", TokenType::BoolLiteral); s_keywords.set("await", TokenType::Await);
s_keywords.set("false", TokenType::BoolLiteral);
s_keywords.set("catch", TokenType::Catch); s_keywords.set("catch", TokenType::Catch);
s_keywords.set("class", TokenType::Class); s_keywords.set("class", TokenType::Class);
s_keywords.set("const", TokenType::Const); s_keywords.set("const", TokenType::Const);
s_keywords.set("delete", TokenType::Delete); s_keywords.set("delete", TokenType::Delete);
s_keywords.set("do", TokenType::Do); s_keywords.set("do", TokenType::Do);
s_keywords.set("else", TokenType::Else); s_keywords.set("else", TokenType::Else);
s_keywords.set("false", TokenType::BoolLiteral);
s_keywords.set("finally", TokenType::Finally); s_keywords.set("finally", TokenType::Finally);
s_keywords.set("for", TokenType::For); s_keywords.set("for", TokenType::For);
s_keywords.set("function", TokenType::Function); s_keywords.set("function", TokenType::Function);
s_keywords.set("if", TokenType::If); s_keywords.set("if", TokenType::If);
s_keywords.set("in", TokenType::In);
s_keywords.set("instanceof", TokenType::Instanceof);
s_keywords.set("interface", TokenType::Interface); s_keywords.set("interface", TokenType::Interface);
s_keywords.set("let", TokenType::Let); s_keywords.set("let", TokenType::Let);
s_keywords.set("new", TokenType::New); s_keywords.set("new", TokenType::New);
s_keywords.set("null", TokenType::NullLiteral); s_keywords.set("null", TokenType::NullLiteral);
s_keywords.set("return", TokenType::Return); s_keywords.set("return", TokenType::Return);
s_keywords.set("true", TokenType::BoolLiteral);
s_keywords.set("try", TokenType::Try); s_keywords.set("try", TokenType::Try);
s_keywords.set("typeof", TokenType::Typeof);
s_keywords.set("var", TokenType::Var); s_keywords.set("var", TokenType::Var);
s_keywords.set("void", TokenType::Void);
s_keywords.set("while", TokenType::While); s_keywords.set("while", TokenType::While);
s_keywords.set("yield", TokenType::Yield);
} }
if (s_three_char_tokens.is_empty()) { if (s_three_char_tokens.is_empty()) {
s_three_char_tokens.set("===", TokenType::EqualsEqualsEquals); s_three_char_tokens.set("===", TokenType::EqualsEqualsEquals);
s_three_char_tokens.set("!==", TokenType::ExclamationMarkEqualsEquals); s_three_char_tokens.set("!==", TokenType::ExclamationMarkEqualsEquals);
s_three_char_tokens.set("**=", TokenType::AsteriskAsteriskEquals);
s_three_char_tokens.set("<<=", TokenType::ShiftLeftEquals);
s_three_char_tokens.set(">>=", TokenType::ShiftRightEquals);
s_three_char_tokens.set(">>>", TokenType::UnsignedShiftRight);
} }
if (s_two_char_tokens.is_empty()) { if (s_two_char_tokens.is_empty()) {
@ -79,12 +89,15 @@ Lexer::Lexer(StringView source)
s_two_char_tokens.set("|=", TokenType::PipeEquals); s_two_char_tokens.set("|=", TokenType::PipeEquals);
s_two_char_tokens.set("&&", TokenType::DoubleAmpersand); s_two_char_tokens.set("&&", TokenType::DoubleAmpersand);
s_two_char_tokens.set("||", TokenType::DoublePipe); s_two_char_tokens.set("||", TokenType::DoublePipe);
s_two_char_tokens.set("??", TokenType::DoubleQuestionMark);
s_two_char_tokens.set("**", TokenType::DoubleAsterisk);
s_two_char_tokens.set("==", TokenType::EqualsEquals); s_two_char_tokens.set("==", TokenType::EqualsEquals);
s_two_char_tokens.set("!=", TokenType::ExclamationMarkEquals); s_two_char_tokens.set("!=", TokenType::ExclamationMarkEquals);
s_two_char_tokens.set("--", TokenType::MinusMinus); s_two_char_tokens.set("--", TokenType::MinusMinus);
s_two_char_tokens.set("++", TokenType::PlusPlus); s_two_char_tokens.set("++", TokenType::PlusPlus);
s_two_char_tokens.set("<<", TokenType::ShiftLeft); s_two_char_tokens.set("<<", TokenType::ShiftLeft);
s_two_char_tokens.set(">>", TokenType::ShiftRight); s_two_char_tokens.set(">>", TokenType::ShiftRight);
s_two_char_tokens.set("?.", TokenType::QuestionMarkPeriod);
} }
if (s_single_char_tokens.is_empty()) { if (s_single_char_tokens.is_empty()) {
@ -92,6 +105,7 @@ Lexer::Lexer(StringView source)
s_single_char_tokens.set('*', TokenType::Asterisk); s_single_char_tokens.set('*', TokenType::Asterisk);
s_single_char_tokens.set('[', TokenType::BracketOpen); s_single_char_tokens.set('[', TokenType::BracketOpen);
s_single_char_tokens.set(']', TokenType::BracketClose); s_single_char_tokens.set(']', TokenType::BracketClose);
s_single_char_tokens.set('^', TokenType::Caret);
s_single_char_tokens.set(',', TokenType::Comma); s_single_char_tokens.set(',', TokenType::Comma);
s_single_char_tokens.set('{', TokenType::CurlyOpen); s_single_char_tokens.set('{', TokenType::CurlyOpen);
s_single_char_tokens.set('}', TokenType::CurlyClose); s_single_char_tokens.set('}', TokenType::CurlyClose);
@ -107,6 +121,7 @@ Lexer::Lexer(StringView source)
s_single_char_tokens.set('?', TokenType::QuestionMark); s_single_char_tokens.set('?', TokenType::QuestionMark);
s_single_char_tokens.set(';', TokenType::Semicolon); s_single_char_tokens.set(';', TokenType::Semicolon);
s_single_char_tokens.set('/', TokenType::Slash); s_single_char_tokens.set('/', TokenType::Slash);
s_single_char_tokens.set('~', TokenType::Tilde);
s_single_char_tokens.set('<', TokenType::LessThan); s_single_char_tokens.set('<', TokenType::LessThan);
s_single_char_tokens.set('>', TokenType::GreaterThan); s_single_char_tokens.set('>', TokenType::GreaterThan);
} }
@ -213,8 +228,25 @@ Token Lexer::next()
} else if (m_current_char == EOF) { } else if (m_current_char == EOF) {
token_type = TokenType::Eof; token_type = TokenType::Eof;
} else { } else {
// There is only one four-char operator: >>>=
bool found_four_char_token = false;
if (m_position + 2 < m_source.length()) {
if (m_current_char == '>'
&& m_source[m_position] == '>'
&& m_source[m_position + 1] == '>'
&& m_source[m_position + 2] == '=') {
found_four_char_token = true;
consume();
consume();
consume();
consume();
token_type = TokenType::UnsignedShiftRightEquals;
}
}
bool found_three_char_token = false; bool found_three_char_token = false;
if (m_position + 1 < m_source.length()) { if (!found_four_char_token && m_position + 1 < m_source.length()) {
char second_char = m_source[m_position]; char second_char = m_source[m_position];
char third_char = m_source[m_position + 1]; char third_char = m_source[m_position + 1];
char three_chars[] { (char)m_current_char, second_char, third_char, 0 }; char three_chars[] { (char)m_current_char, second_char, third_char, 0 };
@ -229,7 +261,7 @@ Token Lexer::next()
} }
bool found_two_char_token = false; bool found_two_char_token = false;
if (!found_three_char_token && m_position < m_source.length()) { if (!found_four_char_token && !found_three_char_token && m_position < m_source.length()) {
char second_char = m_source[m_position]; char second_char = m_source[m_position];
char two_chars[] { (char)m_current_char, second_char, 0 }; char two_chars[] { (char)m_current_char, second_char, 0 };
auto it = s_two_char_tokens.find(two_chars); auto it = s_two_char_tokens.find(two_chars);
@ -242,7 +274,7 @@ Token Lexer::next()
} }
bool found_one_char_token = false; bool found_one_char_token = false;
if (!found_three_char_token && !found_two_char_token) { if (!found_four_char_token && !found_three_char_token && !found_two_char_token) {
auto it = s_single_char_tokens.find(m_current_char); auto it = s_single_char_tokens.find(m_current_char);
if (it != s_single_char_tokens.end()) { if (it != s_single_char_tokens.end()) {
found_one_char_token = true; found_one_char_token = true;
@ -251,7 +283,7 @@ Token Lexer::next()
} }
} }
if (!found_three_char_token && !found_two_char_token && !found_one_char_token) { if (!found_four_char_token && !found_three_char_token && !found_two_char_token && !found_one_char_token) {
consume(); consume();
token_type = TokenType::Invalid; token_type = TokenType::Invalid;
} }

View file

@ -25,14 +25,142 @@
*/ */
#include "Parser.h" #include "Parser.h"
#include <AK/HashMap.h>
#include <AK/StdLibExtras.h> #include <AK/StdLibExtras.h>
#include <stdio.h> #include <stdio.h>
namespace JS { namespace JS {
static HashMap<TokenType, int> g_operator_precedence;
Parser::Parser(Lexer lexer) Parser::Parser(Lexer lexer)
: m_lexer(move(lexer)) : m_lexer(move(lexer))
, m_current_token(m_lexer.next()) , 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() NonnullOwnPtr<Program> Parser::parse_program()
@ -54,7 +182,7 @@ NonnullOwnPtr<Program> Parser::parse_program()
NonnullOwnPtr<Statement> Parser::parse_statement() NonnullOwnPtr<Statement> Parser::parse_statement()
{ {
if (match_expression()) { if (match_expression()) {
return make<JS::ExpressionStatement>(parse_expression()); return make<JS::ExpressionStatement>(parse_expression(0));
} }
switch (m_current_token.type()) { switch (m_current_token.type()) {
@ -83,7 +211,7 @@ NonnullOwnPtr<Expression> Parser::parse_primary_expression()
switch (m_current_token.type()) { switch (m_current_token.type()) {
case TokenType::ParenOpen: { case TokenType::ParenOpen: {
consume(TokenType::ParenOpen); consume(TokenType::ParenOpen);
auto expression = parse_expression(); auto expression = parse_expression(0);
consume(TokenType::ParenClose); consume(TokenType::ParenClose);
return expression; return expression;
} }
@ -113,68 +241,75 @@ NonnullOwnPtr<ObjectExpression> Parser::parse_object_expression()
return make<ObjectExpression>(); return make<ObjectExpression>();
} }
NonnullOwnPtr<Expression> Parser::parse_expression() NonnullOwnPtr<Expression> Parser::parse_expression(int min_precedence, Associativity associativity)
{ {
auto expression = parse_primary_expression(); auto expression = parse_primary_expression();
while (match_secondary_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; 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()) { switch (m_current_token.type()) {
case TokenType::Plus: case TokenType::Plus:
consume(); 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: case TokenType::PlusEquals:
consume(); 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: case TokenType::Minus:
consume(); 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: case TokenType::MinusEquals:
consume(); 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: case TokenType::Asterisk:
consume(); 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: case TokenType::AsteriskEquals:
consume(); 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: case TokenType::Slash:
consume(); 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: case TokenType::SlashEquals:
consume(); 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: case TokenType::GreaterThan:
consume(); 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: case TokenType::GreaterThanEquals:
consume(); 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: case TokenType::LessThan:
consume(); 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: case TokenType::LessThanEquals:
consume(); 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: case TokenType::EqualsEqualsEquals:
consume(); 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: case TokenType::ExclamationMarkEqualsEquals:
consume(); 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: case TokenType::ParenOpen:
return parse_call_expression(move(lhs)); return parse_call_expression(move(lhs));
case TokenType::Equals: case TokenType::Equals:
consume(); 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: case TokenType::Period:
consume(); consume();
return make<MemberExpression>(move(lhs), parse_expression()); return make<MemberExpression>(move(lhs), parse_expression(min_precedence, associativity));
case TokenType::PlusPlus: case TokenType::PlusPlus:
consume(); consume();
return make<UpdateExpression>(UpdateOp::Increment, move(lhs)); return make<UpdateExpression>(UpdateOp::Increment, move(lhs));
@ -196,7 +331,7 @@ NonnullOwnPtr<CallExpression> Parser::parse_call_expression(NonnullOwnPtr<Expres
NonnullOwnPtrVector<Expression> arguments; NonnullOwnPtrVector<Expression> arguments;
while (match_expression()) { while (match_expression()) {
arguments.append(parse_expression()); arguments.append(parse_expression(0));
if (!match(TokenType::Comma)) if (!match(TokenType::Comma))
break; break;
consume(); consume();
@ -204,20 +339,14 @@ NonnullOwnPtr<CallExpression> Parser::parse_call_expression(NonnullOwnPtr<Expres
consume(TokenType::ParenClose); consume(TokenType::ParenClose);
// FIXME: Allow lhs expression instead of just a string return make<CallExpression>(move(lhs), move(arguments));
if (lhs->is_identifier()) {
return make<CallExpression>(static_cast<Identifier*>(lhs.ptr())->string(), move(arguments));
}
m_has_errors = true;
return make<CallExpression>("***ERROR***");
} }
NonnullOwnPtr<ReturnStatement> Parser::parse_return_statement() NonnullOwnPtr<ReturnStatement> Parser::parse_return_statement()
{ {
consume(TokenType::Return); consume(TokenType::Return);
if (match_expression()) { if (match_expression()) {
return make<ReturnStatement>(parse_expression()); return make<ReturnStatement>(parse_expression(0));
} }
return make<ReturnStatement>(nullptr); return make<ReturnStatement>(nullptr);
} }
@ -283,7 +412,7 @@ NonnullOwnPtr<VariableDeclaration> Parser::parse_variable_declaration()
OwnPtr<Expression> initializer; OwnPtr<Expression> initializer;
if (match(TokenType::Equals)) { if (match(TokenType::Equals)) {
consume(); consume();
initializer = parse_expression(); initializer = parse_expression(0);
} }
return make<VariableDeclaration>(make<Identifier>(name), move(initializer), declaration_type); return make<VariableDeclaration>(make<Identifier>(name), move(initializer), declaration_type);
} }
@ -313,7 +442,7 @@ NonnullOwnPtr<ForStatement> Parser::parse_for_statement()
case TokenType::Semicolon: case TokenType::Semicolon:
break; break;
default: default:
test = parse_expression(); test = parse_expression(0);
break; break;
} }
@ -324,7 +453,7 @@ NonnullOwnPtr<ForStatement> Parser::parse_for_statement()
case TokenType::Semicolon: case TokenType::Semicolon:
break; break;
default: default:
update = parse_expression(); update = parse_expression(0);
break; break;
} }
@ -405,9 +534,9 @@ bool Parser::done() const
Token Parser::consume() Token Parser::consume()
{ {
auto oldToken = m_current_token; auto old_token = m_current_token;
m_current_token = m_lexer.next(); m_current_token = m_lexer.next();
return oldToken; return old_token;
} }
Token Parser::consume(TokenType type) Token Parser::consume(TokenType type)

View file

@ -31,6 +31,12 @@
#include <AK/NonnullOwnPtr.h> #include <AK/NonnullOwnPtr.h>
namespace JS { namespace JS {
enum class Associativity {
Left,
Right
};
class Parser { class Parser {
public: public:
explicit Parser(Lexer lexer); explicit Parser(Lexer lexer);
@ -44,15 +50,17 @@ public:
NonnullOwnPtr<VariableDeclaration> parse_variable_declaration(); NonnullOwnPtr<VariableDeclaration> parse_variable_declaration();
NonnullOwnPtr<ForStatement> parse_for_statement(); NonnullOwnPtr<ForStatement> parse_for_statement();
NonnullOwnPtr<Expression> parse_expression(); NonnullOwnPtr<Expression> parse_expression(int min_precedence, Associativity associate = Associativity::Right);
NonnullOwnPtr<Expression> parse_primary_expression(); NonnullOwnPtr<Expression> parse_primary_expression();
NonnullOwnPtr<ObjectExpression> parse_object_expression(); NonnullOwnPtr<ObjectExpression> parse_object_expression();
NonnullOwnPtr<Expression> parse_secondary_expression(NonnullOwnPtr<Expression>); NonnullOwnPtr<Expression> parse_secondary_expression(NonnullOwnPtr<Expression>, int min_precedence, Associativity associate = Associativity::Right);
NonnullOwnPtr<CallExpression> parse_call_expression(NonnullOwnPtr<Expression>); NonnullOwnPtr<CallExpression> parse_call_expression(NonnullOwnPtr<Expression>);
bool has_errors() const { return m_has_errors; } bool has_errors() const { return m_has_errors; }
private: private:
int operator_precedence(TokenType) const;
Associativity operator_associativity(TokenType) const;
bool match_expression() const; bool match_expression() const;
bool match_secondary_expression() const; bool match_secondary_expression() const;
bool match_statement() const; bool match_statement() const;

View file

@ -25,6 +25,7 @@
*/ */
#include "Token.h" #include "Token.h"
#include <AK/Assertions.h>
namespace JS { namespace JS {
@ -37,14 +38,20 @@ const char* Token::name(TokenType type)
return "AmpersandEquals"; return "AmpersandEquals";
case TokenType::Asterisk: case TokenType::Asterisk:
return "Asterisk"; return "Asterisk";
case TokenType::AsteriskAsteriskEquals:
return "AsteriskAsteriskEquals";
case TokenType::AsteriskEquals: case TokenType::AsteriskEquals:
return "AsteriskEquals"; return "AsteriskEquals";
case TokenType::Await:
return "Await";
case TokenType::BoolLiteral: case TokenType::BoolLiteral:
return "BoolLiteral"; return "BoolLiteral";
case TokenType::BracketOpen: case TokenType::BracketOpen:
return "BracketOpen"; return "BracketOpen";
case TokenType::BracketClose: case TokenType::BracketClose:
return "BracketClose"; return "BracketClose";
case TokenType::Caret:
return "Caret";
case TokenType::Catch: case TokenType::Catch:
return "Catch"; return "Catch";
case TokenType::Class: case TokenType::Class:
@ -63,8 +70,12 @@ const char* Token::name(TokenType type)
return "Do"; return "Do";
case TokenType::DoubleAmpersand: case TokenType::DoubleAmpersand:
return "DoubleAmpersand"; return "DoubleAmpersand";
case TokenType::DoubleAsterisk:
return "DoubleAsterisk";
case TokenType::DoublePipe: case TokenType::DoublePipe:
return "DoublePipe"; return "DoublePipe";
case TokenType::DoubleQuestionMark:
return "DoubleQuestionMark";
case TokenType::Else: case TokenType::Else:
return "Else"; return "Else";
case TokenType::Eof: case TokenType::Eof:
@ -77,6 +88,8 @@ const char* Token::name(TokenType type)
return "ExclamationMark"; return "ExclamationMark";
case TokenType::ExclamationMarkEquals: case TokenType::ExclamationMarkEquals:
return "ExclamationMarkEquals"; return "ExclamationMarkEquals";
case TokenType::ExclamationMarkEqualsEquals:
return "ExclamationMarkEqualsEquals";
case TokenType::Finally: case TokenType::Finally:
return "Finally"; return "Finally";
case TokenType::Function: case TokenType::Function:
@ -87,6 +100,10 @@ const char* Token::name(TokenType type)
return "Identifier"; return "Identifier";
case TokenType::If: case TokenType::If:
return "If"; return "If";
case TokenType::In:
return "In";
case TokenType::Instanceof:
return "Instanceof";
case TokenType::Interface: case TokenType::Interface:
return "Interface"; return "Interface";
case TokenType::Invalid: case TokenType::Invalid:
@ -129,6 +146,8 @@ const char* Token::name(TokenType type)
return "PlusPlus"; return "PlusPlus";
case TokenType::QuestionMark: case TokenType::QuestionMark:
return "QuestionMark"; return "QuestionMark";
case TokenType::QuestionMarkPeriod:
return "QuestionMarkPeriod";
case TokenType::RegexLiteral: case TokenType::RegexLiteral:
return "RegexLiteral"; return "RegexLiteral";
case TokenType::Return: case TokenType::Return:
@ -137,21 +156,38 @@ const char* Token::name(TokenType type)
return "Semicolon"; return "Semicolon";
case TokenType::ShiftLeft: case TokenType::ShiftLeft:
return "ShiftLeft"; return "ShiftLeft";
case TokenType::ShiftLeftEquals:
return "ShiftLeftEquals";
case TokenType::ShiftRight: case TokenType::ShiftRight:
return "ShiftRight"; return "ShiftRight";
case TokenType::ShiftRightEquals:
return "ShiftRightEquals";
case TokenType::Slash: case TokenType::Slash:
return "Slash"; return "Slash";
case TokenType::SlashEquals: case TokenType::SlashEquals:
return "SlashEquals"; return "SlashEquals";
case TokenType::StringLiteral: case TokenType::StringLiteral:
return "StringLiteral"; return "StringLiteral";
case TokenType::Tilde:
return "Tilde";
case TokenType::Try: case TokenType::Try:
return "Try"; return "Try";
case TokenType::Typeof:
return "Typeof";
case TokenType::UnsignedShiftRight:
return "UnsignedShiftRight";
case TokenType::UnsignedShiftRightEquals:
return "UnsignedShiftRightEquals";
case TokenType::Var: case TokenType::Var:
return "Var"; return "Var";
case TokenType::Void:
return "Void";
case TokenType::While: case TokenType::While:
return "While"; return "While";
case TokenType::Yield:
return "Yield";
default: default:
ASSERT_NOT_REACHED();
return "<Unknown>"; return "<Unknown>";
} }
} }

View file

@ -35,10 +35,13 @@ enum class TokenType {
Ampersand, Ampersand,
AmpersandEquals, AmpersandEquals,
Asterisk, Asterisk,
AsteriskAsteriskEquals,
AsteriskEquals, AsteriskEquals,
Await,
BoolLiteral, BoolLiteral,
BracketClose, BracketClose,
BracketOpen, BracketOpen,
Caret,
Catch, Catch,
Class, Class,
Comma, Comma,
@ -48,7 +51,9 @@ enum class TokenType {
Delete, Delete,
Do, Do,
DoubleAmpersand, DoubleAmpersand,
DoubleAsterisk,
DoublePipe, DoublePipe,
DoubleQuestionMark,
Else, Else,
Eof, Eof,
Equals, Equals,
@ -64,6 +69,8 @@ enum class TokenType {
GreaterThanEquals, GreaterThanEquals,
Identifier, Identifier,
If, If,
In,
Instanceof,
Interface, Interface,
Invalid, Invalid,
LessThan, LessThan,
@ -86,17 +93,26 @@ enum class TokenType {
PlusEquals, PlusEquals,
PlusPlus, PlusPlus,
QuestionMark, QuestionMark,
QuestionMarkPeriod,
RegexLiteral, RegexLiteral,
Return, Return,
Semicolon, Semicolon,
ShiftLeft, ShiftLeft,
ShiftLeftEquals,
ShiftRight, ShiftRight,
ShiftRightEquals,
Slash, Slash,
SlashEquals, SlashEquals,
StringLiteral, StringLiteral,
Tilde,
Try, Try,
Typeof,
UnsignedShiftRight,
UnsignedShiftRightEquals,
Var, Var,
While Void,
While,
Yield,
}; };
class Token { class Token {
@ -125,3 +141,11 @@ private:
}; };
} }
namespace AK {
template<>
struct Traits<JS::TokenType> : public GenericTraits<JS::TokenType> {
static constexpr bool is_trivial() { return true; }
static unsigned hash(JS::TokenType t) { return int_hash((int)t); }
};
}