/* * Copyright (c) 2021, Itamar S. * Copyright (c) 2023, Volodymyr V. * * SPDX-License-Identifier: BSD-2-Clause */ #include "Parser.h" #include "AST.h" #include namespace GLSL { Parser::Parser(Vector tokens, String const& filename) : m_filename(move(filename)) , m_tokens(move(tokens)) { } ErrorOr> 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> 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(); } ErrorOr 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 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 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 Parser::match_block_statement() { return peek().type() == Token::Type::LeftCurly; } ErrorOr 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 Parser::match_name() { auto type = peek().type(); return type == Token::Type::Identifier || type == Token::Type::KnownType; } ErrorOr Parser::match_string_literal() { return match(Token::Type::DoubleQuotedString) || match(Token::Type::SingleQuotedString); } ErrorOr Parser::match_numeric_literal() { return match(Token::Type::Float) || match(Token::Type::Integer); } ErrorOr 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 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>> Parser::parse_declarations_in_translation_unit(ASTNode const& parent) { Vector> 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> 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> 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(parent, position(), position()); } } ErrorOr> Parser::parse_struct_declaration(ASTNode const& parent) { TRY(consume_keyword("struct"sv)); auto decl = create_ast_node(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>> Parser::parse_struct_members(StructDeclaration& parent) { Vector> members; while (!eof() && peek().type() != Token::Type::RightCurly) { auto member = TRY(parse_variable_declaration(parent)); members.append(move(member)); } return members; } ErrorOr> Parser::parse_function_declaration(ASTNode const& parent) { auto func = create_ast_node(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 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>> Parser::parse_parameter_list(ASTNode const& parent) { Vector> parameters; while (peek().type() != Token::Type::RightParen && !eof()) { auto type = TRY(parse_type(parent)); RefPtr name; if (TRY(match_name())) name = TRY(parse_name(parent)); auto param = create_ast_node(parent, type->start(), !name.is_null() ? name->end() : type->end(), name); const_cast(*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> Parser::parse_function_definition(ASTNode const& parent) { auto func = create_ast_node(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> Parser::parse_variable_declaration(ASTNode const& parent, bool expect_semicolon) { auto var = create_ast_node(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 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> Parser::parse_statement(ASTNode const& parent) { bool should_consume_semicolon = true; RefPtr 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(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(parent, position(), position()); } if (should_consume_semicolon) TRY(consume(Token::Type::Semicolon)); return result.release_nonnull(); } ErrorOr> Parser::parse_block_statement(ASTNode const& parent) { auto block_statement = create_ast_node(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> Parser::parse_if_statement(ASTNode const& parent) { auto if_statement = create_ast_node(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> Parser::parse_for_statement(ASTNode const& parent) { auto for_statement = create_ast_node(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> Parser::parse_return_statement(ASTNode const& parent) { auto return_statement = create_ast_node(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 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 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> 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(parent, start_pos, {}); const_cast(*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> 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 lhs = TRY(parse_name(parent)); while (true) { if (match(Token::Type::LeftParen)) { TRY(consume(Token::Type::LeftParen)); auto expr = create_ast_node(parent, lhs->start(), {}); auto args = TRY(parse_function_call_args(expr)); const_cast(*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(parent, lhs->start(), {}); auto rhs = TRY(parse_name(expr)); const_cast(*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(parent, lhs->start(), {}); auto index = TRY(parse_expression(expr)); TRY(consume(Token::Type::RightBracket)); const_cast(*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(parent, lhs->start(), position()); const_cast(*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(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(parent, position(), position()); } ErrorOr>> Parser::parse_function_call_args(ASTNode const& parent) { Vector> 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> 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(parent, token.start(), token.end(), value); } ErrorOr> Parser::parse_numeric_literal(GLSL::ASTNode const& parent) { auto token = TRY(consume()); auto text = token.text(); return create_ast_node(parent, token.start(), token.end(), text); } ErrorOr> Parser::parse_string_literal(ASTNode const& parent) { Optional start_token_index; Optional 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(parent, start_token.start(), end_token.end()); string_literal->set_value(move(text)); return string_literal; } ErrorOr> Parser::parse_name(ASTNode const& parent, bool allow_sized_name) { NonnullRefPtr name_node = create_ast_node(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 sized_name = create_ast_node(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> Parser::parse_type(ASTNode const& parent) { auto type = create_ast_node(parent, position(), {}); Vector 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 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 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 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 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 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 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 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 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 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 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 tokens; for (size_t i = start_token_index.value(); i <= end_node_index.value(); ++i) { tokens.append(m_tokens[i]); } return tokens; } ErrorOr 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 Parser::error(StringView message) { if (!m_saved_states.is_empty()) return {}; if (message.is_null() || message.is_empty()) message = ""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(); } }