/* * Copyright (c) 2020, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include namespace GUI { static Optional parse_core_object(Queue& tokens) { JsonObject object; JsonArray children; auto peek = [&] { if (tokens.is_empty()) return GMLToken::Type::Unknown; return tokens.head().m_type; }; while (peek() == GMLToken::Type::Comment) tokens.dequeue(); if (peek() != GMLToken::Type::ClassMarker) { dbgln("Expected class marker"); return {}; } tokens.dequeue(); if (peek() != GMLToken::Type::ClassName) { dbgln("Expected class name"); return {}; } auto class_name = tokens.dequeue(); object.set("class", JsonValue(class_name.m_view)); if (peek() != GMLToken::Type::LeftCurly) { // Empty object return object; } tokens.dequeue(); for (;;) { if (peek() == GMLToken::Type::RightCurly) { // End of object break; } if (peek() == GMLToken::Type::ClassMarker) { // It's a child object. auto value = parse_core_object(tokens); if (!value.has_value()) { dbgln("Parsing child object failed"); return {}; } if (!value.value().is_object()) { dbgln("Expected child to be Core::Object"); return {}; } children.append(value.release_value()); } else if (peek() == GMLToken::Type::Identifier) { // It's a property. auto property_name = tokens.dequeue(); if (property_name.m_view.is_empty()) { dbgln("Expected non-empty property name"); return {}; } if (peek() != GMLToken::Type::Colon) { dbgln("Expected ':'"); return {}; } tokens.dequeue(); JsonValue value; if (peek() == GMLToken::Type::ClassMarker) { auto parsed_value = parse_core_object(tokens); if (!parsed_value.has_value()) return {}; if (!parsed_value.value().is_object()) { dbgln("Expected property to be Core::Object"); return {}; } value = parsed_value.release_value(); } else if (peek() == GMLToken::Type::JsonValue) { auto value_string = tokens.dequeue(); auto parsed_value = JsonValue::from_string(value_string.m_view); if (!parsed_value.has_value()) { dbgln("Expected property to be JSON value"); return {}; } value = parsed_value.release_value(); } object.set(property_name.m_view, move(value)); } else if (peek() == GMLToken::Type::Comment) { tokens.dequeue(); } else { dbgln("Expected child, property, comment, or }}"); return {}; } } if (peek() != GMLToken::Type::RightCurly) { dbgln("Expected }}"); return {}; } tokens.dequeue(); if (!children.is_empty()) object.set("children", move(children)); return object; } JsonValue parse_gml(const StringView& string) { auto lexer = GMLLexer(string); Queue tokens; for (auto& token : lexer.lex()) tokens.enqueue(token); auto root = parse_core_object(tokens); if (!root.has_value()) return JsonValue(); return root.release_value(); } }