2020-03-11 18:27:43 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2020, Stephan Unverwerth <s.unverwerth@gmx.de>
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
|
|
|
*
|
|
|
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
|
|
* list of conditions and the following disclaimer.
|
|
|
|
*
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
|
|
* and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
|
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "Parser.h"
|
2020-03-12 22:02:41 +00:00
|
|
|
#include <AK/HashMap.h>
|
2020-03-11 18:27:43 +00:00
|
|
|
#include <AK/StdLibExtras.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
namespace JS {
|
2020-03-12 22:02:41 +00:00
|
|
|
|
|
|
|
static HashMap<TokenType, int> g_operator_precedence;
|
|
|
|
|
2020-03-11 18:27:43 +00:00
|
|
|
Parser::Parser(Lexer lexer)
|
|
|
|
: m_lexer(move(lexer))
|
|
|
|
, m_current_token(m_lexer.next())
|
|
|
|
{
|
2020-03-12 22:02:41 +00:00
|
|
|
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:
|
2020-03-28 10:48:52 +00:00
|
|
|
case TokenType::Typeof:
|
2020-03-12 22:02:41 +00:00
|
|
|
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;
|
|
|
|
}
|
2020-03-11 18:27:43 +00:00
|
|
|
}
|
|
|
|
|
2020-03-18 10:23:53 +00:00
|
|
|
NonnullRefPtr<Program> Parser::parse_program()
|
2020-03-11 18:27:43 +00:00
|
|
|
{
|
2020-03-18 10:23:53 +00:00
|
|
|
auto program = adopt(*new Program);
|
2020-03-11 18:27:43 +00:00
|
|
|
while (!done()) {
|
|
|
|
if (match(TokenType::Semicolon)) {
|
|
|
|
consume();
|
|
|
|
} else if (match_statement()) {
|
|
|
|
program->append(parse_statement());
|
|
|
|
} else {
|
|
|
|
expected("statement");
|
|
|
|
consume();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return program;
|
|
|
|
}
|
|
|
|
|
2020-03-18 10:23:53 +00:00
|
|
|
NonnullRefPtr<Statement> Parser::parse_statement()
|
2020-03-11 18:27:43 +00:00
|
|
|
{
|
2020-03-23 18:08:32 +00:00
|
|
|
auto statement = [this]() -> NonnullRefPtr<Statement> {
|
2020-03-11 18:27:43 +00:00
|
|
|
switch (m_current_token.type()) {
|
|
|
|
case TokenType::Function:
|
2020-03-19 10:52:56 +00:00
|
|
|
return parse_function_node<FunctionDeclaration>();
|
2020-03-11 18:27:43 +00:00
|
|
|
case TokenType::CurlyOpen:
|
|
|
|
return parse_block_statement();
|
|
|
|
case TokenType::Return:
|
|
|
|
return parse_return_statement();
|
|
|
|
case TokenType::Var:
|
2020-03-12 11:16:48 +00:00
|
|
|
case TokenType::Let:
|
2020-03-12 12:24:34 +00:00
|
|
|
case TokenType::Const:
|
2020-03-11 18:27:43 +00:00
|
|
|
return parse_variable_declaration();
|
2020-03-12 12:12:12 +00:00
|
|
|
case TokenType::For:
|
|
|
|
return parse_for_statement();
|
2020-03-21 17:40:17 +00:00
|
|
|
case TokenType::If:
|
|
|
|
return parse_if_statement();
|
2020-03-24 21:03:50 +00:00
|
|
|
case TokenType::Throw:
|
|
|
|
return parse_throw_statement();
|
2020-03-24 13:03:55 +00:00
|
|
|
case TokenType::Try:
|
|
|
|
return parse_try_statement();
|
2020-03-11 18:27:43 +00:00
|
|
|
default:
|
2020-03-23 18:08:32 +00:00
|
|
|
if (match_expression())
|
|
|
|
return adopt(*new ExpressionStatement(parse_expression(0)));
|
2020-03-11 18:27:43 +00:00
|
|
|
m_has_errors = true;
|
|
|
|
expected("statement (missing switch case)");
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<ErrorStatement>();
|
2020-03-23 18:08:32 +00:00
|
|
|
} }();
|
|
|
|
if (match(TokenType::Semicolon))
|
|
|
|
consume();
|
|
|
|
return statement;
|
2020-03-11 18:27:43 +00:00
|
|
|
}
|
|
|
|
|
2020-03-18 10:23:53 +00:00
|
|
|
NonnullRefPtr<Expression> Parser::parse_primary_expression()
|
2020-03-11 18:27:43 +00:00
|
|
|
{
|
2020-03-25 08:51:54 +00:00
|
|
|
if (match_unary_prefixed_expression())
|
|
|
|
return parse_unary_prefixed_expression();
|
|
|
|
|
2020-03-11 18:27:43 +00:00
|
|
|
switch (m_current_token.type()) {
|
|
|
|
case TokenType::ParenOpen: {
|
|
|
|
consume(TokenType::ParenOpen);
|
2020-03-12 22:02:41 +00:00
|
|
|
auto expression = parse_expression(0);
|
2020-03-11 18:27:43 +00:00
|
|
|
consume(TokenType::ParenClose);
|
|
|
|
return expression;
|
|
|
|
}
|
|
|
|
case TokenType::Identifier:
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<Identifier>(consume().value());
|
2020-03-11 18:27:43 +00:00
|
|
|
case TokenType::NumericLiteral:
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<NumericLiteral>(consume().double_value());
|
2020-03-11 18:27:43 +00:00
|
|
|
case TokenType::BoolLiteral:
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<BooleanLiteral>(consume().bool_value());
|
2020-03-12 12:05:06 +00:00
|
|
|
case TokenType::StringLiteral:
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<StringLiteral>(consume().string_value());
|
2020-03-15 21:32:34 +00:00
|
|
|
case TokenType::NullLiteral:
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<NullLiteral>();
|
2020-03-15 21:32:34 +00:00
|
|
|
case TokenType::UndefinedLiteral:
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<UndefinedLiteral>();
|
2020-03-11 18:27:43 +00:00
|
|
|
case TokenType::CurlyOpen:
|
|
|
|
return parse_object_expression();
|
2020-03-19 10:52:56 +00:00
|
|
|
case TokenType::Function:
|
|
|
|
return parse_function_node<FunctionExpression>();
|
2020-03-20 19:29:57 +00:00
|
|
|
case TokenType::BracketOpen:
|
|
|
|
return parse_array_expression();
|
2020-03-11 18:27:43 +00:00
|
|
|
default:
|
|
|
|
m_has_errors = true;
|
|
|
|
expected("primary expression (missing switch case)");
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<ErrorExpression>();
|
2020-03-11 18:27:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-18 10:23:53 +00:00
|
|
|
NonnullRefPtr<Expression> Parser::parse_unary_prefixed_expression()
|
2020-03-14 18:45:51 +00:00
|
|
|
{
|
2020-03-28 10:48:52 +00:00
|
|
|
auto precedence = operator_precedence(m_current_token.type());
|
|
|
|
auto associativity = operator_associativity(m_current_token.type());
|
2020-03-14 18:45:51 +00:00
|
|
|
switch (m_current_token.type()) {
|
|
|
|
case TokenType::PlusPlus:
|
|
|
|
consume();
|
2020-03-28 10:48:52 +00:00
|
|
|
return create_ast_node<UpdateExpression>(UpdateOp::Increment, parse_expression(precedence, associativity), true);
|
2020-03-14 18:45:51 +00:00
|
|
|
case TokenType::MinusMinus:
|
|
|
|
consume();
|
2020-03-28 10:48:52 +00:00
|
|
|
return create_ast_node<UpdateExpression>(UpdateOp::Decrement, parse_expression(precedence, associativity), true);
|
2020-03-14 18:45:51 +00:00
|
|
|
case TokenType::ExclamationMark:
|
|
|
|
consume();
|
2020-03-28 10:48:52 +00:00
|
|
|
return create_ast_node<UnaryExpression>(UnaryOp::Not, parse_expression(precedence, associativity));
|
2020-03-14 18:45:51 +00:00
|
|
|
case TokenType::Tilde:
|
|
|
|
consume();
|
2020-03-28 10:48:52 +00:00
|
|
|
return create_ast_node<UnaryExpression>(UnaryOp::BitwiseNot, parse_expression(precedence, associativity));
|
2020-03-17 19:33:32 +00:00
|
|
|
case TokenType::Typeof:
|
|
|
|
consume();
|
2020-03-28 10:48:52 +00:00
|
|
|
return create_ast_node<UnaryExpression>(UnaryOp::Typeof, parse_expression(precedence, associativity));
|
2020-03-14 18:45:51 +00:00
|
|
|
default:
|
|
|
|
m_has_errors = true;
|
|
|
|
expected("primary expression (missing switch case)");
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<ErrorExpression>();
|
2020-03-14 18:45:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-18 10:23:53 +00:00
|
|
|
NonnullRefPtr<ObjectExpression> Parser::parse_object_expression()
|
2020-03-11 18:27:43 +00:00
|
|
|
{
|
2020-03-22 10:07:55 +00:00
|
|
|
HashMap<FlyString, NonnullRefPtr<Expression>> properties;
|
2020-03-11 18:27:43 +00:00
|
|
|
consume(TokenType::CurlyOpen);
|
2020-03-21 00:29:00 +00:00
|
|
|
|
|
|
|
while (!match(TokenType::CurlyClose)) {
|
|
|
|
auto identifier = create_ast_node<Identifier>(consume(TokenType::Identifier).value());
|
|
|
|
|
|
|
|
if (match(TokenType::Colon)) {
|
|
|
|
consume(TokenType::Colon);
|
|
|
|
properties.set(identifier->string(), parse_expression(0));
|
|
|
|
} else {
|
|
|
|
properties.set(identifier->string(), identifier);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!match(TokenType::Comma))
|
|
|
|
break;
|
|
|
|
|
|
|
|
consume(TokenType::Comma);
|
|
|
|
}
|
|
|
|
|
2020-03-11 18:27:43 +00:00
|
|
|
consume(TokenType::CurlyClose);
|
2020-03-21 00:29:00 +00:00
|
|
|
return create_ast_node<ObjectExpression>(properties);
|
2020-03-11 18:27:43 +00:00
|
|
|
}
|
|
|
|
|
2020-03-20 19:29:57 +00:00
|
|
|
NonnullRefPtr<ArrayExpression> Parser::parse_array_expression()
|
|
|
|
{
|
|
|
|
consume(TokenType::BracketOpen);
|
|
|
|
|
|
|
|
NonnullRefPtrVector<Expression> elements;
|
|
|
|
while (match_expression()) {
|
|
|
|
elements.append(parse_expression(0));
|
|
|
|
if (!match(TokenType::Comma))
|
|
|
|
break;
|
|
|
|
consume(TokenType::Comma);
|
|
|
|
}
|
|
|
|
|
|
|
|
consume(TokenType::BracketClose);
|
|
|
|
return create_ast_node<ArrayExpression>(move(elements));
|
|
|
|
}
|
|
|
|
|
2020-03-18 10:23:53 +00:00
|
|
|
NonnullRefPtr<Expression> Parser::parse_expression(int min_precedence, Associativity associativity)
|
2020-03-11 18:27:43 +00:00
|
|
|
{
|
|
|
|
auto expression = parse_primary_expression();
|
|
|
|
while (match_secondary_expression()) {
|
2020-03-12 22:02:41 +00:00
|
|
|
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);
|
2020-03-11 18:27:43 +00:00
|
|
|
}
|
|
|
|
return expression;
|
|
|
|
}
|
|
|
|
|
2020-03-18 10:23:53 +00:00
|
|
|
NonnullRefPtr<Expression> Parser::parse_secondary_expression(NonnullRefPtr<Expression> lhs, int min_precedence, Associativity associativity)
|
2020-03-11 18:27:43 +00:00
|
|
|
{
|
|
|
|
switch (m_current_token.type()) {
|
|
|
|
case TokenType::Plus:
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<BinaryExpression>(BinaryOp::Plus, move(lhs), parse_expression(min_precedence, associativity));
|
2020-03-12 12:09:15 +00:00
|
|
|
case TokenType::PlusEquals:
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<AssignmentExpression>(AssignmentOp::AdditionAssignment, move(lhs), parse_expression(min_precedence, associativity));
|
2020-03-11 18:27:43 +00:00
|
|
|
case TokenType::Minus:
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<BinaryExpression>(BinaryOp::Minus, move(lhs), parse_expression(min_precedence, associativity));
|
2020-03-12 12:09:15 +00:00
|
|
|
case TokenType::MinusEquals:
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<AssignmentExpression>(AssignmentOp::SubtractionAssignment, move(lhs), parse_expression(min_precedence, associativity));
|
2020-03-12 12:04:52 +00:00
|
|
|
case TokenType::Asterisk:
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<BinaryExpression>(BinaryOp::Asterisk, move(lhs), parse_expression(min_precedence, associativity));
|
2020-03-12 12:09:15 +00:00
|
|
|
case TokenType::AsteriskEquals:
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<AssignmentExpression>(AssignmentOp::MultiplicationAssignment, move(lhs), parse_expression(min_precedence, associativity));
|
2020-03-12 12:04:52 +00:00
|
|
|
case TokenType::Slash:
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<BinaryExpression>(BinaryOp::Slash, move(lhs), parse_expression(min_precedence, associativity));
|
2020-03-12 12:09:15 +00:00
|
|
|
case TokenType::SlashEquals:
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<AssignmentExpression>(AssignmentOp::DivisionAssignment, move(lhs), parse_expression(min_precedence, associativity));
|
2020-03-12 12:10:27 +00:00
|
|
|
case TokenType::GreaterThan:
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<BinaryExpression>(BinaryOp::GreaterThan, move(lhs), parse_expression(min_precedence, associativity));
|
2020-03-12 12:07:08 +00:00
|
|
|
case TokenType::GreaterThanEquals:
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<BinaryExpression>(BinaryOp::GreaterThanEquals, move(lhs), parse_expression(min_precedence, associativity));
|
2020-03-12 12:10:27 +00:00
|
|
|
case TokenType::LessThan:
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<BinaryExpression>(BinaryOp::LessThan, move(lhs), parse_expression(min_precedence, associativity));
|
2020-03-12 12:07:08 +00:00
|
|
|
case TokenType::LessThanEquals:
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<BinaryExpression>(BinaryOp::LessThanEquals, move(lhs), parse_expression(min_precedence, associativity));
|
2020-03-12 12:11:33 +00:00
|
|
|
case TokenType::EqualsEqualsEquals:
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<BinaryExpression>(BinaryOp::TypedEquals, move(lhs), parse_expression(min_precedence, associativity));
|
2020-03-12 12:11:33 +00:00
|
|
|
case TokenType::ExclamationMarkEqualsEquals:
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<BinaryExpression>(BinaryOp::TypedInequals, move(lhs), parse_expression(min_precedence, associativity));
|
2020-03-15 22:23:38 +00:00
|
|
|
case TokenType::EqualsEquals:
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<BinaryExpression>(BinaryOp::AbstractEquals, move(lhs), parse_expression(min_precedence, associativity));
|
2020-03-15 22:23:38 +00:00
|
|
|
case TokenType::ExclamationMarkEquals:
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<BinaryExpression>(BinaryOp::AbstractInequals, move(lhs), parse_expression(min_precedence, associativity));
|
2020-03-11 18:27:43 +00:00
|
|
|
case TokenType::ParenOpen:
|
|
|
|
return parse_call_expression(move(lhs));
|
|
|
|
case TokenType::Equals:
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<AssignmentExpression>(AssignmentOp::Assignment, move(lhs), parse_expression(min_precedence, associativity));
|
2020-03-12 12:05:57 +00:00
|
|
|
case TokenType::Period:
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<MemberExpression>(move(lhs), parse_expression(min_precedence, associativity));
|
2020-03-20 19:51:03 +00:00
|
|
|
case TokenType::BracketOpen: {
|
|
|
|
consume(TokenType::BracketOpen);
|
2020-03-23 18:08:32 +00:00
|
|
|
auto expression = create_ast_node<MemberExpression>(move(lhs), parse_expression(0), true);
|
2020-03-20 19:51:03 +00:00
|
|
|
consume(TokenType::BracketClose);
|
|
|
|
return expression;
|
|
|
|
}
|
2020-03-12 11:45:45 +00:00
|
|
|
case TokenType::PlusPlus:
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<UpdateExpression>(UpdateOp::Increment, move(lhs));
|
2020-03-12 11:45:45 +00:00
|
|
|
case TokenType::MinusMinus:
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<UpdateExpression>(UpdateOp::Decrement, move(lhs));
|
2020-03-15 21:35:22 +00:00
|
|
|
case TokenType::DoubleAmpersand:
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<LogicalExpression>(LogicalOp::And, move(lhs), parse_expression(min_precedence, associativity));
|
2020-03-15 21:35:22 +00:00
|
|
|
case TokenType::DoublePipe:
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<LogicalExpression>(LogicalOp::Or, move(lhs), parse_expression(min_precedence, associativity));
|
2020-03-11 18:27:43 +00:00
|
|
|
default:
|
|
|
|
m_has_errors = true;
|
|
|
|
expected("secondary expression (missing switch case)");
|
|
|
|
consume();
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<ErrorExpression>();
|
2020-03-11 18:27:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-18 10:23:53 +00:00
|
|
|
NonnullRefPtr<CallExpression> Parser::parse_call_expression(NonnullRefPtr<Expression> lhs)
|
2020-03-11 18:27:43 +00:00
|
|
|
{
|
|
|
|
consume(TokenType::ParenOpen);
|
2020-03-12 18:35:23 +00:00
|
|
|
|
2020-03-18 10:23:53 +00:00
|
|
|
NonnullRefPtrVector<Expression> arguments;
|
2020-03-12 18:35:23 +00:00
|
|
|
|
2020-03-12 19:03:12 +00:00
|
|
|
while (match_expression()) {
|
2020-03-12 22:02:41 +00:00
|
|
|
arguments.append(parse_expression(0));
|
2020-03-12 19:03:12 +00:00
|
|
|
if (!match(TokenType::Comma))
|
|
|
|
break;
|
|
|
|
consume();
|
2020-03-12 18:35:23 +00:00
|
|
|
}
|
|
|
|
|
2020-03-11 18:27:43 +00:00
|
|
|
consume(TokenType::ParenClose);
|
|
|
|
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<CallExpression>(move(lhs), move(arguments));
|
2020-03-11 18:27:43 +00:00
|
|
|
}
|
|
|
|
|
2020-03-18 10:23:53 +00:00
|
|
|
NonnullRefPtr<ReturnStatement> Parser::parse_return_statement()
|
2020-03-11 18:27:43 +00:00
|
|
|
{
|
|
|
|
consume(TokenType::Return);
|
|
|
|
if (match_expression()) {
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<ReturnStatement>(parse_expression(0));
|
2020-03-11 18:27:43 +00:00
|
|
|
}
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<ReturnStatement>(nullptr);
|
2020-03-11 18:27:43 +00:00
|
|
|
}
|
|
|
|
|
2020-03-18 10:23:53 +00:00
|
|
|
NonnullRefPtr<BlockStatement> Parser::parse_block_statement()
|
2020-03-11 18:27:43 +00:00
|
|
|
{
|
2020-03-18 10:23:53 +00:00
|
|
|
auto block = create_ast_node<BlockStatement>();
|
2020-03-11 18:27:43 +00:00
|
|
|
consume(TokenType::CurlyOpen);
|
|
|
|
while (!done() && !match(TokenType::CurlyClose)) {
|
|
|
|
if (match(TokenType::Semicolon)) {
|
|
|
|
consume();
|
|
|
|
} else if (match_statement()) {
|
|
|
|
block->append(parse_statement());
|
|
|
|
} else {
|
|
|
|
expected("statement");
|
|
|
|
consume();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
consume(TokenType::CurlyClose);
|
|
|
|
return block;
|
|
|
|
}
|
|
|
|
|
2020-03-19 10:52:56 +00:00
|
|
|
template<typename FunctionNodeType>
|
|
|
|
NonnullRefPtr<FunctionNodeType> Parser::parse_function_node()
|
2020-03-11 18:27:43 +00:00
|
|
|
{
|
|
|
|
consume(TokenType::Function);
|
2020-03-19 10:52:56 +00:00
|
|
|
String name;
|
|
|
|
if (FunctionNodeType::must_have_name()) {
|
|
|
|
name = consume(TokenType::Identifier).value();
|
|
|
|
} else {
|
|
|
|
if (match(TokenType::Identifier))
|
|
|
|
name = consume(TokenType::Identifier).value();
|
|
|
|
}
|
2020-03-11 18:27:43 +00:00
|
|
|
consume(TokenType::ParenOpen);
|
2020-03-22 10:07:55 +00:00
|
|
|
Vector<FlyString> parameters;
|
2020-03-11 18:27:43 +00:00
|
|
|
while (match(TokenType::Identifier)) {
|
2020-03-13 13:40:24 +00:00
|
|
|
auto parameter = consume(TokenType::Identifier).value();
|
|
|
|
parameters.append(parameter);
|
2020-03-11 18:27:43 +00:00
|
|
|
if (match(TokenType::ParenClose)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
consume(TokenType::Comma);
|
|
|
|
}
|
|
|
|
consume(TokenType::ParenClose);
|
|
|
|
auto body = parse_block_statement();
|
2020-03-19 10:52:56 +00:00
|
|
|
return create_ast_node<FunctionNodeType>(name, move(body), move(parameters));
|
2020-03-11 18:27:43 +00:00
|
|
|
}
|
|
|
|
|
2020-03-18 10:23:53 +00:00
|
|
|
NonnullRefPtr<VariableDeclaration> Parser::parse_variable_declaration()
|
2020-03-11 18:27:43 +00:00
|
|
|
{
|
2020-03-12 11:16:48 +00:00
|
|
|
DeclarationType declaration_type;
|
|
|
|
|
|
|
|
switch (m_current_token.type()) {
|
|
|
|
case TokenType::Var:
|
|
|
|
declaration_type = DeclarationType::Var;
|
|
|
|
consume(TokenType::Var);
|
|
|
|
break;
|
|
|
|
case TokenType::Let:
|
|
|
|
declaration_type = DeclarationType::Let;
|
|
|
|
consume(TokenType::Let);
|
|
|
|
break;
|
2020-03-12 12:24:34 +00:00
|
|
|
case TokenType::Const:
|
|
|
|
declaration_type = DeclarationType::Const;
|
|
|
|
consume(TokenType::Const);
|
|
|
|
break;
|
2020-03-12 11:16:48 +00:00
|
|
|
default:
|
|
|
|
ASSERT_NOT_REACHED();
|
|
|
|
}
|
2020-03-11 18:27:43 +00:00
|
|
|
auto name = consume(TokenType::Identifier).value();
|
2020-03-18 10:23:53 +00:00
|
|
|
RefPtr<Expression> initializer;
|
2020-03-11 18:27:43 +00:00
|
|
|
if (match(TokenType::Equals)) {
|
|
|
|
consume();
|
2020-03-12 22:02:41 +00:00
|
|
|
initializer = parse_expression(0);
|
2020-03-11 18:27:43 +00:00
|
|
|
}
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<VariableDeclaration>(create_ast_node<Identifier>(name), move(initializer), declaration_type);
|
2020-03-11 18:27:43 +00:00
|
|
|
}
|
|
|
|
|
2020-03-24 21:03:50 +00:00
|
|
|
NonnullRefPtr<ThrowStatement> Parser::parse_throw_statement()
|
|
|
|
{
|
|
|
|
consume(TokenType::Throw);
|
|
|
|
return create_ast_node<ThrowStatement>(parse_expression(0));
|
|
|
|
}
|
|
|
|
|
2020-03-24 13:03:55 +00:00
|
|
|
NonnullRefPtr<TryStatement> Parser::parse_try_statement()
|
|
|
|
{
|
|
|
|
consume(TokenType::Try);
|
|
|
|
|
|
|
|
auto block = parse_block_statement();
|
|
|
|
|
|
|
|
RefPtr<CatchClause> handler;
|
|
|
|
if (match(TokenType::Catch))
|
|
|
|
handler = parse_catch_clause();
|
|
|
|
|
|
|
|
RefPtr<BlockStatement> finalizer;
|
|
|
|
if (match(TokenType::Finally)) {
|
|
|
|
consume();
|
|
|
|
finalizer = parse_block_statement();
|
|
|
|
}
|
|
|
|
|
|
|
|
return create_ast_node<TryStatement>(move(block), move(handler), move(finalizer));
|
|
|
|
}
|
|
|
|
|
|
|
|
NonnullRefPtr<CatchClause> Parser::parse_catch_clause()
|
|
|
|
{
|
|
|
|
consume(TokenType::Catch);
|
|
|
|
|
|
|
|
String parameter;
|
|
|
|
if (match(TokenType::ParenOpen)) {
|
|
|
|
consume();
|
|
|
|
parameter = consume(TokenType::Identifier).value();
|
|
|
|
consume(TokenType::ParenClose);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto body = parse_block_statement();
|
|
|
|
return create_ast_node<CatchClause>(parameter, move(body));
|
|
|
|
}
|
|
|
|
|
2020-03-21 17:40:17 +00:00
|
|
|
NonnullRefPtr<IfStatement> Parser::parse_if_statement()
|
|
|
|
{
|
|
|
|
consume(TokenType::If);
|
|
|
|
consume(TokenType::ParenOpen);
|
|
|
|
auto predicate = parse_expression(0);
|
|
|
|
consume(TokenType::ParenClose);
|
2020-03-23 15:46:41 +00:00
|
|
|
auto consequent = parse_statement();
|
|
|
|
RefPtr<Statement> alternate;
|
|
|
|
if (match(TokenType::Else)) {
|
|
|
|
consume(TokenType::Else);
|
|
|
|
alternate = parse_statement();
|
|
|
|
}
|
|
|
|
return create_ast_node<IfStatement>(move(predicate), move(consequent), move(alternate));
|
2020-03-21 17:40:17 +00:00
|
|
|
}
|
|
|
|
|
2020-03-18 10:23:53 +00:00
|
|
|
NonnullRefPtr<ForStatement> Parser::parse_for_statement()
|
2020-03-12 12:12:12 +00:00
|
|
|
{
|
|
|
|
consume(TokenType::For);
|
|
|
|
|
|
|
|
consume(TokenType::ParenOpen);
|
|
|
|
|
2020-03-23 18:08:32 +00:00
|
|
|
RefPtr<ASTNode> init;
|
2020-03-12 12:12:12 +00:00
|
|
|
switch (m_current_token.type()) {
|
|
|
|
case TokenType::Semicolon:
|
|
|
|
break;
|
|
|
|
default:
|
2020-03-23 18:08:32 +00:00
|
|
|
if (match_expression())
|
|
|
|
init = parse_expression(0);
|
|
|
|
else if (match_variable_declaration())
|
|
|
|
init = parse_variable_declaration();
|
|
|
|
else
|
|
|
|
ASSERT_NOT_REACHED();
|
2020-03-12 12:12:12 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
consume(TokenType::Semicolon);
|
|
|
|
|
2020-03-18 10:23:53 +00:00
|
|
|
RefPtr<Expression> test;
|
2020-03-12 12:12:12 +00:00
|
|
|
switch (m_current_token.type()) {
|
|
|
|
case TokenType::Semicolon:
|
|
|
|
break;
|
|
|
|
default:
|
2020-03-12 22:02:41 +00:00
|
|
|
test = parse_expression(0);
|
2020-03-12 12:12:12 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
consume(TokenType::Semicolon);
|
|
|
|
|
2020-03-18 10:23:53 +00:00
|
|
|
RefPtr<Expression> update;
|
2020-03-12 12:12:12 +00:00
|
|
|
switch (m_current_token.type()) {
|
2020-03-25 15:14:18 +00:00
|
|
|
case TokenType::ParenClose:
|
2020-03-12 12:12:12 +00:00
|
|
|
break;
|
|
|
|
default:
|
2020-03-12 22:02:41 +00:00
|
|
|
update = parse_expression(0);
|
2020-03-12 12:12:12 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
consume(TokenType::ParenClose);
|
|
|
|
|
|
|
|
auto body = parse_block_statement();
|
|
|
|
|
2020-03-18 10:23:53 +00:00
|
|
|
return create_ast_node<ForStatement>(move(init), move(test), move(update), move(body));
|
2020-03-12 12:12:12 +00:00
|
|
|
}
|
|
|
|
|
2020-03-11 18:27:43 +00:00
|
|
|
bool Parser::match(TokenType type) const
|
|
|
|
{
|
|
|
|
return m_current_token.type() == type;
|
|
|
|
}
|
|
|
|
|
2020-03-23 18:08:32 +00:00
|
|
|
bool Parser::match_variable_declaration() const
|
|
|
|
{
|
|
|
|
switch (m_current_token.type()) {
|
|
|
|
case TokenType::Var:
|
|
|
|
case TokenType::Let:
|
|
|
|
case TokenType::Const:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-11 18:27:43 +00:00
|
|
|
bool Parser::match_expression() const
|
|
|
|
{
|
|
|
|
auto type = m_current_token.type();
|
|
|
|
return type == TokenType::BoolLiteral
|
|
|
|
|| type == TokenType::NumericLiteral
|
|
|
|
|| type == TokenType::StringLiteral
|
2020-03-15 21:32:34 +00:00
|
|
|
|| type == TokenType::UndefinedLiteral
|
2020-03-11 18:27:43 +00:00
|
|
|
|| type == TokenType::NullLiteral
|
|
|
|
|| type == TokenType::Identifier
|
|
|
|
|| type == TokenType::New
|
|
|
|
|| type == TokenType::CurlyOpen
|
|
|
|
|| type == TokenType::BracketOpen
|
2020-03-14 18:45:51 +00:00
|
|
|
|| type == TokenType::ParenOpen
|
2020-03-19 10:52:56 +00:00
|
|
|
|| type == TokenType::Function
|
2020-03-14 18:45:51 +00:00
|
|
|
|| match_unary_prefixed_expression();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Parser::match_unary_prefixed_expression() const
|
|
|
|
{
|
|
|
|
auto type = m_current_token.type();
|
|
|
|
return type == TokenType::PlusPlus
|
|
|
|
|| type == TokenType::MinusMinus
|
|
|
|
|| type == TokenType::ExclamationMark
|
2020-03-17 19:33:32 +00:00
|
|
|
|| type == TokenType::Tilde
|
|
|
|
|| type == TokenType::Typeof;
|
2020-03-11 18:27:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Parser::match_secondary_expression() const
|
|
|
|
{
|
|
|
|
auto type = m_current_token.type();
|
|
|
|
return type == TokenType::Plus
|
2020-03-12 12:09:15 +00:00
|
|
|
|| type == TokenType::PlusEquals
|
2020-03-11 18:27:43 +00:00
|
|
|
|| type == TokenType::Minus
|
2020-03-12 12:09:15 +00:00
|
|
|
|| type == TokenType::MinusEquals
|
2020-03-11 18:27:43 +00:00
|
|
|
|| type == TokenType::Asterisk
|
2020-03-12 12:09:15 +00:00
|
|
|
|| type == TokenType::AsteriskEquals
|
2020-03-11 18:27:43 +00:00
|
|
|
|| type == TokenType::Slash
|
2020-03-12 12:09:15 +00:00
|
|
|
|| type == TokenType::SlashEquals
|
2020-03-11 18:27:43 +00:00
|
|
|
|| type == TokenType::Equals
|
2020-03-12 12:11:33 +00:00
|
|
|
|| type == TokenType::EqualsEqualsEquals
|
|
|
|
|| type == TokenType::ExclamationMarkEqualsEquals
|
2020-03-15 22:23:38 +00:00
|
|
|
|| type == TokenType::EqualsEquals
|
|
|
|
|| type == TokenType::ExclamationMarkEquals
|
2020-03-12 12:10:27 +00:00
|
|
|
|| type == TokenType::GreaterThan
|
2020-03-12 12:07:08 +00:00
|
|
|
|| type == TokenType::GreaterThanEquals
|
2020-03-12 12:10:27 +00:00
|
|
|
|| type == TokenType::LessThan
|
2020-03-12 12:07:08 +00:00
|
|
|
|| type == TokenType::LessThanEquals
|
2020-03-12 12:05:57 +00:00
|
|
|
|| type == TokenType::ParenOpen
|
2020-03-12 11:45:45 +00:00
|
|
|
|| type == TokenType::Period
|
2020-03-20 19:51:03 +00:00
|
|
|
|| type == TokenType::BracketOpen
|
2020-03-12 11:45:45 +00:00
|
|
|
|| type == TokenType::PlusPlus
|
|
|
|
|| type == TokenType::MinusMinus;
|
2020-03-11 18:27:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Parser::match_statement() const
|
|
|
|
{
|
|
|
|
auto type = m_current_token.type();
|
|
|
|
return match_expression()
|
|
|
|
|| type == TokenType::Function
|
|
|
|
|| type == TokenType::Return
|
|
|
|
|| type == TokenType::Let
|
|
|
|
|| type == TokenType::Class
|
|
|
|
|| type == TokenType::Delete
|
|
|
|
|| type == TokenType::Do
|
|
|
|
|| type == TokenType::If
|
2020-03-24 21:03:50 +00:00
|
|
|
|| type == TokenType::Throw
|
2020-03-11 18:27:43 +00:00
|
|
|
|| type == TokenType::Try
|
|
|
|
|| type == TokenType::While
|
2020-03-12 12:12:12 +00:00
|
|
|
|| type == TokenType::For
|
2020-03-11 18:27:43 +00:00
|
|
|
|| type == TokenType::Const
|
|
|
|
|| type == TokenType::CurlyOpen
|
|
|
|
|| type == TokenType::Var;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Parser::done() const
|
|
|
|
{
|
|
|
|
return match(TokenType::Eof);
|
|
|
|
}
|
|
|
|
|
|
|
|
Token Parser::consume()
|
|
|
|
{
|
2020-03-12 22:02:41 +00:00
|
|
|
auto old_token = m_current_token;
|
2020-03-11 18:27:43 +00:00
|
|
|
m_current_token = m_lexer.next();
|
2020-03-12 22:02:41 +00:00
|
|
|
return old_token;
|
2020-03-11 18:27:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Token Parser::consume(TokenType type)
|
|
|
|
{
|
|
|
|
if (m_current_token.type() != type) {
|
|
|
|
m_has_errors = true;
|
|
|
|
fprintf(stderr, "Error: Unexpected token %s. Expected %s\n", m_current_token.name(), Token::name(type));
|
|
|
|
}
|
|
|
|
return consume();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Parser::expected(const char* what)
|
|
|
|
{
|
|
|
|
m_has_errors = true;
|
|
|
|
fprintf(stderr, "Error: Unexpected token %s. Expected %s\n", m_current_token.name(), what);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|