mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 16:32:45 +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();
 | |
| }
 | |
| 
 | |
| }
 | 
