ArkScript
A small, fast, functional and scripting language for video games
Parser.cpp
Go to the documentation of this file.
2
3#include <optional>
4#include <algorithm>
5#include <sstream>
6
7#include <Ark/Files.hpp>
8#include <Ark/Exceptions.hpp>
10
11namespace Ark::internal
12{
13 Parser::Parser(unsigned debug, uint16_t options, const std::vector<std::string>& lib_env) noexcept :
14 m_debug(debug),
15 m_libenv(lib_env),
16 m_options(options),
17 m_lexer(debug),
18 m_file(ARK_NO_NAME_FILE)
19 {}
20
21 void Parser::feed(const std::string& code, const std::string& filename)
22 {
23 // not the default value
24 if (filename != ARK_NO_NAME_FILE)
25 {
27 if (m_debug >= 2)
28 std::cout << "New parser: " << m_file << '\n';
29 m_parent_include.push_back(m_file);
30 }
31
32 m_code = code;
33
34 m_lexer.feed(code);
35 // apply syntactic sugar
36 std::vector<Token>& t = m_lexer.tokens();
37 if (t.empty())
38 throw ParseError("empty file");
39 sugar(t);
40
41 // create program
42 std::list<Token> tokens(t.begin(), t.end());
43 m_last_token = tokens.front();
44
45 // accept every nodes in the file
48 m_ast.list().emplace_back(Keyword::Begin);
49 while (!tokens.empty())
50 m_ast.list().push_back(parse(tokens));
51 // include files if needed
53
54 if (m_debug >= 3)
55 std::cout << "(Parser) AST\n"
56 << m_ast << "\n\n";
57 }
58
59 const Node& Parser::ast() const noexcept
60 {
61 return m_ast;
62 }
63
64 const std::vector<std::string>& Parser::getImports() const noexcept
65 {
66 return m_parent_include;
67 }
68
69 void Parser::sugar(std::vector<Token>& tokens) noexcept
70 {
71 std::size_t i = 0;
72 while (true)
73 {
74 std::size_t line = tokens[i].line;
75 std::size_t col = tokens[i].col;
76
77 if (tokens[i].token == "{")
78 {
79 tokens[i] = Token(TokenType::Grouping, "(", line, col);
80 // handle macros
81 if (i > 0 && tokens[i - 1].token != "!")
82 tokens.insert(tokens.begin() + i + 1, Token(TokenType::Keyword, "begin", line, col));
83 else if (i == 0)
84 tokens.insert(tokens.begin() + i + 1, Token(TokenType::Keyword, "begin", line, col));
85 }
86 else if (tokens[i].token == "}" || tokens[i].token == "]")
87 tokens[i] = Token(TokenType::Grouping, ")", line, col);
88 else if (tokens[i].token == "[")
89 {
90 tokens[i] = Token(TokenType::Grouping, "(", line, col);
91 tokens.insert(tokens.begin() + i + 1, Token(TokenType::Identifier, "list", line, col));
92 }
93
94 ++i;
95
96 if (i == tokens.size())
97 break;
98 }
99 }
100
101 // sugar() was called before, so it's safe to assume we only have ( and )
102 Node Parser::parse(std::list<Token>& tokens, bool authorize_capture, bool authorize_field_read, bool in_macro)
103 {
104 using namespace std::string_literals;
105
106 Token token = nextToken(tokens);
107
108 bool previous_token_was_lparen = false;
109
110 // parse block
111 if (token.token == "(")
112 {
113 previous_token_was_lparen = true;
114 // create a list node to host the block
115 Node block = make_node_list(token.line, token.col, m_file);
116
117 // handle sub-blocks
118 if (tokens.front().token == "(")
119 {
120 block.push_back(parse(tokens, false, false, in_macro));
121 previous_token_was_lparen = false;
122 }
123
124 // take next token, we don't want to play with a "("
125 token = nextToken(tokens);
126
127 // return an empty block
128 if (token.token == ")")
129 return block;
130
131 // check for unexpected keywords between expressions
132 if ((token.type == TokenType::Operator ||
133 token.type == TokenType::Identifier ||
134 token.type == TokenType::Number ||
135 token.type == TokenType::String) &&
136 tokens.front().type == TokenType::Keyword)
137 throwParseError("Unexpected keyword `" + tokens.front().token + "' in the middle of an expression", tokens.front());
138
139 // loop until we reach the end of the block
140 do
141 {
142 Node atomized = atom(token);
143 checkForInvalidTokens(atomized, token, previous_token_was_lparen, authorize_capture, authorize_field_read);
144 block.push_back(atomized);
145
146 expect(!tokens.empty(), "expected more tokens after `" + token.token + "'", m_last_token);
147 m_last_token = tokens.front();
148
149 if (token.type == TokenType::Keyword)
150 {
151 if (token.token == "if")
152 parseIf(block, tokens, in_macro);
153 else if (token.token == "let" || token.token == "mut")
154 parseLetMut(block, token, tokens, in_macro);
155 else if (token.token == "set")
156 parseSet(block, token, tokens, in_macro);
157 else if (token.token == "fun")
158 parseFun(block, token, tokens, in_macro);
159 else if (token.token == "while")
160 parseWhile(block, token, tokens, in_macro);
161 else if (token.token == "begin")
162 parseBegin(block, tokens, in_macro);
163 else if (token.token == "import")
164 parseImport(block, tokens);
165 else if (token.token == "quote")
166 parseQuote(block, tokens, in_macro);
167 else if (token.token == "del")
168 parseDel(block, tokens);
169 else
170 throwParseError("unimplemented keyword `" + token.token + "'. If you see this error please report it on GitHub.", token);
171 }
172 else if (token.type == TokenType::Identifier || token.type == TokenType::Operator ||
173 (token.type == TokenType::Capture && authorize_capture) ||
174 (token.type == TokenType::GetField && authorize_field_read) ||
175 (token.type == TokenType::Spread && in_macro))
176 {
177 while (tokens.front().token != ")")
178 block.push_back(parse(tokens, /* authorize_capture */ false, /* authorize_field_read */ true, in_macro));
179 }
180 } while (tokens.front().token != ")");
181
182 // pop the ")"
183 tokens.pop_front();
184 return block;
185 }
186 else if (token.type == TokenType::Shorthand)
187 return parseShorthand(token, tokens, in_macro);
188 // error, we shouldn't have grouping token here
189 else if (token.type == TokenType::Grouping)
190 throwParseError("Found a lonely `" + token.token + "', you most likely have too much parenthesis.", token);
191 else if ((token.type == TokenType::Operator || token.type == TokenType::Identifier) &&
192 std::find(internal::operators.begin(), internal::operators.end(), token.token) != internal::operators.end())
193 throwParseError("Found a free flying operator, which isn't authorized. Operators should always immediatly follow a `('.", token);
194 else if ((token.type == TokenType::Number ||
195 token.type == TokenType::String) &&
196 tokens.front().type == TokenType::Keyword)
197 throwParseError("Unexpected keyword `" + tokens.front().token + "' in the middle of an expression", tokens.front());
198 else if (token.type == TokenType::Keyword &&
199 !previous_token_was_lparen)
200 throwParseError("Unexpected keyword `" + token.token + "' in the middle of an expression", token);
201 return atom(token);
202 }
203
204 void Parser::parseIf(Node& block, std::list<Token>& tokens, bool in_macro)
205 {
206 auto temp = tokens.front();
207 // parse condition
208 if (temp.type == TokenType::Grouping)
209 block.push_back(parse(tokens, false, false, in_macro));
210 else if (temp.type == TokenType::Identifier || temp.type == TokenType::Number ||
211 temp.type == TokenType::String || (in_macro && temp.type == TokenType::Spread))
212 block.push_back(atom(nextToken(tokens)));
213 else
214 throwParseError("found invalid token after keyword `if', expected function call, value or Identifier", temp);
215 // parse 'then'
216 expect(!tokens.empty() && tokens.front().token != ")", "expected a statement after the condition", temp);
217 block.push_back(parse(tokens, false, false, in_macro));
218 // parse 'else', if there is one
219 if (tokens.front().token != ")")
220 {
221 block.push_back(parse(tokens, false, false, in_macro));
222 // error handling if the if is ill-formed
223 expect(tokens.front().token == ")", "if block is ill-formed, got more than the 3 required arguments (condition, then, else)", m_last_token);
224 }
225 }
226
227 void Parser::parseLetMut(Node& block, Token& token, std::list<Token>& tokens, bool in_macro)
228 {
229 auto temp = tokens.front();
230 // parse identifier
231 if (temp.type == TokenType::Identifier)
232 block.push_back(atom(nextToken(tokens)));
233 else if (in_macro)
234 block.push_back(parse(tokens, false, false, in_macro));
235 else
236 throwParseError(std::string("missing identifier to define a ") + (token.token == "let" ? "constant" : "variable") + ", after keyword `" + token.token + "'", temp);
237 expect(!tokens.empty() && tokens.front().token != ")", "expected a value after the identifier", temp);
238 // value
239 while (tokens.front().token != ")")
240 block.push_back(parse(tokens, /* authorize_capture */ false, /* authorize_field_read */ true, in_macro));
241
242 // the block size can exceed 3 only if we have a serie of getfields
243 expect(
244 block.list().size() <= 3 || std::all_of(block.list().begin() + 3, block.list().end(), [](const Node& n) -> bool {
245 return n.nodeType() == NodeType::GetField;
246 }),
247 "too many arguments given to keyword `" + token.token + "', got " + std::to_string(block.list().size() - 1) + ", expected at most 3", m_last_token);
248 }
249
250 void Parser::parseSet(Node& block, Token& token, std::list<Token>& tokens, bool in_macro)
251 {
252 auto temp = tokens.front();
253 // parse identifier
254 if (temp.type == TokenType::Identifier)
255 block.push_back(atom(nextToken(tokens)));
256 else if (in_macro)
257 block.push_back(parse(tokens, false, false, in_macro));
258 else
259 throwParseError("missing identifier to assign a value to, after keyword `set'", temp);
260 expect(!tokens.empty() && tokens.front().token != ")", "expected a value after the identifier", temp);
261 // set can not accept a.b...c as an identifier
262 if (tokens.front().type == TokenType::GetField)
263 throwParseError("found invalid token after keyword `set', expected an identifier, got a closure field reading expression", tokens.front());
264 // value
265 while (tokens.front().token != ")")
266 block.push_back(parse(tokens, /* authorize_capture */ false, /* authorize_field_read */ true, in_macro));
267
268 // the block size can exceed 3 only if we have a serie of getfields
269 expect(
270 block.list().size() <= 3 || std::all_of(block.list().begin() + 3, block.list().end(), [](const Node& n) -> bool {
271 return n.nodeType() == NodeType::GetField;
272 }),
273 "too many arguments given to keyword `" + token.token + "', got " + std::to_string(block.list().size() - 1) + ", expected at most 3", m_last_token);
274 }
275
276 void Parser::parseFun(Node& block, Token& token, std::list<Token>& tokens, bool in_macro)
277 {
278 // parse arguments
279 if (tokens.front().type == TokenType::Grouping || in_macro)
280 block.push_back(parse(tokens, /* authorize_capture */ true, false, in_macro));
281 else
282 throwParseError("found invalid token after keyword `fun', expected a block to define the argument list of the function\nThe block can be empty if it doesn't have arguments: `()'", tokens.front());
283 // parse body
284 if (tokens.front().type == TokenType::Grouping || in_macro)
285 block.push_back(parse(tokens, false, false, in_macro));
286 else
287 throwParseError("the body of a function must be a block, even an empty one `()'", tokens.front());
288 expect(block.list().size() == 3, "got too many arguments after keyword `" + token.token + "', expected an argument list and a body", m_last_token);
289 }
290
291 void Parser::parseWhile(Node& block, Token& token, std::list<Token>& tokens, bool in_macro)
292 {
293 auto temp = tokens.front();
294 // parse condition
295 if (temp.type == TokenType::Grouping)
296 block.push_back(parse(tokens, false, false, in_macro));
297 else if (temp.type == TokenType::Identifier || temp.type == TokenType::Number ||
298 temp.type == TokenType::String)
299 block.push_back(atom(nextToken(tokens)));
300 else
301 throwParseError("found invalid token after keyword `while', expected function call, value or Identifier", temp);
302 expect(!tokens.empty() && tokens.front().token != ")", "expected a body after the condition", temp);
303 // parse 'do'
304 block.push_back(parse(tokens, false, false, in_macro));
305 expect(block.list().size() == 3, "got too many arguments after keyword `" + token.token + "', expected a condition and a body", temp);
306 }
307
308 void Parser::parseBegin(Node& block, std::list<Token>& tokens, bool in_macro)
309 {
310 while (true)
311 {
312 expect(!tokens.empty(), "a `begin' block was opened but never closed\nYou most likely forgot a `}' or `)'", m_last_token);
313 if (tokens.front().token == ")")
314 break;
315 m_last_token = tokens.front();
316
317 block.push_back(parse(tokens, false, false, in_macro));
318 }
319 }
320
321 void Parser::parseImport(Node& block, std::list<Token>& tokens)
322 {
323 if (tokens.front().type == TokenType::String)
324 block.push_back(atom(nextToken(tokens)));
325 else
326 throwParseError("found invalid token after keyword `import', expected String (path to the file or module to import)", tokens.front());
327 expect(tokens.front().token == ")", "got too many arguments after keyword `import', expected a single filename as String", tokens.front());
328 }
329
330 void Parser::parseQuote(Node& block, std::list<Token>& tokens, bool in_macro)
331 {
332 block.push_back(parse(tokens, false, false, in_macro));
333 expect(tokens.front().token == ")", "got too many arguments after keyword `quote', expected a single block or value", tokens.front());
334 }
335
336 void Parser::parseDel(Node& block, std::list<Token>& tokens)
337 {
338 if (tokens.front().type == TokenType::Identifier)
339 block.push_back(atom(nextToken(tokens)));
340 else
341 throwParseError("found invalid token after keyword `del', expected Identifier", tokens.front());
342 expect(tokens.front().token == ")", "got too many arguments after keyword `del', expected a single identifier", tokens.front());
343 }
344
345 Node Parser::parseShorthand(Token& token, std::list<Token>& tokens, bool in_macro)
346 {
347 if (token.token == "'")
348 {
349 // create a list node to host the block
350 Node block = make_node_list(token.line, token.col, m_file);
351
352 block.push_back(make_node(Keyword::Quote, token.line, token.col, m_file));
353 block.push_back(parse(tokens, false, false, in_macro));
354 return block;
355 }
356 else if (token.token == "!")
357 {
358 if (m_debug >= 2)
359 std::cout << "Found a macro at " << token.line << ':' << token.col << " in " << m_file << '\n';
360
361 // macros
362 Node block = make_node(NodeType::Macro, token.line, token.col, m_file);
363
364 Node parsed = parse(tokens, /* authorize_capture */ false, /* authorize_field_read */ false, /* in_macro */ true);
365 if (parsed.nodeType() != NodeType::List || parsed.list().size() < 2 || parsed.list().size() > 4)
366 throwParseError("Macros can only defined using the !{ name value } or !{ name (args) value } syntax", token);
367
368 // append the nodes of the parsed node to the current macro node
369 for (std::size_t i = 0, end = parsed.list().size(); i < end; ++i)
370 block.push_back(parsed.list()[i]);
371 return block;
372 }
373
374 throwParseError("unknown shorthand", token);
375 }
376
377 void Parser::checkForInvalidTokens(Node& atomized, Token& token, bool previous_token_was_lparen, bool authorize_capture, bool authorize_field_read)
378 {
379 if ((atomized.nodeType() == NodeType::String || atomized.nodeType() == NodeType::Number ||
380 atomized.nodeType() == NodeType::List) &&
381 previous_token_was_lparen)
382 {
383 std::stringstream ss;
384 ss << "found invalid token after `(', expected Keyword, Identifier";
385 if (!authorize_capture && !authorize_field_read)
386 ss << " or Operator";
387 else
388 {
389 ss << ", Operator";
390 if (authorize_capture && !authorize_field_read)
391 ss << " or Capture";
392 else if (!authorize_capture && authorize_field_read)
393 ss << " or GetField";
394 else
395 ss << ", Capture or GetField";
396 }
397 throwParseError(ss.str(), token);
398 }
399 }
400
401 Token Parser::nextToken(std::list<Token>& tokens)
402 {
403 expect(!tokens.empty(), "no more token to consume", m_last_token);
404 m_last_token = tokens.front();
405
406 const Token out = std::move(tokens.front());
407 tokens.pop_front();
408 return out;
409 }
410
411 Node Parser::atom(const Token& token)
412 {
413 switch (token.type)
414 {
416 return make_node(std::stod(token.token), token.line, token.col, m_file);
417
419 {
420 std::string str = token.token;
421 // remove the " at the beginning and at the end
422 str.erase(0, 1);
423 str.erase(token.token.size() - 2, 1);
424
425 return make_node(str, token.line, token.col, m_file);
426 }
427
429 {
430 std::optional<Keyword> kw;
431 if (token.token == "if")
432 kw = Keyword::If;
433 else if (token.token == "set")
434 kw = Keyword::Set;
435 else if (token.token == "let")
436 kw = Keyword::Let;
437 else if (token.token == "mut")
438 kw = Keyword::Mut;
439 else if (token.token == "fun")
440 kw = Keyword::Fun;
441 else if (token.token == "while")
442 kw = Keyword::While;
443 else if (token.token == "begin")
444 kw = Keyword::Begin;
445 else if (token.token == "import")
446 kw = Keyword::Import;
447 else if (token.token == "quote")
448 kw = Keyword::Quote;
449 else if (token.token == "del")
450 kw = Keyword::Del;
451
452 if (kw)
453 return make_node(kw.value(), token.line, token.col, m_file);
454 throwParseError("unknown keyword", token);
455 }
456
460 {
462 n.setString(token.type != TokenType::Spread ? token.token : token.token.substr(3));
463 return n;
464 }
465
467 throwParseError("got a shorthand to atomize, and that's not normal. If you see this error please report it on GitHub.", token);
468
469 default:
470 {
471 // assuming it is a TokenType::Identifier, thus a Symbol
472 Node n = make_node(NodeType::Symbol, token.line, token.col, m_file);
473 n.setString(token.token);
474 return n;
475 }
476 }
477 }
478
479 // high cpu cost
480 bool Parser::checkForInclude(Node& n, Node& parent, std::size_t pos)
481 {
482 namespace fs = std::filesystem;
483
484 // if we have a list, we may find an import statement inside
485 if (n.nodeType() == NodeType::List)
486 {
487 if (n.constList().size() == 0)
488 return false;
489
490 const Node& first = n.constList()[0];
491
492 // if we found an import statement, inspect it
493 if (first.nodeType() == NodeType::Keyword && first.keyword() == Keyword::Import)
494 {
495 if (m_debug >= 2)
496 std::cout << "Import found in file: " << m_file << '\n';
497
498 if (n.constList()[1].nodeType() != NodeType::String)
499 throw TypeError("Arguments of import must be of type String");
500
501 // check if we are not loading a plugin
502 if (std::string file = n.constList()[1].string(); fs::path(file).extension().string() == ".ark")
503 {
504 // search for the source file everywhere
505 std::string included_file = seekFile(file);
506
507 // if the file isn't in the include list, then we can include it
508 // this avoids cyclic includes
509 if (std::find(m_parent_include.begin(), m_parent_include.end(), Utils::canonicalRelPath(included_file)) != m_parent_include.end())
510 return true;
511
512 // feed a new parser with our parent includes
514 for (auto const& pi : m_parent_include)
515 p.m_parent_include.push_back(pi); // new parser, we can assume that the parent include list is empty
516 p.m_parent_include.push_back(m_file); // add the current file to avoid importing it again
517 p.feed(Utils::readFile(included_file), included_file);
518
519 // update our list of included files
520 for (auto const& inc : p.m_parent_include)
521 {
522 if (std::find(m_parent_include.begin(), m_parent_include.end(), inc) == m_parent_include.end())
523 m_parent_include.push_back(inc);
524 }
525
526 for (std::size_t j = 1, end = p.ast().constList().size(); j < end; ++j)
527 parent.list().insert(parent.list().begin() + pos + j, p.ast().constList()[j]);
528
529 return true;
530 }
531 }
532
533 // inspect every other node in the list
534 for (std::size_t i = 0; i < n.list().size(); ++i)
535 {
536 if (checkForInclude(n.list()[i], n, i))
537 {
538 n.list().erase(n.list().begin() + i);
539 --i;
540 }
541 }
542 }
543
544 return false;
545 }
546
547 std::string Parser::seekFile(const std::string& file)
548 {
549 const std::string current_dir = Utils::getDirectoryFromPath(m_file) + "/";
550 const std::string path = (current_dir != "/") ? current_dir + file : file;
551
552 if (m_debug >= 2)
553 {
554 std::cout << "path: " << path << " ; file: " << file << " ; libpath: ";
555 for (auto&& lib : m_libenv)
556 std::cout << lib << ":";
557 std::cout << "\nfilename: " << Utils::getFilenameFromPath(file) << '\n';
558 }
559
560 // search in the current directory
561 if (Utils::fileExists(path))
562 return path;
563
564 // search in all folders in environment path
565 for (auto const& p : m_libenv)
566 {
567 // then search in the standard library directory
568 if (std::string f = p + "/std/" + file; Utils::fileExists(f))
569 return f;
570 // then in the standard library root directory
571 else if (std::string f2 = p + "/" + file; Utils::fileExists(f2))
572 return f2;
573 }
574
575 // fallback, we couldn't find the file
576 throw std::runtime_error("While processing file " + m_file + ", couldn't import " + file + ": file not found");
577 }
578
579 void Parser::expect(bool pred, const std::string& message, internal::Token token)
580 {
581 if (!pred)
582 throwParseError(message, token);
583 }
584
585 void Parser::throwParseError(const std::string& message, internal::Token token)
586 {
587 std::stringstream ss;
588 ss << message << "\nGot TokenType::" << internal::tokentype_string[static_cast<unsigned>(token.type)] << "\n";
589
591 ss << "In file " << m_file << "\n";
592 ss << internal::makeTokenBasedErrorCtx(token.token, token.line, token.col, m_code);
593
594 throw ParseError(ss.str());
595 }
596
597 std::ostream& operator<<(std::ostream& os, const Parser& P) noexcept
598 {
599 os << "AST\n";
600 if (P.ast().nodeType() == NodeType::List)
601 {
602 int i = 0;
603 for (const auto& node : P.ast().constList())
604 std::cout << (i++) << ": " << node << '\n';
605 }
606 else
607 os << "Single item\n"
608 << P.m_ast << std::endl;
609 return os;
610 }
611}
#define ARK_NO_NAME_FILE
Definition: Constants.hpp:26
ArkScript homemade exceptions.
Lots of utilities about the filesystem.
Parses a token stream into an AST by using the Ark::Node.
ParseError thrown by the parser.
Definition: Exceptions.hpp:95
A type error triggered when types don't match.
Definition: Exceptions.hpp:29
std::vector< Token > & tokens() noexcept
Return the list of tokens.
Definition: Lexer.cpp:257
void feed(const std::string &code)
Give code to tokenize and create the list of tokens.
Definition: Lexer.cpp:19
A node of an Abstract Syntax Tree for ArkScript.
Definition: Node.hpp:29
NodeType nodeType() const noexcept
Return the node type.
Definition: Node.cpp:126
const std::vector< Node > & constList() const noexcept
Return the list of sub-nodes held by the node.
Definition: Node.cpp:119
Keyword keyword() const noexcept
Return the keyword held by the value (if the node type allows it)
Definition: Node.cpp:102
void setFilename(const std::string &filename) noexcept
Set the original Filename where the node was.
Definition: Node.cpp:159
void push_back(const Node &node) noexcept
Every node has a list as well as a value so we can push_back on all node no matter their type.
Definition: Node.cpp:109
void setString(const std::string &value) noexcept
Set the String object.
Definition: Node.cpp:136
std::vector< Node > & list() noexcept
Return the list of sub-nodes held by the node.
Definition: Node.cpp:114
The parser is responsible of constructing the Abstract Syntax Tree from a token list.
Definition: Parser.hpp:44
void parseImport(Node &, std::list< Token > &)
Definition: Parser.cpp:321
void parseBegin(Node &, std::list< Token > &, bool)
Definition: Parser.cpp:308
std::string m_code
Definition: Parser.hpp:92
void parseLetMut(Node &, Token &, std::list< Token > &, bool)
Definition: Parser.cpp:227
void parseDel(Node &, std::list< Token > &)
Definition: Parser.cpp:336
Node atom(const Token &token)
Convert a token to a node.
Definition: Parser.cpp:411
uint16_t m_options
Definition: Parser.hpp:84
void sugar(std::vector< Token > &tokens) noexcept
Applying syntactic sugar: {...} => (begin...), [...] => (list ...)
Definition: Parser.cpp:69
void expect(bool pred, const std::string &message, Token token)
Throw a parse exception is the given predicated is false.
Definition: Parser.cpp:579
const Node & ast() const noexcept
Return the generated AST.
Definition: Parser.cpp:59
void parseQuote(Node &, std::list< Token > &, bool)
Definition: Parser.cpp:330
void feed(const std::string &code, const std::string &filename=ARK_NO_NAME_FILE)
Give the code to parse.
Definition: Parser.cpp:21
void throwParseError(const std::string &message, Token token)
Throw a parse error related to a token (seek it in the related file and highlight the error)
Definition: Parser.cpp:585
std::string seekFile(const std::string &file)
Seek a file in the lib folder and everywhere.
Definition: Parser.cpp:547
std::vector< std::string > m_libenv
Definition: Parser.hpp:83
std::string m_file
Definition: Parser.hpp:90
void parseFun(Node &, Token &, std::list< Token > &, bool)
Definition: Parser.cpp:276
void parseSet(Node &, Token &, std::list< Token > &, bool)
Definition: Parser.cpp:250
void checkForInvalidTokens(Node &, Token &, bool, bool, bool)
Definition: Parser.cpp:377
void parseIf(Node &, std::list< Token > &, bool)
Definition: Parser.cpp:204
Node parse(std::list< Token > &tokens, bool authorize_capture=false, bool authorize_field_read=false, bool in_macro=false)
Parse a list of tokens recursively.
Definition: Parser.cpp:102
Parser(unsigned debug, uint16_t options, const std::vector< std::string > &lib_env) noexcept
Construct a new Parser object.
Definition: Parser.cpp:13
std::vector< std::string > m_parent_include
Definition: Parser.hpp:94
void parseWhile(Node &, Token &, std::list< Token > &, bool)
Definition: Parser.cpp:291
bool checkForInclude(Node &n, Node &parent, std::size_t pos=0)
Search for all the includes in a given node, in its sub-nodes and replace them by the code of the inc...
Definition: Parser.cpp:480
Node parseShorthand(Token &, std::list< Token > &, bool)
Definition: Parser.cpp:345
Token nextToken(std::list< Token > &tokens)
Get the next token if possible, from a list of tokens.
Definition: Parser.cpp:401
const std::vector< std::string > & getImports() const noexcept
Return the list of files imported by the code given to the parser.
Definition: Parser.cpp:64
Create string error context for AST errors.
std::string readFile(const std::string &name)
Helper to read a file.
Definition: Files.hpp:48
bool fileExists(const std::string &name) noexcept
Checks if a file exists.
Definition: Files.hpp:29
std::string canonicalRelPath(const std::string &path)
Get the canonical relative path from a path.
Definition: Files.hpp:85
std::string getDirectoryFromPath(const std::string &path)
Get the directory from a path.
Definition: Files.hpp:63
std::string getFilenameFromPath(const std::string &path)
Get the filename from a path.
Definition: Files.hpp:74
std::ostream & operator<<(std::ostream &os, const std::vector< Node > &N) noexcept
Definition: Node.cpp:275
constexpr std::array< std::string_view, 13 > tokentype_string
Definition: Token.hpp:39
Node make_node(T &&value, std::size_t line, std::size_t col, const std::string &file)
Definition: Node.hpp:251
std::string makeTokenBasedErrorCtx(const std::string &match, std::size_t line, std::size_t col, const std::string &code)
Construct an error message based on a given match in the code.
constexpr std::array< std::string_view, 25 > operators
Definition: Common.hpp:89
Node make_node_list(std::size_t line, std::size_t col, const std::string &file)
Definition: Node.hpp:259
NodeType similarNodetypeFromTokentype(TokenType tt)
Definition: Parser.hpp:27
std::size_t line
Definition: Token.hpp:59
std::size_t col
Definition: Token.hpp:60
TokenType type
Definition: Token.hpp:57
std::string token
Definition: Token.hpp:58