ArkScript
A small, fast, functional and scripting language for video games
Parser.hpp
Go to the documentation of this file.
1/**
2 * @file Parser.hpp
3 * @author Alexandre Plateau ([email protected])
4 * @brief Parse ArkScript code, but do not handle any import declarations
5 * @date 2024-05-12
6 *
7 * @copyright Copyright (c) 2024-2025
8 *
9 */
10
11#ifndef COMPILER_AST_PARSER_HPP
12#define COMPILER_AST_PARSER_HPP
13
17#include <Ark/Logger.hpp>
18#include <Ark/Utils.hpp>
19#include <Ark/Platform.hpp>
20
21#include <string>
22#include <optional>
23#include <vector>
24
25#include <utf8.hpp>
26
27namespace Ark::internal
28{
29 class ARK_API Parser final : public BaseParser
30 {
31 public:
32 /**
33 * @brief Constructs a new Parser object
34 * @param debug debug level
35 * @param interpret interpret escape codes in strings
36 */
37 explicit Parser(unsigned debug, bool interpret = true);
38
39 /**
40 * @brief Parse the given code
41 * @param filename can be left empty, used for error generation
42 * @param code content of the file
43 */
44 void process(const std::string& filename, const std::string& code);
45
46 /**
47 *
48 * @return const Node& resulting AST after processing the given code
49 */
50 [[nodiscard]] const Node& ast() const noexcept;
51
52 /**
53 *
54 * @return const std::vector<Import>& list of imports detected by the parser
55 */
56 [[nodiscard]] const std::vector<Import>& imports() const;
57
58 private:
59 bool m_interpret; ///< interpret escape codes in strings
62 std::vector<Import> m_imports;
63 unsigned m_allow_macro_behavior; ///< Toggled on when inside a macro definition, off afterward
64 std::size_t m_nested_nodes; ///< Nested node counter
65 std::vector<std::function<std::optional<Node>()>> m_parsers;
66
67 /**
68 * @brief Update a node given a file position
69 * @param node node to update
70 * @param cursor the node position in file
71 * @return Node& the modified node
72 */
73 Node& setNodePosAndFilename(Node& node, const std::optional<FilePosition>& cursor = std::nullopt) const;
74
75 std::optional<Node> node();
76 std::optional<Node> letMutSet();
77 std::optional<Node> del();
78 std::optional<Node> condition();
79 std::optional<Node> loop();
80 std::optional<Node> import_();
81 std::optional<Node> block();
82 std::optional<Node> functionArgs();
83 std::optional<Node> function();
84 std::optional<Node> macroCondition();
85 std::optional<Node> macroArgs();
86 std::optional<Node> macro();
87 std::optional<Node> functionCall();
88 std::optional<Node> list();
89
90 std::optional<Node> number()
91 {
92 auto pos = getCount();
93
94 std::string res;
95 if (signedNumber(&res))
96 {
97 double output;
98 if (Utils::isDouble(res, &output))
99 return std::optional<Node>(output);
100 backtrack(pos);
101 error("Is not a valid number", res);
102 }
103 return std::nullopt;
104 }
105
106 std::optional<Node> string()
107 {
108 std::string res;
109 if (accept(IsChar('"')))
110 {
111 while (true)
112 {
113 if (accept(IsChar('\\')))
114 {
115 if (!m_interpret)
116 res += '\\';
117
118 if (accept(IsChar('"')))
119 res += '"';
120 else if (accept(IsChar('\\')))
121 res += '\\';
122 else if (accept(IsChar('n')))
123 res += m_interpret ? '\n' : 'n';
124 else if (accept(IsChar('t')))
125 res += m_interpret ? '\t' : 't';
126 else if (accept(IsChar('v')))
127 res += m_interpret ? '\v' : 'v';
128 else if (accept(IsChar('r')))
129 res += m_interpret ? '\r' : 'r';
130 else if (accept(IsChar('a')))
131 res += m_interpret ? '\a' : 'a';
132 else if (accept(IsChar('b')))
133 res += m_interpret ? '\b' : 'b';
134 else if (accept(IsChar('f')))
135 res += m_interpret ? '\f' : 'f';
136 else if (accept(IsChar('u')))
137 {
138 std::string seq;
139 if (hexNumber(4, &seq))
140 {
141 if (m_interpret)
142 {
143 char utf8_str[5];
144 utf8::decode(seq.c_str(), utf8_str);
145 if (*utf8_str == '\0')
146 error("Invalid escape sequence", "\\u" + seq);
147 res += utf8_str;
148 }
149 else
150 res += "u" + seq;
151 }
152 else
153 error("Invalid escape sequence", "\\u");
154 }
155 else if (accept(IsChar('U')))
156 {
157 std::string seq;
158 if (hexNumber(8, &seq))
159 {
160 if (m_interpret)
161 {
162 std::size_t begin = 0;
163 for (; seq[begin] == '0'; ++begin)
164 ;
165 char utf8_str[5];
166 utf8::decode(seq.c_str() + begin, utf8_str);
167 if (*utf8_str == '\0')
168 error("Invalid escape sequence", "\\U" + seq);
169 res += utf8_str;
170 }
171 else
172 res += "U" + seq;
173 }
174 else
175 error("Invalid escape sequence", "\\U");
176 }
177 else
178 {
179 backtrack(getCount() - 1);
180 error("Unknown escape sequence", "\\");
181 }
182 }
183 else
184 accept(IsNot(IsEither(IsChar('\\'), IsChar('"'))), &res);
185
186 if (accept(IsChar('"')))
187 break;
188 if (isEOF())
189 expectSuffixOrError('"', "after string");
190 }
191
192 return { Node(NodeType::String, res) };
193 }
194 return std::nullopt;
195 }
196
197 std::optional<Node> field()
198 {
199 std::string sym;
200 if (!name(&sym))
201 return std::nullopt;
202
203 std::optional<Node> leaf { Node(NodeType::Field) };
204 setNodePosAndFilename(leaf.value());
205 leaf->push_back(Node(NodeType::Symbol, sym));
206
207 while (true)
208 {
209 if (leaf->list().size() == 1 && !accept(IsChar('.'))) // Symbol:abc
210 return std::nullopt;
211
212 if (leaf->list().size() > 1 && !accept(IsChar('.')))
213 break;
214 std::string res;
215 if (!name(&res))
216 errorWithNextToken("Expected a field name: <symbol>.<field>");
217 leaf->push_back(Node(NodeType::Symbol, res));
218 }
219
220 return leaf;
221 }
222
223 std::optional<Node> symbol()
224 {
225 std::string res;
226 if (!name(&res))
227 return std::nullopt;
228 return { Node(NodeType::Symbol, res) };
229 }
230
231 std::optional<Node> spread()
232 {
233 std::string res;
234 if (sequence("..."))
235 {
236 if (!name(&res))
237 errorWithNextToken("Expected a name for the variadic");
238 return { Node(NodeType::Spread, res) };
239 }
240 return std::nullopt;
241 }
242
243 std::optional<Node> nil()
244 {
245 if (!accept(IsChar('(')))
246 return std::nullopt;
247
248 std::string comment;
249 newlineOrComment(&comment);
250 if (!accept(IsChar(')')))
251 return std::nullopt;
252
253 if (m_interpret)
254 return { Node(NodeType::Symbol, "nil").attachNearestCommentBefore(comment) };
255 return { Node(NodeType::List).attachNearestCommentBefore(comment) };
256 }
257
258 /**
259 * @brief Try to parse an atom (number, string, spread, field, symbol, nil)
260 * @return std::optional<Node> std::nullopt if no atom could be parsed
261 */
262 std::optional<Node> atom();
263
264 /**
265 * @brief Try to parse an atom, if any, match its type against the given list
266 * @param types autorized types
267 * @return std::optional<Node> std::nullopt if the parsed atom didn't match the given types
268 */
269 std::optional<Node> anyAtomOf(std::initializer_list<NodeType> types);
270
271 /**
272 * @brief Try to parse an atom first, if it fails try to parse a node
273 * @return std::optional<Node> std::nullopt if no atom or node could be parsed
274 */
275 std::optional<Node> nodeOrValue();
276
277 /**
278 * @brief Try to parse using a given parser, prefixing and suffixing it with (...), handling comments around the parsed node
279 * @param parser parser method returning a std::optional<Node>
280 * @param name construction name, eg "let", "condition"
281 * @return std::optional<Node> std::nullopt if the parser didn't match
282 */
283 std::optional<Node> wrapped(std::optional<Node> (Parser::*parser)(), const std::string& name);
284 };
285}
286
287#endif
Lots of utilities about string, filesystem and more.
Internal logger.
#define ARK_API
Definition Module.hpp:28
AST node used by the parser, optimizer and compiler.
ArkScript configuration macros.
A node of an Abstract Syntax Tree for ArkScript.
Definition Node.hpp:30
Node & attachNearestCommentBefore(const std::string &comment)
Set the comment field with the nearest comment before this node.
Definition Node.cpp:119
std::vector< std::function< std::optional< Node >()> > m_parsers
Definition Parser.hpp:65
bool m_interpret
interpret escape codes in strings
Definition Parser.hpp:59
std::optional< Node > nil()
Definition Parser.hpp:243
std::optional< Node > symbol()
Definition Parser.hpp:223
std::optional< Node > number()
Definition Parser.hpp:90
std::optional< Node > string()
Definition Parser.hpp:106
std::optional< Node > spread()
Definition Parser.hpp:231
std::optional< Node > field()
Definition Parser.hpp:197
unsigned m_allow_macro_behavior
Toggled on when inside a macro definition, off afterward.
Definition Parser.hpp:63
std::vector< Import > m_imports
Definition Parser.hpp:62
std::size_t m_nested_nodes
Nested node counter.
Definition Parser.hpp:64
void decode(const char *input, char *dest)
Convert hex string to utf8 string.
Definition utf8.hpp:67