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/DeprecatedString.h>
  9. #include <AK/Error.h>
  10. #include <AK/Forward.h>
  11. #include <AK/HashMap.h>
  12. #include <AK/IterationDecision.h>
  13. #include <AK/JsonArray.h>
  14. #include <AK/JsonValue.h>
  15. #include <AK/NonnullRefPtr.h>
  16. #include <AK/NonnullRefPtrVector.h>
  17. #include <AK/RefCounted.h>
  18. #include <AK/RefPtr.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. DeprecatedString to_deprecated_string() const
  35. {
  36. StringBuilder builder;
  37. format(builder, 0, false);
  38. return builder.to_deprecated_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(" "sv);
  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(DeprecatedString 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. DeprecatedString m_text {};
  76. };
  77. // Any JSON-like key: value pair.
  78. class KeyValuePair : public Node {
  79. public:
  80. KeyValuePair(DeprecatedString 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. DeprecatedString key() const { return m_key; }
  96. NonnullRefPtr<ValueNode> value() const { return m_value; }
  97. private:
  98. DeprecatedString 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(", "sv);
  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(DeprecatedString 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(DeprecatedString 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. template<FallibleFunction<NonnullRefPtr<Object>> Callback>
  180. ErrorOr<void> try_for_each_child_object(Callback callback)
  181. {
  182. for (NonnullRefPtr<Node> child : m_sub_objects) {
  183. // doesn't capture layout as intended, as that's behind a kv-pair
  184. if (is<Object>(child.ptr())) {
  185. auto object = static_ptr_cast<Object>(child);
  186. TRY(callback(object));
  187. }
  188. }
  189. return {};
  190. }
  191. RefPtr<Object> layout_object() const
  192. {
  193. for (NonnullRefPtr<Node> child : m_properties) {
  194. if (is<KeyValuePair>(child.ptr())) {
  195. auto property = static_ptr_cast<KeyValuePair>(child);
  196. if (property->key() == "layout") {
  197. VERIFY(is<Object>(property->value().ptr()));
  198. return static_ptr_cast<Object>(property->value());
  199. }
  200. }
  201. }
  202. return nullptr;
  203. }
  204. RefPtr<ValueNode> get_property(StringView property_name)
  205. {
  206. for (NonnullRefPtr<Node> child : m_properties) {
  207. if (is<KeyValuePair>(child.ptr())) {
  208. auto property = static_ptr_cast<KeyValuePair>(child);
  209. if (property->key() == property_name)
  210. return property->value();
  211. }
  212. }
  213. return nullptr;
  214. }
  215. virtual void format(StringBuilder& builder, size_t indentation, bool is_inline) const override
  216. {
  217. if (!is_inline)
  218. indent(builder, indentation);
  219. builder.append('@');
  220. builder.append(m_name);
  221. builder.append(" {"sv);
  222. if (!m_properties.is_empty() || !m_sub_objects.is_empty()) {
  223. builder.append('\n');
  224. for (auto const& property : m_properties)
  225. property.format(builder, indentation + 1, false);
  226. if (!m_properties.is_empty() && !m_sub_objects.is_empty())
  227. builder.append('\n');
  228. // This loop is necessary as we need to know what the last child is.
  229. for (size_t i = 0; i < m_sub_objects.size(); ++i) {
  230. auto const& child = m_sub_objects[i];
  231. child.format(builder, indentation + 1, false);
  232. if (is<Object>(child) && i != m_sub_objects.size() - 1)
  233. builder.append('\n');
  234. }
  235. indent(builder, indentation);
  236. }
  237. builder.append('}');
  238. if (!is_inline)
  239. builder.append('\n');
  240. }
  241. private:
  242. // Properties and comments
  243. NonnullRefPtrVector<Node> m_properties;
  244. // Sub objects and comments
  245. NonnullRefPtrVector<Node> m_sub_objects;
  246. DeprecatedString m_name {};
  247. };
  248. class GMLFile : public Node {
  249. public:
  250. virtual ~GMLFile() override = default;
  251. ErrorOr<void> add_child(NonnullRefPtr<Node> child)
  252. {
  253. if (!has_main_class()) {
  254. if (is<Comment>(child.ptr())) {
  255. return m_leading_comments.try_append(*static_ptr_cast<Comment>(child));
  256. }
  257. if (is<Object>(child.ptr())) {
  258. m_main_class = static_ptr_cast<Object>(child);
  259. return {};
  260. }
  261. return Error::from_string_literal("Unexpected data before main class");
  262. }
  263. // After the main class, only comments are allowed.
  264. if (!is<Comment>(child.ptr()))
  265. return Error::from_string_literal("Data not allowed after main class");
  266. return m_trailing_comments.try_append(*static_ptr_cast<Comment>(child));
  267. }
  268. bool has_main_class() const { return m_main_class != nullptr; }
  269. NonnullRefPtrVector<Comment> leading_comments() const { return m_leading_comments; }
  270. Object& main_class()
  271. {
  272. VERIFY(!m_main_class.is_null());
  273. return *m_main_class.ptr();
  274. }
  275. NonnullRefPtrVector<Comment> trailing_comments() const { return m_trailing_comments; }
  276. virtual void format(StringBuilder& builder, size_t indentation, [[maybe_unused]] bool is_inline) const override
  277. {
  278. for (auto const& comment : m_leading_comments)
  279. comment.format(builder, indentation, false);
  280. if (!m_leading_comments.is_empty())
  281. builder.append('\n');
  282. m_main_class->format(builder, indentation, false);
  283. if (!m_trailing_comments.is_empty())
  284. builder.append('\n');
  285. for (auto const& comment : m_trailing_comments)
  286. comment.format(builder, indentation, false);
  287. }
  288. private:
  289. NonnullRefPtrVector<Comment> m_leading_comments;
  290. RefPtr<Object> m_main_class;
  291. NonnullRefPtrVector<Comment> m_trailing_comments;
  292. };
  293. }