/* * Copyright (c) 2020, Andreas Kling * Copyright (c) 2022, kleines Filmröllchen * Copyright (c) 2022, Idan Horowitz * * SPDX-License-Identifier: BSD-2-Clause */ #include "Parser.h" #include "AST.h" #include "Lexer.h" #include #include #include #include #include #include namespace GUI::GML { static ErrorOr> parse_gml_object(Queue& tokens) { auto object = TRY(try_make_ref_counted()); auto peek = [&] { if (tokens.is_empty()) return Token::Type::Unknown; return tokens.head().m_type; }; while (peek() == Token::Type::Comment) TRY(object->add_property_child(TRY(Node::from_token(tokens.dequeue())))); if (peek() != Token::Type::ClassMarker) return Error::from_string_literal("Expected class marker"sv); tokens.dequeue(); if (peek() != Token::Type::ClassName) return Error::from_string_literal("Expected class name"sv); auto class_name = tokens.dequeue(); object->set_name(class_name.m_view); if (peek() != Token::Type::LeftCurly) return Error::from_string_literal("Expected {{"sv); tokens.dequeue(); NonnullRefPtrVector pending_comments; for (;;) { if (peek() == Token::Type::RightCurly) { // End of object break; } if (peek() == Token::Type::ClassMarker) { // It's a child object. while (!pending_comments.is_empty()) TRY(object->add_sub_object_child(pending_comments.take_first())); TRY(object->add_sub_object_child(TRY(parse_gml_object(tokens)))); } else if (peek() == Token::Type::Identifier) { // It's a property. while (!pending_comments.is_empty()) TRY(object->add_property_child(pending_comments.take_first())); auto property_name = tokens.dequeue(); if (property_name.m_view.is_empty()) return Error::from_string_literal("Expected non-empty property name"sv); if (peek() != Token::Type::Colon) return Error::from_string_literal("Expected ':'"sv); tokens.dequeue(); RefPtr value; if (peek() == Token::Type::ClassMarker) value = TRY(parse_gml_object(tokens)); else if (peek() == Token::Type::JsonValue) value = TRY(try_make_ref_counted(TRY(JsonValueNode::from_string(tokens.dequeue().m_view)))); auto property = TRY(try_make_ref_counted(property_name.m_view, value.release_nonnull())); TRY(object->add_property_child(property)); } else if (peek() == Token::Type::Comment) { pending_comments.append(TRY(Node::from_token(tokens.dequeue()))); } else { return Error::from_string_literal("Expected child, property, comment, or }}"sv); } } // Insert any left-over comments as sub object children, as these will be serialized last while (!pending_comments.is_empty()) TRY(object->add_sub_object_child(pending_comments.take_first())); if (peek() != Token::Type::RightCurly) return Error::from_string_literal("Expected }}"sv); tokens.dequeue(); return object; } ErrorOr> parse_gml(StringView string) { auto lexer = Lexer(string); Queue tokens; for (auto& token : lexer.lex()) tokens.enqueue(token); auto file = TRY(try_make_ref_counted()); auto peek = [&] { if (tokens.is_empty()) return Token::Type::Unknown; return tokens.head().m_type; }; while (peek() == Token::Type::Comment) TRY(file->add_child(TRY(Node::from_token(tokens.dequeue())))); TRY(file->add_child(TRY(parse_gml_object(tokens)))); while (!tokens.is_empty()) TRY(file->add_child(TRY(Node::from_token(tokens.dequeue())))); return file; } }