AST.h 10 KB

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