mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 11:22:45 +00:00 
			
		
		
		
	 8a48246ed1
			
		
	
	
		8a48246ed1
		
	
	
	
	
		
			
			This class had slightly confusing semantics and the added weirdness doesn't seem worth it just so we can say "." instead of "->" when iterating over a vector of NNRPs. This patch replaces NonnullRefPtrVector<T> with Vector<NNRP<T>>.
		
			
				
	
	
		
			134 lines
		
	
	
	
		
			4.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			134 lines
		
	
	
	
		
			4.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
 | |
|  * Copyright (c) 2022, kleines Filmröllchen <filmroellchen@serenityos.org>
 | |
|  * Copyright (c) 2022, Idan Horowitz <idan.horowitz@serenityos.org>
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-2-Clause
 | |
|  */
 | |
| 
 | |
| #include "Parser.h"
 | |
| #include "AST.h"
 | |
| #include "Lexer.h"
 | |
| #include <AK/Error.h>
 | |
| #include <AK/GenericLexer.h>
 | |
| #include <AK/JsonObject.h>
 | |
| #include <AK/JsonValue.h>
 | |
| #include <AK/Queue.h>
 | |
| #include <AK/RefPtr.h>
 | |
| 
 | |
| namespace GUI::GML {
 | |
| 
 | |
| static ErrorOr<NonnullRefPtr<Object>> parse_gml_object(Queue<Token>& tokens)
 | |
| {
 | |
|     auto object = TRY(try_make_ref_counted<Object>());
 | |
| 
 | |
|     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<Comment>(tokens.dequeue()))));
 | |
| 
 | |
|     if (peek() != Token::Type::ClassMarker)
 | |
|         return Error::from_string_literal("Expected class marker");
 | |
| 
 | |
|     tokens.dequeue();
 | |
| 
 | |
|     if (peek() != Token::Type::ClassName)
 | |
|         return Error::from_string_literal("Expected class name");
 | |
| 
 | |
|     auto class_name = tokens.dequeue();
 | |
|     object->set_name(class_name.m_view);
 | |
| 
 | |
|     if (peek() == Token::Type::LeftCurly) {
 | |
| 
 | |
|         tokens.dequeue();
 | |
| 
 | |
|         Vector<NonnullRefPtr<Comment>> 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");
 | |
| 
 | |
|                 if (peek() != Token::Type::Colon)
 | |
|                     return Error::from_string_literal("Expected ':'");
 | |
| 
 | |
|                 tokens.dequeue();
 | |
| 
 | |
|                 RefPtr<ValueNode> value;
 | |
|                 if (peek() == Token::Type::ClassMarker)
 | |
|                     value = TRY(parse_gml_object(tokens));
 | |
|                 else if (peek() == Token::Type::JsonValue)
 | |
|                     value = TRY(try_make_ref_counted<JsonValueNode>(TRY(JsonValueNode::from_string(tokens.dequeue().m_view))));
 | |
| 
 | |
|                 auto property = TRY(try_make_ref_counted<KeyValuePair const>(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<Comment>(tokens.dequeue())));
 | |
|             } else {
 | |
|                 return Error::from_string_literal("Expected child, property, comment, or }}");
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // 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 }}");
 | |
| 
 | |
|         tokens.dequeue();
 | |
|     }
 | |
| 
 | |
|     return object;
 | |
| }
 | |
| 
 | |
| ErrorOr<NonnullRefPtr<GMLFile>> parse_gml(StringView string)
 | |
| {
 | |
|     auto lexer = Lexer(string);
 | |
| 
 | |
|     Queue<Token> tokens;
 | |
|     for (auto& token : lexer.lex())
 | |
|         tokens.enqueue(token);
 | |
| 
 | |
|     auto file = TRY(try_make_ref_counted<GMLFile>());
 | |
| 
 | |
|     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<Comment>(tokens.dequeue()))));
 | |
| 
 | |
|     TRY(file->add_child(TRY(parse_gml_object(tokens))));
 | |
| 
 | |
|     while (!tokens.is_empty())
 | |
|         TRY(file->add_child(TRY(Node::from_token<Comment>(tokens.dequeue()))));
 | |
| 
 | |
|     return file;
 | |
| }
 | |
| 
 | |
| }
 |