AST.h 10 KB

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