mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 23:50:19 +00:00
1142 lines
36 KiB
C++
1142 lines
36 KiB
C++
/*
|
|
* Copyright (c) 2021, Itamar S. <itamar8910@gmail.com>
|
|
* Copyright (c) 2023, Volodymyr V. <vvmposeydon@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include "Parser.h"
|
|
#include "AST.h"
|
|
#include <LibGLSL/Lexer.h>
|
|
|
|
namespace GLSL {
|
|
|
|
Parser::Parser(Vector<Token> tokens, String const& filename)
|
|
: m_filename(move(filename))
|
|
, m_tokens(move(tokens))
|
|
{
|
|
}
|
|
|
|
ErrorOr<NonnullRefPtr<TranslationUnit>> Parser::parse()
|
|
{
|
|
if (m_tokens.is_empty())
|
|
return create_root_ast_node({}, {});
|
|
auto unit = create_root_ast_node(m_tokens.first().start(), m_tokens.last().end());
|
|
unit->set_declarations(TRY(parse_declarations_in_translation_unit(*unit)));
|
|
return unit;
|
|
}
|
|
|
|
bool Parser::eof() const
|
|
{
|
|
return m_state.token_index >= m_tokens.size();
|
|
}
|
|
|
|
void Parser::print_tokens() const
|
|
{
|
|
for (auto& token : m_tokens) {
|
|
outln("{}", token.to_string());
|
|
}
|
|
}
|
|
|
|
ErrorOr<Optional<Parser::DeclarationType>> Parser::match_declaration_in_translation_unit()
|
|
{
|
|
if (TRY(match_variable_declaration()))
|
|
return DeclarationType::Variable;
|
|
if (TRY(match_function_declaration()))
|
|
return DeclarationType::Function;
|
|
if (TRY(match_struct_declaration()))
|
|
return DeclarationType::Struct;
|
|
return Optional<DeclarationType>();
|
|
}
|
|
|
|
ErrorOr<bool> Parser::match_struct_declaration()
|
|
{
|
|
save_state();
|
|
ScopeGuard state_guard = [this] { load_state(); };
|
|
|
|
if (!match_keyword("struct"sv))
|
|
return false;
|
|
TRY(consume(Token::Type::Keyword));
|
|
|
|
if (!match(Token::Type::Identifier))
|
|
return false;
|
|
TRY(consume(Token::Type::Identifier));
|
|
|
|
return match(Token::Type::LeftCurly);
|
|
}
|
|
|
|
ErrorOr<bool> Parser::match_function_declaration()
|
|
{
|
|
save_state();
|
|
ScopeGuard state_guard = [this] { load_state(); };
|
|
|
|
if (!TRY(match_type()))
|
|
return false;
|
|
|
|
VERIFY(m_root_node);
|
|
(void)parse_type(get_dummy_node());
|
|
|
|
if (!TRY(match_name()))
|
|
return false;
|
|
|
|
(void)parse_name(get_dummy_node());
|
|
|
|
if (!peek(Token::Type::LeftParen).has_value())
|
|
return false;
|
|
TRY(consume());
|
|
|
|
while (TRY(consume()).type() != Token::Type::RightParen && !eof())
|
|
;
|
|
|
|
if (peek(Token::Type::Semicolon).has_value() || peek(Token::Type::LeftCurly).has_value())
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
ErrorOr<bool> Parser::match_variable_declaration()
|
|
{
|
|
save_state();
|
|
ScopeGuard state_guard = [this] { load_state(); };
|
|
|
|
if (!TRY(match_type())) {
|
|
return false;
|
|
}
|
|
|
|
VERIFY(m_root_node);
|
|
(void)parse_type(get_dummy_node());
|
|
|
|
// Identifier
|
|
if (!TRY(match_name()))
|
|
return false;
|
|
|
|
(void)TRY(parse_name(get_dummy_node()));
|
|
|
|
while (!eof() && (peek().type() == Token::Type::LeftBracket)) {
|
|
TRY(consume(Token::Type::LeftBracket));
|
|
|
|
if (match(Token::Type::Integer)) {
|
|
TRY(consume(Token::Type::Integer));
|
|
}
|
|
if (!match(Token::Type::RightBracket)) {
|
|
TRY(error("No closing right bracket"sv));
|
|
return false;
|
|
}
|
|
TRY(consume(Token::Type::RightBracket));
|
|
}
|
|
|
|
if (match(Token::Type::Equals)) {
|
|
TRY(consume(Token::Type::Equals));
|
|
if (!TRY(match_expression())) {
|
|
TRY(error("initial value of variable is not an expression"sv));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return match(Token::Type::Semicolon);
|
|
}
|
|
|
|
ErrorOr<bool> Parser::match_block_statement()
|
|
{
|
|
return peek().type() == Token::Type::LeftCurly;
|
|
}
|
|
|
|
ErrorOr<bool> Parser::match_expression()
|
|
{
|
|
return TRY(match_name())
|
|
|| match_unary_op()
|
|
|| match(Token::Type::LeftParen)
|
|
|| TRY(match_boolean_literal())
|
|
|| TRY(match_numeric_literal())
|
|
|| TRY(match_string_literal());
|
|
}
|
|
|
|
ErrorOr<bool> Parser::match_name()
|
|
{
|
|
auto type = peek().type();
|
|
return type == Token::Type::Identifier || type == Token::Type::KnownType;
|
|
}
|
|
|
|
ErrorOr<bool> Parser::match_string_literal()
|
|
{
|
|
return match(Token::Type::DoubleQuotedString) || match(Token::Type::SingleQuotedString);
|
|
}
|
|
|
|
ErrorOr<bool> Parser::match_numeric_literal()
|
|
{
|
|
return match(Token::Type::Float) || match(Token::Type::Integer);
|
|
}
|
|
|
|
ErrorOr<bool> Parser::match_boolean_literal()
|
|
{
|
|
auto token = peek();
|
|
if (token.type() != Token::Type::Keyword)
|
|
return false;
|
|
auto text = token.text();
|
|
return text == "true" || text == "false";
|
|
}
|
|
|
|
ErrorOr<bool> Parser::match_type()
|
|
{
|
|
save_state();
|
|
ScopeGuard state_guard = [this] { load_state(); };
|
|
|
|
if (match_storage_qualifier())
|
|
TRY(consume_storage_qualifier());
|
|
|
|
if (!TRY(match_name()))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
ErrorOr<Vector<NonnullRefPtr<Declaration const>>> Parser::parse_declarations_in_translation_unit(ASTNode const& parent)
|
|
{
|
|
Vector<NonnullRefPtr<Declaration const>> declarations;
|
|
while (!eof()) {
|
|
auto declaration = TRY(parse_single_declaration_in_translation_unit(parent));
|
|
if (declaration) {
|
|
declarations.append(declaration.release_nonnull());
|
|
} else {
|
|
TRY(error("unexpected token"sv));
|
|
TRY(consume());
|
|
}
|
|
}
|
|
return declarations;
|
|
}
|
|
|
|
ErrorOr<RefPtr<Declaration const>> Parser::parse_single_declaration_in_translation_unit(ASTNode const& parent)
|
|
{
|
|
while (!eof()) {
|
|
if (match_preprocessor()) {
|
|
TRY(consume_preprocessor());
|
|
continue;
|
|
}
|
|
|
|
auto declaration = TRY(match_declaration_in_translation_unit());
|
|
if (declaration.has_value()) {
|
|
return parse_declaration(parent, declaration.value());
|
|
}
|
|
return nullptr;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
ErrorOr<NonnullRefPtr<Declaration const>> Parser::parse_declaration(ASTNode const& parent, DeclarationType declaration_type)
|
|
{
|
|
switch (declaration_type) {
|
|
case DeclarationType::Function:
|
|
return parse_function_declaration(parent);
|
|
case DeclarationType::Variable:
|
|
return parse_variable_declaration(parent);
|
|
case DeclarationType::Struct:
|
|
return parse_struct_declaration(parent);
|
|
default:
|
|
TRY(error("unexpected declaration type"sv));
|
|
return create_ast_node<InvalidDeclaration>(parent, position(), position());
|
|
}
|
|
}
|
|
|
|
ErrorOr<NonnullRefPtr<StructDeclaration const>> Parser::parse_struct_declaration(ASTNode const& parent)
|
|
{
|
|
TRY(consume_keyword("struct"sv));
|
|
|
|
auto decl = create_ast_node<StructDeclaration>(parent, position(), {});
|
|
decl->set_name(TRY(parse_name(*decl)));
|
|
|
|
TRY(consume(Token::Type::LeftCurly));
|
|
while (!eof() && peek().type() != Token::Type::RightCurly) {
|
|
decl->set_members(TRY(parse_struct_members(*decl)));
|
|
}
|
|
TRY(consume(Token::Type::RightCurly));
|
|
|
|
TRY(consume(Token::Type::Semicolon));
|
|
decl->set_end(position());
|
|
|
|
return decl;
|
|
}
|
|
|
|
ErrorOr<Vector<NonnullRefPtr<Declaration const>>> Parser::parse_struct_members(StructDeclaration& parent)
|
|
{
|
|
Vector<NonnullRefPtr<Declaration const>> members;
|
|
while (!eof() && peek().type() != Token::Type::RightCurly) {
|
|
auto member = TRY(parse_variable_declaration(parent));
|
|
members.append(move(member));
|
|
}
|
|
return members;
|
|
}
|
|
|
|
ErrorOr<NonnullRefPtr<FunctionDeclaration const>> Parser::parse_function_declaration(ASTNode const& parent)
|
|
{
|
|
auto func = create_ast_node<FunctionDeclaration>(parent, position(), {});
|
|
|
|
func->set_return_type(TRY(parse_type(*func)));
|
|
func->set_name(TRY(parse_name(*func)));
|
|
|
|
TRY(consume(Token::Type::LeftParen));
|
|
func->set_parameters(TRY(parse_parameter_list(*func)));
|
|
TRY(consume(Token::Type::RightParen));
|
|
|
|
RefPtr<FunctionDefinition const> body;
|
|
Position func_end {};
|
|
if (peek(Token::Type::LeftCurly).has_value()) {
|
|
body = TRY(parse_function_definition(*func));
|
|
func_end = body->end();
|
|
} else {
|
|
func_end = position();
|
|
TRY(consume(Token::Type::Semicolon));
|
|
}
|
|
|
|
func->set_definition(move(body));
|
|
func->set_end(func_end);
|
|
return func;
|
|
}
|
|
|
|
ErrorOr<Vector<NonnullRefPtr<Parameter const>>> Parser::parse_parameter_list(ASTNode const& parent)
|
|
{
|
|
Vector<NonnullRefPtr<Parameter const>> parameters;
|
|
while (peek().type() != Token::Type::RightParen && !eof()) {
|
|
auto type = TRY(parse_type(parent));
|
|
|
|
RefPtr<Name const> name;
|
|
if (TRY(match_name()))
|
|
name = TRY(parse_name(parent));
|
|
|
|
auto param = create_ast_node<Parameter>(parent, type->start(), !name.is_null() ? name->end() : type->end(), name);
|
|
const_cast<Type&>(*type).set_parent(*param.ptr());
|
|
|
|
param->set_type(move(type));
|
|
parameters.append(move(param));
|
|
|
|
if (peek(Token::Type::Comma).has_value())
|
|
TRY(consume(Token::Type::Comma));
|
|
}
|
|
return parameters;
|
|
}
|
|
|
|
ErrorOr<NonnullRefPtr<FunctionDefinition const>> Parser::parse_function_definition(ASTNode const& parent)
|
|
{
|
|
auto func = create_ast_node<FunctionDefinition>(parent, position(), {});
|
|
|
|
TRY(consume(Token::Type::LeftCurly));
|
|
while (!eof() && peek().type() != Token::Type::RightCurly) {
|
|
func->add_statement(TRY(parse_statement(func)));
|
|
}
|
|
func->set_end(position());
|
|
TRY(consume(Token::Type::RightCurly));
|
|
|
|
return func;
|
|
}
|
|
|
|
ErrorOr<NonnullRefPtr<VariableDeclaration const>> Parser::parse_variable_declaration(ASTNode const& parent, bool expect_semicolon)
|
|
{
|
|
auto var = create_ast_node<VariableDeclaration>(parent, position(), {});
|
|
if (!TRY(match_variable_declaration())) {
|
|
TRY(error("unexpected token for variable type"sv));
|
|
var->set_end(position());
|
|
return var;
|
|
}
|
|
var->set_type(TRY(parse_type(var)));
|
|
auto name = TRY(parse_name(*var, true));
|
|
RefPtr<Expression const> initial_value;
|
|
|
|
if (match(Token::Type::Equals)) {
|
|
TRY(consume(Token::Type::Equals));
|
|
initial_value = TRY(parse_expression(var));
|
|
}
|
|
|
|
if (expect_semicolon)
|
|
TRY(consume(Token::Type::Semicolon));
|
|
|
|
var->set_end(position());
|
|
var->set_name(name);
|
|
var->set_initial_value(move(initial_value));
|
|
|
|
return var;
|
|
}
|
|
|
|
ErrorOr<NonnullRefPtr<Statement const>> Parser::parse_statement(ASTNode const& parent)
|
|
{
|
|
bool should_consume_semicolon = true;
|
|
RefPtr<Statement const> result;
|
|
|
|
if (TRY(match_block_statement())) {
|
|
should_consume_semicolon = false;
|
|
result = TRY(parse_block_statement(parent));
|
|
} else if (TRY(match_variable_declaration())) {
|
|
result = TRY(parse_variable_declaration(parent, false));
|
|
} else if (TRY(match_expression())) {
|
|
result = TRY(parse_expression(parent));
|
|
} else if (match_keyword("return"sv)) {
|
|
result = TRY(parse_return_statement(parent));
|
|
} else if (match_keyword("discard"sv)) {
|
|
auto start = position();
|
|
TRY(consume());
|
|
result = create_ast_node<DiscardStatement>(parent, start, position());
|
|
} else if (match_keyword("for"sv)) {
|
|
should_consume_semicolon = false;
|
|
result = TRY(parse_for_statement(parent));
|
|
} else if (match_keyword("if"sv)) {
|
|
should_consume_semicolon = false;
|
|
result = TRY(parse_if_statement(parent));
|
|
} else {
|
|
TRY(error("unexpected statement type"sv));
|
|
should_consume_semicolon = false;
|
|
TRY(consume());
|
|
return create_ast_node<InvalidStatement>(parent, position(), position());
|
|
}
|
|
|
|
if (should_consume_semicolon)
|
|
TRY(consume(Token::Type::Semicolon));
|
|
return result.release_nonnull();
|
|
}
|
|
|
|
ErrorOr<NonnullRefPtr<BlockStatement const>> Parser::parse_block_statement(ASTNode const& parent)
|
|
{
|
|
auto block_statement = create_ast_node<BlockStatement>(parent, position(), {});
|
|
|
|
TRY(consume(Token::Type::LeftCurly));
|
|
while (!eof() && peek().type() != Token::Type::RightCurly) {
|
|
block_statement->add_statement(TRY(parse_statement(*block_statement)));
|
|
}
|
|
TRY(consume(Token::Type::RightCurly));
|
|
|
|
block_statement->set_end(position());
|
|
return block_statement;
|
|
}
|
|
|
|
ErrorOr<NonnullRefPtr<IfStatement const>> Parser::parse_if_statement(ASTNode const& parent)
|
|
{
|
|
auto if_statement = create_ast_node<IfStatement>(parent, position(), {});
|
|
TRY(consume_keyword("if"sv));
|
|
TRY(consume(Token::Type::LeftParen));
|
|
if_statement->set_predicate(TRY(parse_expression(*if_statement)));
|
|
TRY(consume(Token::Type::RightParen));
|
|
if_statement->set_then_statement(TRY(parse_statement(*if_statement)));
|
|
if (match_keyword("else"sv)) {
|
|
TRY(consume(Token::Type::Keyword));
|
|
if_statement->set_else_statement(TRY(parse_statement(*if_statement)));
|
|
if_statement->set_end(if_statement->else_statement()->end());
|
|
} else {
|
|
if_statement->set_end(if_statement->then_statement()->end());
|
|
}
|
|
return if_statement;
|
|
}
|
|
|
|
ErrorOr<NonnullRefPtr<ForStatement const>> Parser::parse_for_statement(ASTNode const& parent)
|
|
{
|
|
auto for_statement = create_ast_node<ForStatement>(parent, position(), {});
|
|
TRY(consume_keyword("for"sv));
|
|
TRY(consume(Token::Type::LeftParen));
|
|
if (peek().type() != Token::Type::Semicolon)
|
|
for_statement->set_init(TRY(parse_variable_declaration(*for_statement, false)));
|
|
TRY(consume(Token::Type::Semicolon));
|
|
|
|
if (peek().type() != Token::Type::Semicolon)
|
|
for_statement->set_test(TRY(parse_expression(*for_statement)));
|
|
TRY(consume(Token::Type::Semicolon));
|
|
|
|
if (peek().type() != Token::Type::RightParen)
|
|
for_statement->set_update(TRY(parse_expression(*for_statement)));
|
|
TRY(consume(Token::Type::RightParen));
|
|
|
|
for_statement->set_body(TRY(parse_statement(*for_statement)));
|
|
|
|
for_statement->set_end(for_statement->body()->end());
|
|
return for_statement;
|
|
}
|
|
|
|
ErrorOr<NonnullRefPtr<ReturnStatement const>> Parser::parse_return_statement(ASTNode const& parent)
|
|
{
|
|
auto return_statement = create_ast_node<ReturnStatement>(parent, position(), {});
|
|
TRY(consume_keyword("return"sv));
|
|
if (!peek(Token::Type::Semicolon).has_value()) {
|
|
return_statement->set_value(TRY(parse_expression(*return_statement)));
|
|
}
|
|
return_statement->set_end(position());
|
|
return return_statement;
|
|
}
|
|
|
|
HashMap<BinaryOp, int> s_operator_precedence = {
|
|
{ BinaryOp::Assignment, 1 },
|
|
{ BinaryOp::AdditionAssignment, 1 },
|
|
{ BinaryOp::SubtractionAssignment, 1 },
|
|
{ BinaryOp::MultiplicationAssignment, 1 },
|
|
{ BinaryOp::DivisionAssignment, 1 },
|
|
{ BinaryOp::ModuloAssignment, 1 },
|
|
{ BinaryOp::AndAssignment, 1 },
|
|
{ BinaryOp::XorAssignment, 1 },
|
|
{ BinaryOp::OrAssignment, 1 },
|
|
{ BinaryOp::LeftShiftAssignment, 1 },
|
|
{ BinaryOp::RightShiftAssignment, 1 },
|
|
{ BinaryOp::LogicalOr, 2 },
|
|
{ BinaryOp::LogicalXor, 3 },
|
|
{ BinaryOp::LogicalAnd, 4 },
|
|
{ BinaryOp::BitwiseOr, 5 },
|
|
{ BinaryOp::BitwiseXor, 6 },
|
|
{ BinaryOp::BitwiseAnd, 7 },
|
|
{ BinaryOp::EqualsEquals, 8 },
|
|
{ BinaryOp::NotEqual, 8 },
|
|
{ BinaryOp::LessThan, 9 },
|
|
{ BinaryOp::LessThanEquals, 9 },
|
|
{ BinaryOp::GreaterThan, 9 },
|
|
{ BinaryOp::GreaterThanEquals, 9 },
|
|
{ BinaryOp::LeftShift, 10 },
|
|
{ BinaryOp::RightShift, 10 },
|
|
{ BinaryOp::Addition, 11 },
|
|
{ BinaryOp::Subtraction, 11 },
|
|
{ BinaryOp::Multiplication, 12 },
|
|
{ BinaryOp::Division, 12 },
|
|
{ BinaryOp::Modulo, 12 },
|
|
};
|
|
|
|
HashMap<BinaryOp, Parser::Associativity> Parser::s_operator_associativity = {
|
|
{ BinaryOp::Assignment, Associativity::RightToLeft },
|
|
{ BinaryOp::AdditionAssignment, Associativity::RightToLeft },
|
|
{ BinaryOp::SubtractionAssignment, Associativity::RightToLeft },
|
|
{ BinaryOp::MultiplicationAssignment, Associativity::RightToLeft },
|
|
{ BinaryOp::DivisionAssignment, Associativity::RightToLeft },
|
|
{ BinaryOp::ModuloAssignment, Associativity::RightToLeft },
|
|
{ BinaryOp::AndAssignment, Associativity::RightToLeft },
|
|
{ BinaryOp::XorAssignment, Associativity::RightToLeft },
|
|
{ BinaryOp::OrAssignment, Associativity::RightToLeft },
|
|
{ BinaryOp::LeftShiftAssignment, Associativity::RightToLeft },
|
|
{ BinaryOp::RightShiftAssignment, Associativity::RightToLeft },
|
|
{ BinaryOp::LogicalOr, Associativity::LeftToRight },
|
|
{ BinaryOp::LogicalXor, Associativity::LeftToRight },
|
|
{ BinaryOp::LogicalAnd, Associativity::LeftToRight },
|
|
{ BinaryOp::BitwiseOr, Associativity::LeftToRight },
|
|
{ BinaryOp::BitwiseXor, Associativity::LeftToRight },
|
|
{ BinaryOp::BitwiseAnd, Associativity::LeftToRight },
|
|
{ BinaryOp::EqualsEquals, Associativity::LeftToRight },
|
|
{ BinaryOp::NotEqual, Associativity::LeftToRight },
|
|
{ BinaryOp::LessThan, Associativity::LeftToRight },
|
|
{ BinaryOp::LessThanEquals, Associativity::LeftToRight },
|
|
{ BinaryOp::GreaterThan, Associativity::LeftToRight },
|
|
{ BinaryOp::GreaterThanEquals, Associativity::LeftToRight },
|
|
{ BinaryOp::LeftShift, Associativity::LeftToRight },
|
|
{ BinaryOp::RightShift, Associativity::LeftToRight },
|
|
{ BinaryOp::Addition, Associativity::LeftToRight },
|
|
{ BinaryOp::Subtraction, Associativity::LeftToRight },
|
|
{ BinaryOp::Multiplication, Associativity::LeftToRight },
|
|
{ BinaryOp::Division, Associativity::LeftToRight },
|
|
{ BinaryOp::Modulo, Associativity::LeftToRight },
|
|
};
|
|
|
|
ErrorOr<NonnullRefPtr<Expression const>> Parser::parse_expression(ASTNode const& parent, int min_precedence, Associativity associativity)
|
|
{
|
|
auto start_pos = position();
|
|
|
|
auto lhs = TRY(parse_unary_expression(get_dummy_node()));
|
|
|
|
while (match_binary_op()) {
|
|
auto op = TRY(peek_binary_op());
|
|
auto maybe_op_precedence = s_operator_precedence.get(op);
|
|
VERIFY(maybe_op_precedence.has_value());
|
|
|
|
auto op_precedence = maybe_op_precedence.value();
|
|
if (op_precedence < min_precedence || (op_precedence == min_precedence && associativity == Associativity::LeftToRight))
|
|
break;
|
|
TRY(consume());
|
|
|
|
auto maybe_op_associativity = s_operator_associativity.get(op);
|
|
VERIFY(maybe_op_associativity.has_value());
|
|
auto op_associativity = maybe_op_associativity.value();
|
|
|
|
auto expr = create_ast_node<BinaryExpression>(parent, start_pos, {});
|
|
const_cast<Expression&>(*lhs).set_parent(expr);
|
|
expr->set_lhs(move(lhs));
|
|
expr->set_op(op);
|
|
expr->set_rhs(TRY(parse_expression(expr, op_precedence, op_associativity)));
|
|
|
|
expr->set_end(position());
|
|
|
|
lhs = move(expr);
|
|
}
|
|
|
|
return lhs;
|
|
}
|
|
|
|
// NOTE: this function should parse everything with precedence of prefix increment and above, e.g. ++/--/!/~, function call, member expressions and expressions in parentheses
|
|
ErrorOr<NonnullRefPtr<Expression const>> Parser::parse_unary_expression(const GLSL::ASTNode& parent)
|
|
{
|
|
if (match(Token::Type::LeftParen)) {
|
|
TRY(consume(Token::Type::LeftParen));
|
|
auto expr = TRY(parse_expression(parent));
|
|
TRY(consume(Token::Type::RightParen));
|
|
|
|
return expr;
|
|
}
|
|
|
|
if (TRY(match_boolean_literal()))
|
|
return parse_boolean_literal(parent);
|
|
|
|
if (TRY(match_numeric_literal()))
|
|
return parse_numeric_literal(parent);
|
|
|
|
if (TRY(match_string_literal()))
|
|
return parse_string_literal(parent);
|
|
|
|
if (TRY(match_name())) {
|
|
NonnullRefPtr<Expression const> lhs = TRY(parse_name(parent));
|
|
|
|
while (true) {
|
|
if (match(Token::Type::LeftParen)) {
|
|
TRY(consume(Token::Type::LeftParen));
|
|
auto expr = create_ast_node<FunctionCall>(parent, lhs->start(), {});
|
|
auto args = TRY(parse_function_call_args(expr));
|
|
|
|
const_cast<Expression&>(*lhs).set_parent(expr);
|
|
expr->set_callee(move(lhs));
|
|
expr->set_arguments(move(args));
|
|
|
|
TRY(consume(Token::Type::RightParen));
|
|
expr->set_end(position());
|
|
|
|
lhs = move(expr);
|
|
} else if (match(Token::Type::Dot)) {
|
|
TRY(consume(Token::Type::Dot));
|
|
|
|
auto expr = create_ast_node<MemberExpression>(parent, lhs->start(), {});
|
|
auto rhs = TRY(parse_name(expr));
|
|
|
|
const_cast<Expression&>(*lhs).set_parent(expr);
|
|
expr->set_object(move(lhs));
|
|
expr->set_property(move(rhs));
|
|
expr->set_end(position());
|
|
|
|
lhs = move(expr);
|
|
} else if (match(Token::Type::LeftBracket)) {
|
|
TRY(consume(Token::Type::LeftBracket));
|
|
|
|
auto expr = create_ast_node<ArrayElementExpression>(parent, lhs->start(), {});
|
|
auto index = TRY(parse_expression(expr));
|
|
|
|
TRY(consume(Token::Type::RightBracket));
|
|
|
|
const_cast<Expression&>(*lhs).set_parent(expr);
|
|
expr->set_array(move(lhs));
|
|
expr->set_index(move(index));
|
|
expr->set_end(position());
|
|
|
|
lhs = move(expr);
|
|
} else if (match(Token::Type::PlusPlus) || match(Token::Type::MinusMinus)) {
|
|
auto op = TRY(consume_unary_op());
|
|
|
|
auto expr = create_ast_node<UnaryExpression>(parent, lhs->start(), position());
|
|
|
|
const_cast<Expression&>(*lhs).set_parent(expr);
|
|
expr->set_lhs(move(lhs));
|
|
expr->set_op(op);
|
|
expr->set_is_postfix(true);
|
|
|
|
lhs = move(expr);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return lhs;
|
|
}
|
|
|
|
if (match_unary_op()) {
|
|
auto expr = create_ast_node<UnaryExpression>(parent, position(), {});
|
|
auto op = TRY(consume_unary_op());
|
|
|
|
auto lhs = TRY(parse_unary_expression(expr));
|
|
expr->set_lhs(move(lhs));
|
|
expr->set_op(op);
|
|
expr->set_end(position());
|
|
|
|
return expr;
|
|
}
|
|
|
|
TRY(error(TRY(String::formatted("unable to parse unary expression starting with {}", TRY(peek().type_as_string())))));
|
|
return create_ast_node<InvalidExpression>(parent, position(), position());
|
|
}
|
|
|
|
ErrorOr<Vector<NonnullRefPtr<Expression const>>> Parser::parse_function_call_args(ASTNode const& parent)
|
|
{
|
|
Vector<NonnullRefPtr<Expression const>> result;
|
|
while (!match(Token::Type::RightParen)) {
|
|
auto arg = TRY(parse_expression(parent));
|
|
result.append(move(arg));
|
|
|
|
if (!match(Token::Type::RightParen))
|
|
TRY(consume(Token::Type::Comma));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ErrorOr<NonnullRefPtr<Expression const>> Parser::parse_boolean_literal(ASTNode const& parent)
|
|
{
|
|
auto token = TRY(consume(Token::Type::Keyword));
|
|
auto text = token.text();
|
|
bool value = (text == "true");
|
|
return create_ast_node<BooleanLiteral>(parent, token.start(), token.end(), value);
|
|
}
|
|
|
|
ErrorOr<NonnullRefPtr<Expression const>> Parser::parse_numeric_literal(GLSL::ASTNode const& parent)
|
|
{
|
|
auto token = TRY(consume());
|
|
auto text = token.text();
|
|
return create_ast_node<NumericLiteral>(parent, token.start(), token.end(), text);
|
|
}
|
|
|
|
ErrorOr<NonnullRefPtr<Expression const>> Parser::parse_string_literal(ASTNode const& parent)
|
|
{
|
|
Optional<size_t> start_token_index;
|
|
Optional<size_t> end_token_index;
|
|
while (!eof()) {
|
|
auto token = peek();
|
|
if (token.type() != Token::Type::DoubleQuotedString && token.type() != Token::Type::SingleQuotedString && token.type() != Token::Type::EscapeSequence) {
|
|
VERIFY(start_token_index.has_value());
|
|
end_token_index = m_state.token_index - 1;
|
|
break;
|
|
}
|
|
if (!start_token_index.has_value())
|
|
start_token_index = m_state.token_index;
|
|
TRY(consume());
|
|
}
|
|
|
|
// String was not terminated
|
|
if (!end_token_index.has_value()) {
|
|
end_token_index = m_tokens.size() - 1;
|
|
}
|
|
|
|
VERIFY(start_token_index.has_value());
|
|
VERIFY(end_token_index.has_value());
|
|
|
|
Token start_token = m_tokens[start_token_index.value()];
|
|
Token end_token = m_tokens[end_token_index.value()];
|
|
|
|
auto text = TRY(text_in_range(start_token.start(), end_token.end()));
|
|
auto string_literal = create_ast_node<StringLiteral>(parent, start_token.start(), end_token.end());
|
|
string_literal->set_value(move(text));
|
|
return string_literal;
|
|
}
|
|
|
|
ErrorOr<NonnullRefPtr<Name const>> Parser::parse_name(ASTNode const& parent, bool allow_sized_name)
|
|
{
|
|
NonnullRefPtr<Name> name_node = create_ast_node<Name>(parent, position(), {});
|
|
|
|
if (peek().type() == Token::Type::Identifier || peek().type() == Token::Type::KnownType) {
|
|
auto token = TRY(consume());
|
|
name_node->set_name(token.text());
|
|
name_node->set_end(position());
|
|
} else {
|
|
TRY(error("expected keyword or identifier while trying to parse name"sv));
|
|
name_node->set_end(position());
|
|
return name_node;
|
|
}
|
|
|
|
if (peek().type() == Token::Type::LeftBracket && allow_sized_name) {
|
|
NonnullRefPtr<SizedName> sized_name = create_ast_node<SizedName>(parent, name_node->start(), {});
|
|
sized_name->set_name(name_node->name());
|
|
|
|
while (peek().type() == Token::Type::LeftBracket) {
|
|
TRY(consume(Token::Type::LeftBracket));
|
|
|
|
StringView size = "0"sv;
|
|
if (peek().type() == Token::Type::Integer)
|
|
size = TRY(consume(Token::Type::Integer)).text();
|
|
sized_name->append_dimension(size);
|
|
|
|
TRY(consume(Token::Type::RightBracket));
|
|
}
|
|
name_node->set_end(position());
|
|
name_node = sized_name;
|
|
}
|
|
|
|
name_node->set_end(previous_token_end());
|
|
return name_node;
|
|
}
|
|
|
|
ErrorOr<NonnullRefPtr<Type const>> Parser::parse_type(ASTNode const& parent)
|
|
{
|
|
auto type = create_ast_node<Type>(parent, position(), {});
|
|
|
|
Vector<StorageTypeQualifier> storage_qualifiers;
|
|
while (match_storage_qualifier()) {
|
|
storage_qualifiers.append(TRY(consume_storage_qualifier()));
|
|
}
|
|
type->set_storage_qualifiers(move(storage_qualifiers));
|
|
|
|
if (match_keyword("struct"sv)) {
|
|
TRY(consume(Token::Type::Keyword)); // Consume struct prefix
|
|
}
|
|
|
|
if (!TRY(match_name())) {
|
|
type->set_end(position());
|
|
TRY(error(TRY(String::formatted("expected name instead of: {}", peek().text()))));
|
|
return type;
|
|
}
|
|
type->set_name(TRY(parse_name(*type)));
|
|
|
|
type->set_end(previous_token_end());
|
|
|
|
return type;
|
|
}
|
|
|
|
bool Parser::match_unary_op()
|
|
{
|
|
return match(Token::Type::Plus)
|
|
|| match(Token::Type::Minus)
|
|
|| match(Token::Type::PlusPlus)
|
|
|| match(Token::Type::MinusMinus)
|
|
|| match(Token::Type::ExclamationMark)
|
|
|| match(Token::Type::Tilde);
|
|
}
|
|
|
|
ErrorOr<UnaryOp> Parser::consume_unary_op()
|
|
{
|
|
switch (TRY(consume()).type()) {
|
|
case Token::Type::Plus:
|
|
return UnaryOp::Plus;
|
|
case Token::Type::Minus:
|
|
return UnaryOp::Minus;
|
|
case Token::Type::PlusPlus:
|
|
return UnaryOp::PlusPlus;
|
|
case Token::Type::MinusMinus:
|
|
return UnaryOp::MinusMinus;
|
|
case Token::Type::ExclamationMark:
|
|
return UnaryOp::Not;
|
|
case Token::Type::Tilde:
|
|
return UnaryOp::BitwiseNot;
|
|
default:
|
|
VERIFY_NOT_REACHED();
|
|
}
|
|
}
|
|
|
|
bool Parser::match_binary_op()
|
|
{
|
|
return match(Token::Type::Plus)
|
|
|| match(Token::Type::Minus)
|
|
|| match(Token::Type::Asterisk)
|
|
|| match(Token::Type::Slash)
|
|
|| match(Token::Type::Percent)
|
|
|| match(Token::Type::And)
|
|
|| match(Token::Type::Pipe)
|
|
|| match(Token::Type::Caret)
|
|
|| match(Token::Type::AndAnd)
|
|
|| match(Token::Type::PipePipe)
|
|
|| match(Token::Type::CaretCaret)
|
|
|| match(Token::Type::LessLess)
|
|
|| match(Token::Type::GreaterGreater)
|
|
|| match(Token::Type::Less)
|
|
|| match(Token::Type::LessEquals)
|
|
|| match(Token::Type::Greater)
|
|
|| match(Token::Type::GreaterEquals)
|
|
|| match(Token::Type::EqualsEquals)
|
|
|| match(Token::Type::ExclamationMarkEquals)
|
|
|| match(Token::Type::Equals)
|
|
|| match(Token::Type::PlusEquals)
|
|
|| match(Token::Type::MinusEquals)
|
|
|| match(Token::Type::AsteriskEquals)
|
|
|| match(Token::Type::SlashEquals)
|
|
|| match(Token::Type::PercentEquals)
|
|
|| match(Token::Type::LessLessEquals)
|
|
|| match(Token::Type::GreaterGreaterEquals)
|
|
|| match(Token::Type::AndEquals)
|
|
|| match(Token::Type::PipeEquals)
|
|
|| match(Token::Type::CaretEquals);
|
|
}
|
|
|
|
ErrorOr<BinaryOp> Parser::peek_binary_op()
|
|
{
|
|
switch (peek().type()) {
|
|
case Token::Type::Plus:
|
|
return BinaryOp::Addition;
|
|
case Token::Type::Minus:
|
|
return BinaryOp::Subtraction;
|
|
case Token::Type::Asterisk:
|
|
return BinaryOp::Multiplication;
|
|
case Token::Type::Slash:
|
|
return BinaryOp::Division;
|
|
case Token::Type::Percent:
|
|
return BinaryOp::Modulo;
|
|
case Token::Type::And:
|
|
return BinaryOp::BitwiseAnd;
|
|
case Token::Type::Pipe:
|
|
return BinaryOp::BitwiseOr;
|
|
case Token::Type::Caret:
|
|
return BinaryOp::BitwiseXor;
|
|
case Token::Type::AndAnd:
|
|
return BinaryOp::LogicalAnd;
|
|
case Token::Type::PipePipe:
|
|
return BinaryOp::LogicalOr;
|
|
case Token::Type::CaretCaret:
|
|
return BinaryOp::LogicalXor;
|
|
case Token::Type::LessLess:
|
|
return BinaryOp::LeftShift;
|
|
case Token::Type::GreaterGreater:
|
|
return BinaryOp::RightShift;
|
|
case Token::Type::Less:
|
|
return BinaryOp::LessThan;
|
|
case Token::Type::LessEquals:
|
|
return BinaryOp::LessThanEquals;
|
|
case Token::Type::Greater:
|
|
return BinaryOp::GreaterThan;
|
|
case Token::Type::GreaterEquals:
|
|
return BinaryOp::GreaterThanEquals;
|
|
case Token::Type::EqualsEquals:
|
|
return BinaryOp::EqualsEquals;
|
|
case Token::Type::ExclamationMarkEquals:
|
|
return BinaryOp::NotEqual;
|
|
case Token::Type::Equals:
|
|
return BinaryOp::Assignment;
|
|
case Token::Type::PlusEquals:
|
|
return BinaryOp::AdditionAssignment;
|
|
case Token::Type::MinusEquals:
|
|
return BinaryOp::SubtractionAssignment;
|
|
case Token::Type::AsteriskEquals:
|
|
return BinaryOp::MultiplicationAssignment;
|
|
case Token::Type::SlashEquals:
|
|
return BinaryOp::DivisionAssignment;
|
|
case Token::Type::PercentEquals:
|
|
return BinaryOp::ModuloAssignment;
|
|
case Token::Type::LessLessEquals:
|
|
return BinaryOp::LeftShiftAssignment;
|
|
case Token::Type::GreaterGreaterEquals:
|
|
return BinaryOp::RightShiftAssignment;
|
|
case Token::Type::AndEquals:
|
|
return BinaryOp::AndAssignment;
|
|
case Token::Type::PipeEquals:
|
|
return BinaryOp::OrAssignment;
|
|
case Token::Type::CaretEquals:
|
|
return BinaryOp::XorAssignment;
|
|
default:
|
|
VERIFY_NOT_REACHED();
|
|
}
|
|
}
|
|
|
|
bool Parser::match_storage_qualifier()
|
|
{
|
|
return match_keyword("const"sv)
|
|
|| match_keyword("in"sv)
|
|
|| match_keyword("out"sv)
|
|
|| match_keyword("inout"sv)
|
|
|| match_keyword("centroid"sv)
|
|
|| match_keyword("patch"sv)
|
|
|| match_keyword("sample"sv)
|
|
|| match_keyword("uniform"sv)
|
|
|| match_keyword("buffer"sv)
|
|
|| match_keyword("shared"sv)
|
|
|| match_keyword("coherent"sv)
|
|
|| match_keyword("volatile"sv)
|
|
|| match_keyword("restrict"sv)
|
|
|| match_keyword("readonly"sv)
|
|
|| match_keyword("writeonly"sv)
|
|
|| match_keyword("subroutine"sv);
|
|
}
|
|
|
|
ErrorOr<StorageTypeQualifier> Parser::consume_storage_qualifier()
|
|
{
|
|
VERIFY(peek().type() == Token::Type::Keyword);
|
|
auto keyword = MUST(consume()).text();
|
|
if (keyword == "buffer")
|
|
return StorageTypeQualifier::Buffer;
|
|
if (keyword == "centroid")
|
|
return StorageTypeQualifier::Centroid;
|
|
if (keyword == "coherent")
|
|
return StorageTypeQualifier::Coherent;
|
|
if (keyword == "const")
|
|
return StorageTypeQualifier::Const;
|
|
if (keyword == "in")
|
|
return StorageTypeQualifier::In;
|
|
if (keyword == "inout")
|
|
return StorageTypeQualifier::Inout;
|
|
if (keyword == "out")
|
|
return StorageTypeQualifier::Out;
|
|
if (keyword == "patch")
|
|
return StorageTypeQualifier::Patch;
|
|
if (keyword == "readonly")
|
|
return StorageTypeQualifier::Readonly;
|
|
if (keyword == "restrict")
|
|
return StorageTypeQualifier::Restrict;
|
|
if (keyword == "sample")
|
|
return StorageTypeQualifier::Sample;
|
|
if (keyword == "shared")
|
|
return StorageTypeQualifier::Shared;
|
|
if (keyword == "subroutine")
|
|
return StorageTypeQualifier::Subroutine;
|
|
if (keyword == "uniform")
|
|
return StorageTypeQualifier::Uniform;
|
|
if (keyword == "volatile")
|
|
return StorageTypeQualifier::Volatile;
|
|
if (keyword == "writeonly")
|
|
return StorageTypeQualifier::Writeonly;
|
|
VERIFY_NOT_REACHED();
|
|
}
|
|
|
|
Token Parser::peek(size_t offset) const
|
|
{
|
|
if (m_state.token_index + offset >= m_tokens.size())
|
|
return { Token::Type::EOF_TOKEN, position(), position(), {} };
|
|
return m_tokens[m_state.token_index + offset];
|
|
}
|
|
|
|
Optional<Token> Parser::peek(Token::Type type) const
|
|
{
|
|
auto token = peek();
|
|
if (token.type() == type)
|
|
return token;
|
|
return {};
|
|
}
|
|
|
|
bool Parser::match(Token::Type type)
|
|
{
|
|
return peek().type() == type;
|
|
}
|
|
|
|
bool Parser::match_keyword(StringView keyword)
|
|
{
|
|
auto token = peek();
|
|
if (token.type() != Token::Type::Keyword) {
|
|
return false;
|
|
}
|
|
if (token.text() != keyword) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Parser::match_preprocessor()
|
|
{
|
|
return match(Token::Type::PreprocessorStatement) || match(Token::Type::IncludeStatement);
|
|
}
|
|
|
|
ErrorOr<Token> Parser::consume()
|
|
{
|
|
if (eof()) {
|
|
TRY(error("GLSL Parser: out of tokens"sv));
|
|
return Token { Token::Type::EOF_TOKEN, position(), position(), {} };
|
|
}
|
|
return m_tokens[m_state.token_index++];
|
|
}
|
|
|
|
ErrorOr<Token> Parser::consume(Token::Type type)
|
|
{
|
|
auto token = TRY(consume());
|
|
if (token.type() != type)
|
|
TRY(error(TRY(String::formatted("expected {} at {}:{}, found: {}", Token::type_to_string(type), token.start().line, token.start().column, Token::type_to_string(token.type())))));
|
|
return token;
|
|
}
|
|
|
|
ErrorOr<Token> Parser::consume_keyword(StringView keyword)
|
|
{
|
|
auto token = TRY(consume());
|
|
if (token.type() != Token::Type::Keyword) {
|
|
TRY(error(TRY(String::formatted("unexpected token: {}, expected Keyword", TRY(token.to_string())))));
|
|
return token;
|
|
}
|
|
if (token.text() != keyword) {
|
|
TRY(error(TRY(String::formatted("unexpected keyword: {}, expected {}", token.text(), keyword))));
|
|
return token;
|
|
}
|
|
return token;
|
|
}
|
|
|
|
ErrorOr<void> Parser::consume_preprocessor()
|
|
{
|
|
switch (peek().type()) {
|
|
case Token::Type::PreprocessorStatement:
|
|
TRY(consume());
|
|
break;
|
|
case Token::Type::IncludeStatement:
|
|
TRY(consume());
|
|
TRY(consume(Token::Type::IncludePath));
|
|
break;
|
|
default:
|
|
TRY(error("unexpected token while parsing preprocessor statement"sv));
|
|
TRY(consume());
|
|
}
|
|
return {};
|
|
}
|
|
|
|
Position Parser::position() const
|
|
{
|
|
if (m_tokens.is_empty())
|
|
return {};
|
|
|
|
if (eof())
|
|
return m_tokens.last().end();
|
|
|
|
return peek().start();
|
|
}
|
|
|
|
Position Parser::previous_token_end() const
|
|
{
|
|
if (m_state.token_index < 1)
|
|
return {};
|
|
return m_tokens[m_state.token_index - 1].end();
|
|
}
|
|
|
|
Optional<size_t> Parser::index_of_token_at(Position pos) const
|
|
{
|
|
for (size_t token_index = 0; token_index < m_tokens.size(); ++token_index) {
|
|
auto token = m_tokens[token_index];
|
|
if (token.start() > pos || token.end() < pos)
|
|
continue;
|
|
return token_index;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
Vector<Token> Parser::tokens_in_range(Position start, Position end) const
|
|
{
|
|
auto start_token_index = index_of_token_at(start);
|
|
auto end_node_index = index_of_token_at(end);
|
|
VERIFY(start_token_index.has_value());
|
|
VERIFY(end_node_index.has_value());
|
|
|
|
Vector<Token> tokens;
|
|
for (size_t i = start_token_index.value(); i <= end_node_index.value(); ++i) {
|
|
tokens.append(m_tokens[i]);
|
|
}
|
|
return tokens;
|
|
}
|
|
|
|
ErrorOr<String> Parser::text_in_range(Position start, Position end) const
|
|
{
|
|
StringBuilder builder;
|
|
for (auto token : tokens_in_range(start, end)) {
|
|
builder.append(token.text());
|
|
}
|
|
return builder.to_string();
|
|
}
|
|
|
|
ErrorOr<void> Parser::error(StringView message)
|
|
{
|
|
if (!m_saved_states.is_empty())
|
|
return {};
|
|
|
|
if (message.is_null() || message.is_empty())
|
|
message = "<empty>"sv;
|
|
String formatted_message;
|
|
if (m_state.token_index >= m_tokens.size()) {
|
|
formatted_message = TRY(String::formatted("GLSL Parsed error on EOF.{}", message));
|
|
} else {
|
|
formatted_message = TRY(String::formatted("GLSL Parser error: {}. token: {} ({}:{})",
|
|
message,
|
|
m_state.token_index < m_tokens.size() ? m_tokens[m_state.token_index].text() : "EOF"sv,
|
|
m_tokens[m_state.token_index].start().line,
|
|
m_tokens[m_state.token_index].start().column));
|
|
}
|
|
|
|
m_errors.append(formatted_message);
|
|
return {};
|
|
}
|
|
|
|
void Parser::save_state()
|
|
{
|
|
m_saved_states.append(m_state);
|
|
}
|
|
|
|
void Parser::load_state()
|
|
{
|
|
m_state = m_saved_states.take_last();
|
|
}
|
|
|
|
}
|