ArkScript
A small, fast, functional and scripting language for video games
Parser.cpp
Go to the documentation of this file.
2
3#include <fmt/core.h>
4
5namespace Ark::internal
6{
7 Parser::Parser(const unsigned debug, const bool interpret) :
8 BaseParser(), m_interpret(interpret), m_logger("Parser", debug),
9 m_ast(NodeType::List), m_imports({}), m_allow_macro_behavior(0)
10 {
11 m_ast.push_back(Node(Keyword::Begin));
12 }
13
14 void Parser::process(const std::string& filename, const std::string& code)
15 {
16 m_logger.traceStart("process");
17 initParser(filename, code);
18
19 while (!isEOF())
20 {
21 std::string comment;
23 if (isEOF())
24 {
25 if (!comment.empty())
26 m_ast.list().back().attachCommentAfter(comment);
27 break;
28 }
29
30 const auto pos = getCount();
31 if (auto n = node())
32 {
33 m_ast.push_back(n->attachNearestCommentBefore(n->comment() + comment));
34 comment.clear();
36 m_ast.list().back().attachCommentAfter(comment);
37 }
38 else
39 {
40 backtrack(pos);
41 errorWithNextToken("invalid syntax, expected node");
42 }
43 }
44
46 }
47
48 const Node& Parser::ast() const noexcept
49 {
50 return m_ast;
51 }
52
53 const std::vector<Import>& Parser::imports() const
54 {
55 return m_imports;
56 }
57
58 Node& Parser::setNodePosAndFilename(Node& node, const std::optional<FilePosition>& cursor) const
59 {
60 if (node.line() != 0 || node.col() != 0)
61 return node;
62
63 const auto [row, col] = cursor.value_or(getCursor());
64 node.setPos(row, col);
65 node.setFilename(m_filename);
66 return node;
67 }
68
69 std::optional<Node> Parser::node()
70 {
71 // save current position in buffer to be able to go back if needed
72 const auto position = getCount();
73
74 if (auto result = wrapped(&Parser::letMutSet, "variable assignment or declaration"))
75 return result;
76 backtrack(position);
77
78 if (auto result = wrapped(&Parser::function, "function"))
79 return result;
80 backtrack(position);
81
82 if (auto result = wrapped(&Parser::condition, "condition"))
83 return result;
84 backtrack(position);
85
86 if (auto result = wrapped(&Parser::loop, "loop"))
87 return result;
88 backtrack(position);
89
90 if (auto result = import_(); result.has_value())
91 return result;
92 backtrack(position);
93
94 if (auto result = block(); result.has_value())
95 return result;
96 backtrack(position);
97
98 if (auto result = wrapped(&Parser::macroCondition, "$if"))
99 return result;
100 backtrack(position);
101
102 if (auto result = macro(); result.has_value())
103 return result;
104 backtrack(position);
105
106 if (auto result = wrapped(&Parser::del, "del"))
107 return result;
108 backtrack(position);
109
110 if (auto result = functionCall(); result.has_value())
111 return result;
112 backtrack(position);
113
114 if (auto result = list(); result.has_value())
115 return result;
116 backtrack(position);
117
118 return std::nullopt; // will never reach
119 }
120
121 std::optional<Node> Parser::letMutSet()
122 {
123 std::optional<Node> leaf { NodeType::List };
124 setNodePosAndFilename(leaf.value());
125
126 std::string token;
127 if (!oneOf({ "let", "mut", "set" }, &token))
128 return std::nullopt;
129 std::string comment;
131 leaf->attachNearestCommentBefore(comment);
132
133 if (token == "let")
134 leaf->push_back(Node(Keyword::Let));
135 else if (token == "mut")
136 leaf->push_back(Node(Keyword::Mut));
137 else // "set"
138 leaf->push_back(Node(Keyword::Set));
139
141 {
142 const auto position = getCount();
143 if (const auto value = nodeOrValue(); value.has_value())
144 {
145 const auto sym = value.value();
146 if (sym.nodeType() == NodeType::List || sym.nodeType() == NodeType::Symbol || sym.nodeType() == NodeType::Macro || sym.nodeType() == NodeType::Spread)
147 leaf->push_back(sym);
148 else
149 error(fmt::format("Can not use a {} as a symbol name, even in a macro", nodeTypes[static_cast<std::size_t>(sym.nodeType())]), sym.repr());
150 }
151 else
152 backtrack(position);
153 }
154
155 if (leaf->constList().size() == 1)
156 {
157 // we haven't parsed anything while in "macro state"
158 std::string symbol;
159 if (!name(&symbol))
160 errorWithNextToken(token + " needs a symbol");
161
162 leaf->push_back(Node(NodeType::Symbol, symbol));
163 }
164
165 comment.clear();
167
168 if (auto value = nodeOrValue(); value.has_value())
169 leaf->push_back(value.value().attachNearestCommentBefore(comment));
170 else
171 errorWithNextToken("Expected a value");
172
173 return leaf;
174 }
175
176 std::optional<Node> Parser::del()
177 {
178 std::optional<Node> leaf { NodeType::List };
179 setNodePosAndFilename(leaf.value());
180
181 if (!oneOf({ "del" }))
182 return std::nullopt;
183 leaf->push_back(Node(Keyword::Del));
184
185 std::string comment;
187
188 std::string symbol;
189 if (!name(&symbol))
190 errorWithNextToken("del needs a symbol");
191
192 leaf->push_back(Node(NodeType::Symbol, symbol));
193 leaf->list().back().attachNearestCommentBefore(comment);
194 setNodePosAndFilename(leaf->list().back());
195
196 return leaf;
197 }
198
199 std::optional<Node> Parser::condition()
200 {
201 std::optional<Node> leaf { NodeType::List };
202 setNodePosAndFilename(leaf.value());
203
204 if (!oneOf({ "if" }))
205 return std::nullopt;
206
207 std::string comment;
209
210 leaf->push_back(Node(Keyword::If));
211
212 if (auto condition = nodeOrValue(); condition.has_value())
213 leaf->push_back(condition.value().attachNearestCommentBefore(comment));
214 else
215 errorWithNextToken("`if' needs a valid condition");
216
217 comment.clear();
219
220 if (auto value_if_true = nodeOrValue(); value_if_true.has_value())
221 leaf->push_back(value_if_true.value().attachNearestCommentBefore(comment));
222 else
223 errorWithNextToken("Expected a node or value after condition");
224
225 comment.clear();
227
228 if (auto value_if_false = nodeOrValue(); value_if_false.has_value())
229 {
230 leaf->push_back(value_if_false.value().attachNearestCommentBefore(comment));
231 comment.clear();
233 leaf->list().back().attachCommentAfter(comment);
234 }
235 else if (!comment.empty())
236 leaf->attachCommentAfter(comment);
237
238 setNodePosAndFilename(leaf->list().back());
239 return leaf;
240 }
241
242 std::optional<Node> Parser::loop()
243 {
244 std::optional<Node> leaf { NodeType::List };
245 setNodePosAndFilename(leaf.value());
246
247 if (!oneOf({ "while" }))
248 return std::nullopt;
249
250 std::string comment;
252
253 leaf->push_back(Node(Keyword::While));
254
255 if (auto condition = nodeOrValue(); condition.has_value())
256 leaf->push_back(condition.value().attachNearestCommentBefore(comment));
257 else
258 errorWithNextToken("`while' needs a valid condition");
259
260 comment.clear();
262
263 if (auto body = nodeOrValue(); body.has_value())
264 leaf->push_back(body.value().attachNearestCommentBefore(comment));
265 else
266 errorWithNextToken("Expected a node or value after loop condition");
267
268 setNodePosAndFilename(leaf->list().back());
269 return leaf;
270 }
271
272 std::optional<Node> Parser::import_()
273 {
274 std::optional<Node> leaf { NodeType::List };
275 setNodePosAndFilename(leaf.value());
276
277 if (!accept(IsChar('(')))
278 return std::nullopt;
279 std::string comment;
281 leaf->attachNearestCommentBefore(comment);
282
283 if (!oneOf({ "import" }))
284 return std::nullopt;
285 comment.clear();
287 leaf->push_back(Node(Keyword::Import));
288
289 Import import_data;
290
291 const auto pos = getCount();
292 if (!packageName(&import_data.prefix))
293 errorWithNextToken("Import expected a package name");
294
295 if (import_data.prefix.size() > 255)
296 {
297 backtrack(pos);
298 errorWithNextToken(fmt::format("Import name too long, expected at most 255 characters, got {}", import_data.prefix.size()));
299 }
300 import_data.package.push_back(import_data.prefix);
301
302 const auto [row, col] = getCursor();
303 import_data.col = col;
304 import_data.line = row;
305
306 Node packageNode(NodeType::List);
308 packageNode.push_back(Node(NodeType::Symbol, import_data.prefix));
309
310 // first, parse the package name
311 while (!isEOF())
312 {
313 // parsing package folder.foo.bar.yes
314 if (accept(IsChar('.')))
315 {
316 std::string path;
317 if (!packageName(&path))
318 errorWithNextToken("Package name expected after '.'");
319 else
320 {
321 packageNode.push_back(Node(NodeType::Symbol, path));
322 setNodePosAndFilename(packageNode.list().back());
323 import_data.package.push_back(path);
324 import_data.prefix = path; // in the end we will store the last element of the package, which is what we want
325
326 if (path.size() > 255)
327 {
328 backtrack(pos);
329 errorWithNextToken(fmt::format("Import name too long, expected at most 255 characters, got {}", path.size()));
330 }
331 }
332 }
333 else if (accept(IsChar(':')) && accept(IsChar('*'))) // parsing :*
334 {
335 leaf->push_back(packageNode);
336 leaf->push_back(Node(NodeType::Symbol, "*"));
337 setNodePosAndFilename(leaf->list().back());
338
339 space();
340 expect(IsChar(')'));
341
342 // save the import data structure to know we encounter an import node, and retrieve its data more easily later on
343 import_data.with_prefix = false;
344 import_data.is_glob = true;
345 m_imports.push_back(import_data);
346
347 return leaf;
348 }
349 else
350 break;
351 }
352
353 Node symbols(NodeType::List);
354 setNodePosAndFilename(symbols);
355 // then parse the symbols to import, if any
356 if (space()) // FIXME: potential regression introduced here
357 {
358 comment.clear();
360
361 while (!isEOF())
362 {
363 if (accept(IsChar(':'))) // parsing potential :a :b :c
364 {
365 std::string symbol;
366 if (!name(&symbol))
367 errorWithNextToken("Expected a valid symbol to import");
368 if (symbol == "*")
369 error(fmt::format("Glob patterns can not be separated from the package, use (import {}:*) instead", import_data.toPackageString()), symbol);
370
371 if (symbol.size() >= 2 && symbol[symbol.size() - 2] == ':' && symbol.back() == '*')
372 {
373 backtrack(getCount() - 2); // we can backtrack n-2 safely here because we know the previous chars were ":*"
374 error("Glob pattern can not follow a symbol to import", ":*");
375 }
376
377 symbols.push_back(Node(NodeType::Symbol, symbol).attachNearestCommentBefore(comment));
378 comment.clear();
379 setNodePosAndFilename(symbols.list().back());
380 import_data.symbols.push_back(symbol);
381 // we do not need the prefix when importing specific symbols
382 import_data.with_prefix = false;
383 }
384
385 if (!space())
386 break;
387 comment.clear();
389 }
390
391 if (!comment.empty() && !symbols.list().empty())
392 symbols.list().back().attachCommentAfter(comment);
393 }
394
395 leaf->push_back(packageNode);
396 leaf->push_back(symbols);
397 // save the import data
398 m_imports.push_back(import_data);
399
400 comment.clear();
402 leaf->list().back().attachCommentAfter(comment);
403
404 expect(IsChar(')'));
405 return leaf;
406 }
407
408 std::optional<Node> Parser::block()
409 {
410 std::optional<Node> leaf { NodeType::List };
411 setNodePosAndFilename(leaf.value());
412
413 bool alt_syntax = false;
414 std::string comment;
415 if (accept(IsChar('(')))
416 {
418 if (!oneOf({ "begin" }))
419 return std::nullopt;
420 }
421 else if (accept(IsChar('{')))
422 alt_syntax = true;
423 else
424 return std::nullopt;
425
426 leaf->push_back(Node(Keyword::Begin).attachNearestCommentBefore(comment));
427
428 comment.clear();
430
431 while (!isEOF())
432 {
433 if (auto value = nodeOrValue(); value.has_value())
434 {
435 leaf->push_back(value.value().attachNearestCommentBefore(comment));
436 comment.clear();
438 }
439 else
440 break;
441 }
442
444 expect(IsChar(!alt_syntax ? ')' : '}'));
445 setNodePosAndFilename(leaf->list().back());
446 leaf->list().back().attachCommentAfter(comment);
447 return leaf;
448 }
449
450 std::optional<Node> Parser::functionArgs()
451 {
452 expect(IsChar('('));
453 std::optional<Node> args { NodeType::List };
454 setNodePosAndFilename(args.value());
455
456 std::string comment;
458 args->attachNearestCommentBefore(comment);
459
460 bool has_captures = false;
461
462 while (!isEOF())
463 {
464 const auto pos = getCursor();
465 if (accept(IsChar('&'))) // captures
466 {
467 has_captures = true;
468 std::string capture;
469 if (!name(&capture))
470 break;
473 args->push_back(node);
474 }
475 else
476 {
477 const auto count = getCount();
478 std::string symbol;
479 if (!name(&symbol))
480 break;
481 if (has_captures)
482 {
483 backtrack(count);
484 error("Captured variables should be at the end of the argument list", symbol);
485 }
486
489 args->push_back(node);
490 }
491
492 comment.clear();
494 }
495
496 if (accept(IsChar(')')))
497 return args;
498 return std::nullopt;
499 }
500
501 std::optional<Node> Parser::function()
502 {
503 std::optional<Node> leaf { NodeType::List };
504 setNodePosAndFilename(leaf.value());
505
506 if (!oneOf({ "fun" }))
507 return std::nullopt;
508 leaf->push_back(Node(Keyword::Fun));
509
510 std::string comment_before_args;
511 newlineOrComment(&comment_before_args);
512
513 while (m_allow_macro_behavior > 0)
514 {
515 const auto position = getCount();
516
517 // args
518 if (const auto value = nodeOrValue(); value.has_value())
519 {
520 // if value is nil, just add an empty argument bloc to prevent bugs when
521 // declaring functions inside macros
522 Node args = value.value();
524 if (args.nodeType() == NodeType::Symbol && args.string() == "nil")
525 leaf->push_back(Node(NodeType::List));
526 else
527 leaf->push_back(args);
528 }
529 else
530 {
531 backtrack(position);
532 break;
533 }
534
535 std::string comment;
537 // body
538 if (auto value = nodeOrValue(); value.has_value())
539 leaf->push_back(value.value().attachNearestCommentBefore(comment));
540 else
541 errorWithNextToken("Expected a body for the function");
542 setNodePosAndFilename(leaf->list().back());
543 return leaf;
544 }
545
546 const auto position = getCount();
547 if (auto args = functionArgs(); args.has_value())
548 leaf->push_back(args.value().attachNearestCommentBefore(comment_before_args));
549 else
550 {
551 backtrack(position);
552
553 if (auto value = nodeOrValue(); value.has_value())
554 leaf->push_back(value.value().attachNearestCommentBefore(comment_before_args));
555 else
556 errorWithNextToken("Expected an argument list");
557 }
558
559 std::string comment;
561
562 if (auto value = nodeOrValue(); value.has_value())
563 leaf->push_back(value.value().attachNearestCommentBefore(comment));
564 else
565 errorWithNextToken("Expected a body for the function");
566
567 setNodePosAndFilename(leaf->list().back());
568 return leaf;
569 }
570
571 std::optional<Node> Parser::macroCondition()
572 {
573 std::optional<Node> leaf { NodeType::Macro };
574 setNodePosAndFilename(leaf.value());
575
576 if (!oneOf({ "$if" }))
577 return std::nullopt;
578 leaf->push_back(Node(Keyword::If));
579
580 std::string comment;
582 leaf->attachNearestCommentBefore(comment);
583
584 if (const auto condition = nodeOrValue(); condition.has_value())
585 leaf->push_back(condition.value());
586 else
587 errorWithNextToken("$if need a valid condition");
588
589 comment.clear();
591
592 if (auto value_if_true = nodeOrValue(); value_if_true.has_value())
593 leaf->push_back(value_if_true.value().attachNearestCommentBefore(comment));
594 else
595 errorWithNextToken("Expected a node or value after condition");
596
597 comment.clear();
599
600 if (auto value_if_false = nodeOrValue(); value_if_false.has_value())
601 {
602 leaf->push_back(value_if_false.value().attachNearestCommentBefore(comment));
603 comment.clear();
605 leaf->list().back().attachCommentAfter(comment);
606 }
607
608 setNodePosAndFilename(leaf->list().back());
609 return leaf;
610 }
611
612 std::optional<Node> Parser::macroArgs()
613 {
614 if (!accept(IsChar('(')))
615 return std::nullopt;
616
617 std::optional<Node> args { NodeType::List };
618 setNodePosAndFilename(args.value());
619
620 std::string comment;
622 args->attachNearestCommentBefore(comment);
623
624 std::vector<std::string> names;
625 while (!isEOF())
626 {
627 const auto pos = getCount();
628
629 std::string arg_name;
630 if (!name(&arg_name))
631 break;
632 comment.clear();
634 args->push_back(Node(NodeType::Symbol, arg_name).attachNearestCommentBefore(comment));
635
636 if (std::ranges::find(names, arg_name) != names.end())
637 {
638 backtrack(pos);
639 errorWithNextToken(fmt::format("Argument names must be unique, can not reuse `{}'", arg_name));
640 }
641 names.push_back(arg_name);
642 }
643
644 const auto pos = getCount();
645 if (sequence("..."))
646 {
647 std::string spread_name;
648 if (!name(&spread_name))
649 errorWithNextToken("Expected a name for the variadic arguments list");
650 args->push_back(Node(NodeType::Spread, spread_name));
651
652 comment.clear();
654 args->list().back().attachCommentAfter(comment);
655
656 if (std::ranges::find(names, spread_name) != names.end())
657 {
658 backtrack(pos);
659 errorWithNextToken(fmt::format("Argument names must be unique, can not reuse `{}'", spread_name));
660 }
661 }
662
663 if (!accept(IsChar(')')))
664 return std::nullopt;
665 comment.clear();
667 {
668 if (args->list().empty())
669 args->attachCommentAfter(comment);
670 else
671 args->list().back().attachCommentAfter(comment);
672 }
673
674 return args;
675 }
676
677 std::optional<Node> Parser::macro()
678 {
679 std::optional<Node> leaf { NodeType::Macro };
680 setNodePosAndFilename(leaf.value());
681
682 if (!accept(IsChar('(')))
683 return std::nullopt;
684 std::string comment;
686
687 if (!oneOf({ "$" }))
688 return std::nullopt;
690 leaf->attachNearestCommentBefore(comment);
691
692 std::string symbol;
693 if (!name(&symbol))
694 errorWithNextToken("$ needs a symbol to declare a macro");
695 comment.clear();
697
698 leaf->push_back(Node(NodeType::Symbol, symbol).attachNearestCommentBefore(comment));
699
700 const auto position = getCount();
701 if (const auto args = macroArgs(); args.has_value())
702 leaf->push_back(args.value());
703 else
704 {
705 backtrack(position);
706
708 const auto value = nodeOrValue();
710
711 if (value.has_value())
712 leaf->push_back(value.value());
713 else
714 errorWithNextToken(fmt::format("Expected an argument list, atom or node while defining macro `{}'", symbol));
715
716 setNodePosAndFilename(leaf->list().back());
717 if (accept(IsChar(')')))
718 return leaf;
719 }
720
722 const auto value = nodeOrValue();
724
725 if (value.has_value())
726 leaf->push_back(value.value());
727 else if (leaf->list().size() == 2)
728 {
729 setNodePosAndFilename(leaf->list().back());
730 comment.clear();
732 leaf->list().back().attachCommentAfter(comment);
733
734 expect(IsChar(')'));
735 return leaf;
736 }
737 else
738 {
739 backtrack(position);
740 errorWithNextToken(fmt::format("Expected a value while defining macro `{}'", symbol));
741 }
742
743 setNodePosAndFilename(leaf->list().back());
744 comment.clear();
746 leaf->list().back().attachCommentAfter(comment);
747
748 expect(IsChar(')'));
749 return leaf;
750 }
751
752 std::optional<Node> Parser::functionCall()
753 {
754 if (!accept(IsChar('(')))
755 return std::nullopt;
756 std::string comment;
758 auto cursor = getCursor();
759
760 std::optional<Node> func;
761 if (auto atom = anyAtomOf({ NodeType::Symbol, NodeType::Field }); atom.has_value())
762 func = atom->attachNearestCommentBefore(comment);
763 else if (auto nested = node(); nested.has_value())
764 func = nested->attachNearestCommentBefore(comment);
765 else
766 return std::nullopt;
767 comment.clear();
769
770 std::optional<Node> leaf { NodeType::List };
771 setNodePosAndFilename(leaf.value(), cursor);
772 setNodePosAndFilename(func.value(), cursor);
773 leaf->push_back(func.value());
774
775 while (!isEOF())
776 {
777 if (auto arg = nodeOrValue(); arg.has_value())
778 {
779 leaf->push_back(arg.value().attachNearestCommentBefore(comment));
780 comment.clear();
782 }
783 else
784 break;
785 }
786
787 leaf->list().back().attachCommentAfter(comment);
788
789 comment.clear();
791 leaf->list().back().attachCommentAfter(comment);
792
793 expect(IsChar(')'));
794 return leaf;
795 }
796
797 std::optional<Node> Parser::list()
798 {
799 std::optional<Node> leaf { NodeType::List };
800 setNodePosAndFilename(leaf.value());
801
802 if (!accept(IsChar('[')))
803 return std::nullopt;
804 leaf->push_back(Node(NodeType::Symbol, "list"));
805
806 std::string comment;
808 leaf->attachNearestCommentBefore(comment);
809
810 comment.clear();
811 while (!isEOF())
812 {
813 if (auto value = nodeOrValue(); value.has_value())
814 {
815 leaf->push_back(value.value().attachNearestCommentBefore(comment));
816 comment.clear();
818 }
819 else
820 break;
821 }
822 leaf->list().back().attachCommentAfter(comment);
823
824 expect(IsChar(']'));
825 return leaf;
826 }
827
828 std::optional<Node> Parser::atom()
829 {
830 const auto pos = getCount();
831
832 if (auto res = Parser::number(); res.has_value())
833 return res;
834 backtrack(pos);
835
836 if (auto res = Parser::string(); res.has_value())
837 return res;
838 backtrack(pos);
839
840 if (auto res = Parser::spread(); m_allow_macro_behavior > 0 && res.has_value())
841 return res;
842 backtrack(pos);
843
844 if (auto res = Parser::field(); res.has_value())
845 return res;
846 backtrack(pos);
847
848 if (auto res = Parser::symbol(); res.has_value())
849 return res;
850 backtrack(pos);
851
852 if (auto res = Parser::nil(); res.has_value())
853 return res;
854 backtrack(pos);
855
856 return std::nullopt;
857 }
858
859 std::optional<Node> Parser::anyAtomOf(const std::initializer_list<NodeType> types)
860 {
861 auto cursor = getCursor();
862 if (auto value = atom(); value.has_value())
863 {
864 setNodePosAndFilename(value.value(), cursor);
865 for (const auto type : types)
866 {
867 if (value->nodeType() == type)
868 return value;
869 }
870 }
871 return std::nullopt;
872 }
873
874 std::optional<Node> Parser::nodeOrValue()
875 {
876 auto cursor = getCursor();
877 if (auto value = atom(); value.has_value())
878 {
879 setNodePosAndFilename(value.value(), cursor);
880 return value;
881 }
882 if (auto sub_node = node(); sub_node.has_value())
883 {
884 setNodePosAndFilename(sub_node.value(), cursor);
885 return sub_node;
886 }
887
888 return std::nullopt;
889 }
890
891 std::optional<Node> Parser::wrapped(std::optional<Node> (Parser::*parser)(), const std::string& name)
892 {
893 auto cursor = getCursor();
894 if (!prefix('('))
895 return std::nullopt;
896 std::string comment;
898
899 if (auto result = (this->*parser)(); result.has_value())
900 {
901 result->attachNearestCommentBefore(result->comment() + comment);
902 setNodePosAndFilename(result.value(), cursor);
903
904 comment.clear();
906 result.value().attachCommentAfter(comment);
907
908 if (result->isListLike())
909 setNodePosAndFilename(result->list().back());
910 if (!suffix(')'))
912
913 comment.clear();
914 if (spaceComment(&comment))
915 result.value().attachCommentAfter(comment);
916
917 return result;
918 }
919
920 return std::nullopt;
921 }
922}
Parse ArkScript code, but do not handle any import declarations.
bool sequence(const std::string &s)
bool spaceComment(std::string *s=nullptr)
void initParser(const std::string &filename, const std::string &code)
bool expect(const CharPred &t, std::string *s=nullptr)
heck if a Character Predicate was able to parse, call next() if matching ; throw a CodeError if it do...
bool accept(const CharPred &t, std::string *s=nullptr)
check if a Character Predicate was able to parse, call next() if matching
bool newlineOrComment(std::string *s=nullptr)
bool oneOf(std::initializer_list< std::string > words, std::string *s=nullptr)
Fetch a token and try to match one of the given words.
void error(const std::string &error, std::string exp)
bool space(std::string *s=nullptr)
bool name(std::string *s=nullptr)
bool comment(std::string *s=nullptr)
bool packageName(std::string *s=nullptr)
void errorWithNextToken(const std::string &message)
Fetch the next token (space and paren delimited) to generate an error.
void errorMissingSuffix(char suffix, const std::string &node_name)
Generate an error for a given node when a suffix is missing.
FilePosition getCursor() const
void traceStart(std::string &&trace_name)
Definition Logger.hpp:75
A node of an Abstract Syntax Tree for ArkScript.
Definition Node.hpp:31
NodeType nodeType() const noexcept
Return the node type.
Definition Node.cpp:77
const std::string & string() const noexcept
Return the string held by the value (if the node type allows it)
Definition Node.cpp:37
void setPos(std::size_t line, std::size_t col) noexcept
Set the Position of the node in the text.
Definition Node.cpp:108
Node & attachNearestCommentBefore(const std::string &comment)
Set the comment field with the nearest comment before this node.
Definition Node.cpp:119
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:62
std::vector< Node > & list() noexcept
Return the list of sub-nodes held by the node.
Definition Node.cpp:67
std::optional< Node > functionArgs()
Definition Parser.cpp:450
std::optional< Node > macro()
Definition Parser.cpp:677
std::optional< Node > wrapped(std::optional< Node >(Parser::*parser)(), const std::string &name)
Try to parse using a given parser, prefixing and suffixing it with (...), handling comments around th...
Definition Parser.cpp:891
std::optional< Node > atom()
Try to parse an atom (number, string, spread, field, symbol, nil)
Definition Parser.cpp:828
void process(const std::string &filename, const std::string &code)
Parse the given code.
Definition Parser.cpp:14
std::optional< Node > loop()
Definition Parser.cpp:242
Parser(unsigned debug, bool interpret=true)
Constructs a new Parser object.
Definition Parser.cpp:7
std::optional< Node > nil()
Definition Parser.hpp:242
std::optional< Node > symbol()
Definition Parser.hpp:222
std::optional< Node > number()
Definition Parser.hpp:89
std::optional< Node > macroArgs()
Definition Parser.cpp:612
const Node & ast() const noexcept
Definition Parser.cpp:48
std::optional< Node > nodeOrValue()
Try to parse an atom first, if it fails try to parse a node.
Definition Parser.cpp:874
std::optional< Node > import_()
Definition Parser.cpp:272
const std::vector< Import > & imports() const
Definition Parser.cpp:53
std::optional< Node > string()
Definition Parser.hpp:105
std::optional< Node > macroCondition()
Definition Parser.cpp:571
std::optional< Node > spread()
Definition Parser.hpp:230
std::optional< Node > condition()
Definition Parser.cpp:199
Node & setNodePosAndFilename(Node &node, const std::optional< FilePosition > &cursor=std::nullopt) const
Update a node given a file position.
Definition Parser.cpp:58
std::optional< Node > node()
Definition Parser.cpp:69
std::optional< Node > functionCall()
Definition Parser.cpp:752
std::optional< Node > field()
Definition Parser.hpp:196
unsigned m_allow_macro_behavior
Toggled on when inside a macro definition, off afterward.
Definition Parser.hpp:64
std::optional< Node > letMutSet()
Definition Parser.cpp:121
std::optional< Node > anyAtomOf(std::initializer_list< NodeType > types)
Try to parse an atom, if any, match its type against the given list.
Definition Parser.cpp:859
std::vector< Import > m_imports
Definition Parser.hpp:63
std::optional< Node > function()
Definition Parser.cpp:501
std::optional< Node > list()
Definition Parser.cpp:797
std::optional< Node > block()
Definition Parser.cpp:408
std::optional< Node > del()
Definition Parser.cpp:176
constexpr std::array< std::string_view, 11 > nodeTypes
Node types as string, in the same order as the enum NodeType.
Definition Common.hpp:44
NodeType
The different node types available.
Definition Common.hpp:29
std::size_t line
Definition Import.hpp:14
std::vector< std::string > symbols
List of symbols to import, can be empty if none provided. (import package :a :b)
Definition Import.hpp:48
std::size_t col
Position in the source file.
Definition Import.hpp:14
std::string prefix
The filename without the extension.
Definition Import.hpp:23
bool is_glob
Import as glob (import package:*)
Definition Import.hpp:42
std::string toPackageString() const
Definition Import.hpp:54
std::vector< std::string > package
Package with all the segments.
Definition Import.hpp:31
bool with_prefix
Import with prefix (import package)
Definition Import.hpp:37