AST.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /*
  2. * Copyright (c) 2022, kleines Filmröllchen <filmroellchen@serenityos.org>.
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #pragma once
  7. #include <AK/Concepts.h>
  8. #include <AK/DeprecatedString.h>
  9. #include <AK/Error.h>
  10. #include <AK/Forward.h>
  11. #include <AK/HashMap.h>
  12. #include <AK/JsonArray.h>
  13. #include <AK/JsonValue.h>
  14. #include <AK/NonnullRefPtr.h>
  15. #include <AK/RefCounted.h>
  16. #include <AK/RefPtr.h>
  17. #include <AK/StringBuilder.h>
  18. #include <AK/TypeCasts.h>
  19. #include <LibGUI/GML/Lexer.h>
  20. namespace GUI::GML {
  21. class Comment;
  22. class JsonValueNode;
  23. // Base of the GML Abstract Syntax Tree (AST).
  24. class Node : public RefCounted<Node> {
  25. public:
  26. virtual ~Node() = default;
  27. template<typename NodeT>
  28. requires(IsBaseOf<Node, NodeT>) static ErrorOr<NonnullRefPtr<NodeT>> from_token(Token token)
  29. {
  30. return try_make_ref_counted<NodeT>(token.m_view);
  31. }
  32. DeprecatedString to_deprecated_string() const
  33. {
  34. StringBuilder builder;
  35. format(builder, 0, false);
  36. return builder.to_deprecated_string();
  37. }
  38. // Format this AST node with the builder at the given indentation level.
  39. // is_inline controls whether we are starting on a new line.
  40. virtual void format(StringBuilder& builder, size_t indentation, bool is_inline) const = 0;
  41. // FIXME: We can't change the kind of indentation right now.
  42. static void indent(StringBuilder& builder, size_t indentation)
  43. {
  44. for (size_t i = 0; i < indentation; ++i)
  45. builder.append(" "sv);
  46. }
  47. };
  48. // AST nodes that actually hold data and can be in a KeyValuePair.
  49. class ValueNode
  50. : public Node {
  51. public:
  52. virtual ~ValueNode() = default;
  53. };
  54. // Single line comments with //.
  55. class Comment : public Node {
  56. public:
  57. Comment(DeprecatedString text)
  58. : m_text(move(text))
  59. {
  60. }
  61. virtual void format(StringBuilder& builder, size_t indentation, bool is_inline) const override
  62. {
  63. if (is_inline) {
  64. builder.append(m_text);
  65. } else {
  66. indent(builder, indentation);
  67. builder.append(m_text);
  68. }
  69. builder.append('\n');
  70. }
  71. virtual ~Comment() override = default;
  72. private:
  73. DeprecatedString m_text {};
  74. };
  75. // Any JSON-like key: value pair.
  76. class KeyValuePair : public Node {
  77. public:
  78. KeyValuePair(DeprecatedString key, NonnullRefPtr<ValueNode> value)
  79. : m_key(move(key))
  80. , m_value(move(value))
  81. {
  82. }
  83. virtual ~KeyValuePair() override = default;
  84. virtual void format(StringBuilder& builder, size_t indentation, bool is_inline) const override
  85. {
  86. if (!is_inline)
  87. indent(builder, indentation);
  88. builder.appendff("{}: ", m_key);
  89. m_value->format(builder, indentation, true);
  90. if (!is_inline)
  91. builder.append('\n');
  92. }
  93. DeprecatedString key() const { return m_key; }
  94. NonnullRefPtr<ValueNode> value() const { return m_value; }
  95. private:
  96. DeprecatedString m_key;
  97. NonnullRefPtr<ValueNode> m_value;
  98. };
  99. // Just a mixin so that we can use JSON values in the AST
  100. // FIXME: Use a specialized value type for all the possible GML property values. Right now that's all possible JSON values (?)
  101. class JsonValueNode : public ValueNode
  102. , public JsonValue {
  103. public:
  104. JsonValueNode(JsonValue const& value)
  105. : JsonValue(value)
  106. {
  107. }
  108. virtual void format(StringBuilder& builder, size_t indentation, bool is_inline) const override
  109. {
  110. if (!is_inline)
  111. indent(builder, indentation);
  112. if (is_array()) {
  113. // custom array serialization as AK's doesn't pretty-print
  114. // objects and arrays (we only care about arrays (for now))
  115. builder.append('[');
  116. auto first = true;
  117. as_array().for_each([&](auto& value) {
  118. if (!first)
  119. builder.append(", "sv);
  120. first = false;
  121. value.serialize(builder);
  122. });
  123. builder.append(']');
  124. } else {
  125. serialize(builder);
  126. }
  127. if (!is_inline)
  128. builder.append('\n');
  129. }
  130. };
  131. // GML class declaration, starting with '@'
  132. class Object : public ValueNode {
  133. public:
  134. Object() = default;
  135. Object(DeprecatedString name, Vector<NonnullRefPtr<Node const>> properties, Vector<NonnullRefPtr<Node const>> sub_objects)
  136. : m_properties(move(properties))
  137. , m_sub_objects(move(sub_objects))
  138. , m_name(move(name))
  139. {
  140. }
  141. virtual ~Object() override = default;
  142. StringView name() const { return m_name; }
  143. void set_name(DeprecatedString name) { m_name = move(name); }
  144. ErrorOr<void> add_sub_object_child(NonnullRefPtr<Node const> child)
  145. {
  146. VERIFY(is<Object>(child.ptr()) || is<Comment>(child.ptr()));
  147. return m_sub_objects.try_append(move(child));
  148. }
  149. ErrorOr<void> add_property_child(NonnullRefPtr<Node const> child)
  150. {
  151. VERIFY(is<KeyValuePair>(child.ptr()) || is<Comment>(child.ptr()));
  152. return m_properties.try_append(move(child));
  153. }
  154. // Does not return key-value pair `layout: ...`!
  155. template<typename Callback>
  156. void for_each_property(Callback callback) const
  157. {
  158. for (auto const& child : m_properties) {
  159. if (is<KeyValuePair>(child)) {
  160. auto const& property = static_cast<KeyValuePair const&>(*child);
  161. if (property.key() != "layout" && is<JsonValueNode>(property.value().ptr()))
  162. callback(property.key(), static_ptr_cast<JsonValueNode>(property.value()));
  163. }
  164. }
  165. }
  166. template<typename Callback>
  167. void for_each_child_object(Callback callback) const
  168. {
  169. for (NonnullRefPtr<Node const> child : m_sub_objects) {
  170. // doesn't capture layout as intended, as that's behind a kv-pair
  171. if (is<Object>(child.ptr())) {
  172. auto object = static_ptr_cast<Object const>(child);
  173. callback(object);
  174. }
  175. }
  176. }
  177. template<FallibleFunction<NonnullRefPtr<Object>> Callback>
  178. ErrorOr<void> try_for_each_child_object(Callback callback) const
  179. {
  180. for (auto const& child : m_sub_objects) {
  181. // doesn't capture layout as intended, as that's behind a kv-pair
  182. if (is<Object>(child)) {
  183. TRY(callback(static_cast<Object const&>(*child)));
  184. }
  185. }
  186. return {};
  187. }
  188. RefPtr<Object const> layout_object() const
  189. {
  190. for (auto const& child : m_properties) {
  191. if (is<KeyValuePair>(child)) {
  192. auto const& property = static_cast<KeyValuePair const&>(*child);
  193. if (property.key() == "layout") {
  194. VERIFY(is<Object>(property.value().ptr()));
  195. return static_cast<Object const&>(*property.value());
  196. }
  197. }
  198. }
  199. return nullptr;
  200. }
  201. RefPtr<ValueNode const> get_property(StringView property_name) const
  202. {
  203. for (auto const& child : m_properties) {
  204. if (is<KeyValuePair>(child)) {
  205. auto const& property = static_cast<KeyValuePair const&>(*child);
  206. if (property.key() == property_name)
  207. return property.value();
  208. }
  209. }
  210. return nullptr;
  211. }
  212. virtual void format(StringBuilder& builder, size_t indentation, bool is_inline) const override
  213. {
  214. if (!is_inline)
  215. indent(builder, indentation);
  216. builder.append('@');
  217. builder.append(m_name);
  218. builder.append(" {"sv);
  219. if (!m_properties.is_empty() || !m_sub_objects.is_empty()) {
  220. builder.append('\n');
  221. for (auto const& property : m_properties)
  222. property->format(builder, indentation + 1, false);
  223. if (!m_properties.is_empty() && !m_sub_objects.is_empty())
  224. builder.append('\n');
  225. // This loop is necessary as we need to know what the last child is.
  226. for (size_t i = 0; i < m_sub_objects.size(); ++i) {
  227. auto const& child = m_sub_objects[i];
  228. child->format(builder, indentation + 1, false);
  229. if (is<Object>(child) && i != m_sub_objects.size() - 1)
  230. builder.append('\n');
  231. }
  232. indent(builder, indentation);
  233. }
  234. builder.append('}');
  235. if (!is_inline)
  236. builder.append('\n');
  237. }
  238. private:
  239. // Properties and comments
  240. Vector<NonnullRefPtr<Node const>> m_properties;
  241. // Sub objects and comments
  242. Vector<NonnullRefPtr<Node const>> m_sub_objects;
  243. DeprecatedString m_name {};
  244. };
  245. class GMLFile : public Node {
  246. public:
  247. virtual ~GMLFile() override = default;
  248. ErrorOr<void> add_child(NonnullRefPtr<Node const> child)
  249. {
  250. if (!has_main_class()) {
  251. if (is<Comment>(child.ptr())) {
  252. return m_leading_comments.try_append(*static_ptr_cast<Comment const>(child));
  253. }
  254. if (is<Object>(child.ptr())) {
  255. m_main_class = static_ptr_cast<Object const>(child);
  256. return {};
  257. }
  258. return Error::from_string_literal("Unexpected data before main class");
  259. }
  260. // After the main class, only comments are allowed.
  261. if (!is<Comment>(child.ptr()))
  262. return Error::from_string_literal("Data not allowed after main class");
  263. return m_trailing_comments.try_append(*static_ptr_cast<Comment const>(child));
  264. }
  265. bool has_main_class() const { return m_main_class != nullptr; }
  266. Vector<NonnullRefPtr<Comment const>> leading_comments() const { return m_leading_comments; }
  267. Object const& main_class() const
  268. {
  269. VERIFY(!m_main_class.is_null());
  270. return *m_main_class.ptr();
  271. }
  272. Vector<NonnullRefPtr<Comment const>> trailing_comments() const { return m_trailing_comments; }
  273. virtual void format(StringBuilder& builder, size_t indentation, [[maybe_unused]] bool is_inline) const override
  274. {
  275. for (auto const& comment : m_leading_comments)
  276. comment->format(builder, indentation, false);
  277. if (!m_leading_comments.is_empty())
  278. builder.append('\n');
  279. m_main_class->format(builder, indentation, false);
  280. if (!m_trailing_comments.is_empty())
  281. builder.append('\n');
  282. for (auto const& comment : m_trailing_comments)
  283. comment->format(builder, indentation, false);
  284. }
  285. private:
  286. Vector<NonnullRefPtr<Comment const>> m_leading_comments;
  287. RefPtr<Object const> m_main_class;
  288. Vector<NonnullRefPtr<Comment const>> m_trailing_comments;
  289. };
  290. }