ArkScript
A small, fast, functional and scripting language for video games
Processor.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <utility>
5
6#include <Ark/Exceptions.hpp>
12
13namespace Ark::internal
14{
15 MacroProcessor::MacroProcessor(unsigned debug, uint16_t options) noexcept :
16 m_debug(debug), m_options(options)
17 {
18 // create executors pipeline
19 m_executor_pipeline = MacroExecutorPipeline(
20 { std::make_shared<SymbolExecutor>(this),
21 std::make_shared<ConditionalExecutor>(this),
22 std::make_shared<FunctionExecutor>(this) });
23
24 m_predefined_macros = {
25 "symcat",
26 "argcount"
27 };
28 }
29
30 void MacroProcessor::feed(const Node& ast)
31 {
32 if (m_debug >= 2)
33 std::cout << "Processing macros...\n";
34
35 // to be able to modify it
36 m_ast = ast;
37 process(m_ast, 0);
38
39 if (m_debug >= 3)
40 {
41 std::cout << "(MacroProcessor) AST after processing macros\n";
42 std::cout << m_ast << '\n';
43 }
44 }
45
46 const Node& MacroProcessor::ast() const noexcept
47 {
48 return m_ast;
49 }
50
52 {
53 // a macro needs at least 2 nodes, name + value is the minimal form
54 if (node.constList().size() < 2)
55 throwMacroProcessingError("invalid macro, missing value", node);
56
57 Node& first_node = node.list()[0];
58 Node& second_node = node.list()[1];
59
60 // !{name value}
61 if (node.constList().size() == 2)
62 {
63 if (first_node.nodeType() == NodeType::Symbol)
64 {
65 if (first_node.string() != "undef")
66 m_macros.back().add(first_node.string(), node);
67 else if (second_node.nodeType() == NodeType::Symbol) // undefine a macro
68 deleteNearestMacro(second_node.string());
69 else // used undef on a non-symbol
70 throwMacroProcessingError("can not undefine a macro without a name", second_node);
71 return;
72 }
73 throwMacroProcessingError("can not define a macro without a symbol", first_node);
74 }
75 // !{name (args) body}
76 else if (node.constList().size() == 3 && first_node.nodeType() == NodeType::Symbol)
77 {
78 if (second_node.nodeType() != NodeType::List)
79 throwMacroProcessingError("invalid macro argument's list", second_node);
80 else
81 {
82 bool had_spread = false;
83 for (const Node& n : second_node.constList())
84 {
85 if (n.nodeType() != NodeType::Symbol && n.nodeType() != NodeType::Spread)
86 throwMacroProcessingError("invalid macro argument's list, expected symbols", n);
87 else if (n.nodeType() == NodeType::Spread)
88 {
89 if (had_spread)
90 throwMacroProcessingError("got another spread argument, only one is allowed", n);
91 had_spread = true;
92 }
93 else if (had_spread && n.nodeType() == NodeType::Symbol)
94 throwMacroProcessingError("got another argument after a spread argument, which is invalid", n);
95 }
96 m_macros.back().add(first_node.string(), node);
97 return;
98 }
99 }
100 // !{if cond then else}
101 else if (std::size_t size = node.constList().size(); size == 3 || size == 4)
102 {
103 if (first_node.nodeType() == NodeType::Keyword && first_node.keyword() == Keyword::If)
104 {
105 applyMacro(node);
106 return;
107 }
108 else if (first_node.nodeType() == NodeType::Keyword)
109 throwMacroProcessingError("the only authorized keyword in macros is `if'", first_node);
110 }
111 // if we are here, it means we couldn't recognize the given macro, thus making it invalid
112 throwMacroProcessingError("unrecognized macro form", node);
113 }
114
116 {
117 if (node.nodeType() == NodeType::List && node.constList().size() > 0 && node.constList()[0].nodeType() == NodeType::Keyword)
118 {
119 Keyword kw = node.constList()[0].keyword();
120 // checking for function definition, which can occur only inside an assignment node
121 if (kw != Keyword::Let && kw != Keyword::Mut && kw != Keyword::Set)
122 return;
123
124 const Node& inner = node.constList()[2];
125 if (inner.nodeType() != NodeType::List)
126 return;
127
128 if (inner.constList().size() > 0 && inner.constList()[0].nodeType() == NodeType::Keyword && inner.constList()[0].keyword() == Keyword::Fun)
129 m_defined_functions[node.constList()[1].string()] = inner.constList()[1];
130 }
131 }
132
133 void MacroProcessor::process(Node& node, unsigned depth)
134 {
135 bool has_created = false;
136
137 if (node.nodeType() == NodeType::List)
138 {
139 // register known functions
140 registerFuncDef(node);
141
142 // recursive call
143 std::size_t i = 0;
144 while (i < node.list().size())
145 {
146 if (node.list()[i].nodeType() == NodeType::Macro)
147 {
148 // create a scope only if needed
149 if ((!m_macros.empty() && !m_macros.back().empty() && m_macros.back().depth() < depth) || !has_created)
150 {
151 has_created = true;
152 m_macros.emplace_back(depth);
153 }
154
155 bool had = hadBegin(node.list()[i]);
156 bool removed_begin = false;
157 std::size_t old = i;
158
159 registerMacro(node.list()[i]);
160 if (hadBegin(node.list()[i]) && !had)
161 {
162 removeBegin(node, i);
163 removed_begin = true;
164 }
165 else if (node.list()[i].nodeType() == NodeType::Macro || node.list()[i].nodeType() == NodeType::Unused)
166 node.list().erase(node.constList().begin() + i);
167
168 if (removed_begin)
169 i = old;
170 }
171 else // running on non-macros
172 {
173 bool added_begin = false;
174
175 bool had = hadBegin(node.list()[i]);
176 bool applied = applyMacro(node.list()[i]);
177
178 // remove unused blocks
179 if (node.list()[i].nodeType() == NodeType::Unused)
180 node.list().erase(node.constList().begin() + i);
181 // if we got `macro`, it was replaced but not entirely applied.
182 // but `(macro)` would get entirely applied because it's in a list,
183 // thus we need to evaluate the node if we have list[i].list[0] as a macro
184 if (applied)
185 recurApply(node.list()[i]);
186
187 if (hadBegin(node.list()[i]) && !had)
188 added_begin = true;
189
190 if (node.nodeType() == NodeType::List)
191 {
192 process(node.list()[i], depth + 1);
193 // needed if we created a function node from a macro
194 registerFuncDef(node.list()[i]);
195 }
196
197 // remove begins in macros
198 if (added_begin)
199 removeBegin(node, i);
200
201 // go forward only if it isn't a macro, because we delete macros
202 // while running on the AST
203 ++i;
204 }
205 }
206
207 // delete a scope only if needed
208 if (!m_macros.empty() && m_macros.back().depth() == depth)
209 m_macros.pop_back();
210 }
211 }
212
214 {
215 return m_executor_pipeline.applyMacro(node);
216 }
217
218 void MacroProcessor::unify(const std::unordered_map<std::string, Node>& map, Node& target, Node* parent, std::size_t index)
219 {
220 if (target.nodeType() == NodeType::Symbol)
221 {
222 if (auto p = map.find(target.string()); p != map.end())
223 target = p->second;
224 }
225 else if (target.nodeType() == NodeType::List || target.nodeType() == NodeType::Macro)
226 {
227 for (std::size_t i = 0, end = target.list().size(); i < end; ++i)
228 unify(map, target.list()[i], &target, i);
229 }
230 else if (target.nodeType() == NodeType::Spread)
231 {
232 Node subnode = target;
234 unify(map, subnode, parent);
235
236 if (subnode.nodeType() != NodeType::List)
237 throwMacroProcessingError("Got a non-list while trying to apply the spread operator", subnode);
238
239 for (std::size_t i = 1, end = subnode.list().size(); i < end; ++i)
240 parent->list().insert(parent->list().begin() + index + i, subnode.list()[i]);
241 parent->list().erase(parent->list().begin() + index); // remove the spread
242 }
243 }
244
245 Node MacroProcessor::evaluate(Node& node, bool is_not_body)
246 {
247 if (node.nodeType() == NodeType::Symbol)
248 {
249 const Node* macro = findNearestMacro(node.string());
250 if (macro != nullptr && macro->constList().size() == 2)
251 return macro->constList()[1];
252 else
253 return node;
254 }
255 else if (node.nodeType() == NodeType::List && node.constList().size() > 1 && node.list()[0].nodeType() == NodeType::Symbol)
256 {
257#define GEN_NOT_BODY(str_name, error_handler, ret) \
258 else if (name == str_name && is_not_body) \
259 { \
260 if (node.list().size() != 3) error_handler; \
261 Node one = evaluate(node.list()[1], is_not_body), \
262 two = evaluate(node.list()[2], is_not_body); \
263 return ret; \
264 }
265
266#define GEN_COMPARATOR(str_name, cond) GEN_NOT_BODY( \
267 str_name, \
268 throwMacroProcessingError("Interpreting a `" str_name "' condition with " + \
269 std::to_string(node.list().size() - 1) + " arguments, instead of 2.", \
270 node), \
271 (cond) ? Node::getTrueNode() : Node::getFalseNode())
272
273#define GEN_OP(str_name, op) GEN_NOT_BODY( \
274 str_name, \
275 throwMacroProcessingError("Interpreting a `" str_name "' operation with " + \
276 std::to_string(node.list().size() - 1) + " arguments, instead of 2.", \
277 node), \
278 (one.nodeType() == two.nodeType() && two.nodeType() == NodeType::Number) ? Node(one.number() op two.number()) : node)
279
280 const std::string& name = node.list()[0].string();
281 if (const Node* macro = findNearestMacro(name); macro != nullptr)
282 {
283 applyMacro(node.list()[0]);
284 if (node.list()[0].nodeType() == NodeType::Unused)
285 node.list().erase(node.constList().begin());
286 }
287 GEN_COMPARATOR("=", one == two)
288 GEN_COMPARATOR("!=", !(one == two))
289 GEN_COMPARATOR("<", one < two)
290 GEN_COMPARATOR(">", !(one < two) && !(one == two))
291 GEN_COMPARATOR("<=", one < two || one == two)
292 GEN_COMPARATOR(">=", !(one < two))
293 GEN_OP("+", +)
294 GEN_OP("-", -)
295 GEN_OP("*", *)
296 GEN_OP("/", /)
297 else if (name == "not" && is_not_body)
298 {
299 if (node.list().size() != 2)
300 throwMacroProcessingError("Interpreting a `not' condition with " + std::to_string(node.list().size() - 1) + " arguments, instead of 1.", node);
301
302 return (!isTruthy(evaluate(node.list()[1], is_not_body))) ? Node::getTrueNode() : Node::getFalseNode();
303 }
304 else if (name == "and" && is_not_body)
305 {
306 if (node.list().size() < 3)
307 throwMacroProcessingError("Interpreting a `and' chain with " + std::to_string(node.list().size() - 1) + " arguments, expected at least 2.", node);
308
309 for (std::size_t i = 1, end = node.list().size(); i < end; ++i)
310 {
311 if (!isTruthy(evaluate(node.list()[i], is_not_body)))
312 return Node::getFalseNode();
313 }
314 return Node::getTrueNode();
315 }
316 else if (name == "or" && is_not_body)
317 {
318 if (node.list().size() < 3)
319 throwMacroProcessingError("Interpreting a `or' chain with " + std::to_string(node.list().size() - 1) + " arguments, expected at least 2.", node);
320
321 for (std::size_t i = 1, end = node.list().size(); i < end; ++i)
322 {
323 if (isTruthy(evaluate(node.list()[i], is_not_body)))
324 return Node::getTrueNode();
325 }
326 return Node::getFalseNode();
327 }
328 else if (name == "len")
329 {
330 if (node.list().size() > 2)
331 throwMacroProcessingError("When expanding `len' inside a macro, got " + std::to_string(node.list().size() - 1) + " arguments, needed only 1", node);
332 else if (Node& lst = node.list()[1]; lst.nodeType() == NodeType::List) // only apply len at compile time if we can
333 {
334 if (isConstEval(lst))
335 {
336 if (lst.list().size() > 0 && lst.list()[0] == Node::getListNode())
337 node = Node(static_cast<long>(lst.list().size()) - 1);
338 else
339 node = Node(static_cast<long>(lst.list().size()));
340 }
341 }
342 }
343 else if (name == "@")
344 {
345 if (node.list().size() != 3)
346 throwMacroProcessingError("Interpreting a `@' with " + std::to_string(node.list().size() - 1) + " arguments, instead of 2.", node);
347
348 Node sublist = evaluate(node.list()[1], is_not_body);
349 Node idx = evaluate(node.list()[2], is_not_body);
350
351 if (sublist.nodeType() == NodeType::List && idx.nodeType() == NodeType::Number)
352 {
353 long size = static_cast<long>(sublist.list().size());
354 long real_size = size;
355 long num_idx = static_cast<long>(idx.number());
356
357 // if the first node is the function call to "list", don't count it
358 if (size > 0 && sublist.list()[0] == Node::getListNode())
359 {
360 real_size--;
361 if (num_idx >= 0)
362 ++num_idx;
363 }
364 num_idx = num_idx >= 0 ? num_idx : size + num_idx;
365
366 if (num_idx < size)
367 return sublist.list()[num_idx];
368 else
369 throwMacroProcessingError("Index (" + std::to_string(static_cast<long>(idx.number())) + ") out of range (list size: " + std::to_string(real_size) + ")", node);
370 }
371 }
372 else if (name == "head")
373 {
374 if (node.list().size() > 2)
375 throwMacroProcessingError("When expanding `head' inside a macro, got " + std::to_string(node.list().size() - 1) + " arguments, needed only 1", node);
376 else if (node.list()[1].nodeType() == NodeType::List)
377 {
378 Node& sublist = node.list()[1];
379 if (sublist.constList().size() > 0 && sublist.constList()[0] == Node::getListNode())
380 {
381 if (sublist.constList().size() > 1)
382 {
383 const Node sublistCopy = sublist.constList()[1];
384 node = sublistCopy;
385 }
386 else
387 node = Node::getNilNode();
388 }
389 else if (sublist.list().size() > 0)
390 node = sublist.constList()[0];
391 else
392 node = Node::getNilNode();
393 }
394 }
395 else if (name == "tail")
396 {
397 if (node.list().size() > 2)
398 throwMacroProcessingError("When expanding `tail' inside a macro, got " + std::to_string(node.list().size() - 1) + " arguments, needed only 1", node);
399 else if (node.list()[1].nodeType() == NodeType::List)
400 {
401 Node sublist = node.list()[1];
402 if (sublist.list().size() > 0 && sublist.list()[0] == Node::getListNode())
403 {
404 if (sublist.list().size() > 1)
405 {
406 sublist.list().erase(sublist.constList().begin() + 1);
407 node = sublist;
408 }
409 else
410 {
411 node = Node(NodeType::List);
413 }
414 }
415 else if (sublist.list().size() > 0)
416 {
417 sublist.list().erase(sublist.constList().begin());
418 node = sublist;
419 }
420 else
421 {
422 node = Node(NodeType::List);
424 }
425 }
426 }
427 else if (name == "symcat")
428 {
429 if (node.list().size() <= 2)
430 throwMacroProcessingError("When expanding `symcat', expected at least 2 arguments, got " + std::to_string(node.list().size() - 1) + " arguments", node);
431 if (node.list()[1].nodeType() != NodeType::Symbol)
432 throwMacroProcessingError("When expanding `symcat', expected the first argument to be a Symbol, got a " + typeToString(node.list()[1]), node);
433
434 std::string sym = node.list()[1].string();
435
436 for (std::size_t i = 2, end = node.list().size(); i < end; ++i)
437 {
438 Node ev = evaluate(node.list()[i], /* is_not_body */ true);
439
440 switch (ev.nodeType())
441 {
442 case NodeType::Number:
443 sym += std::to_string(static_cast<long int>(ev.number())); // we don't want '.' in identifiers
444 break;
445
446 case NodeType::String:
447 case NodeType::Symbol:
448 sym += ev.string();
449 break;
450
451 default:
452 throwMacroProcessingError("When expanding `symcat', expected either a Number, String or Symbol, got a " + typeToString(ev), ev);
453 }
454 }
455
457 node.setString(sym);
458 }
459 else if (name == "argcount")
460 {
461 Node sym = node.constList()[1];
462 if (sym.nodeType() == NodeType::Symbol)
463 {
464 if (auto it = m_defined_functions.find(sym.string()); it != m_defined_functions.end())
465 node = Node(static_cast<long>(it->second.constList().size()));
466 else
467 throwMacroProcessingError("When expanding `argcount', expected a known function name, got unbound variable " + sym.string(), sym);
468 }
469 else if (sym.nodeType() == NodeType::List && sym.list().size() == 3 && sym.list()[0].nodeType() == NodeType::Keyword && sym.list()[0].keyword() == Keyword::Fun)
470 node = Node(static_cast<long>(sym.list()[1].list().size()));
471 else
472 throwMacroProcessingError("When trying to apply `argcount', got a " + std::string(nodeTypes[static_cast<std::size_t>(sym.nodeType())]) + " instead of a Symbol or Function", sym);
473 }
474 }
475
476 if (node.nodeType() == NodeType::List && node.constList().size() >= 1)
477 {
478 for (std::size_t i = 0; i < node.list().size(); ++i)
479 node.list()[i] = evaluate(node.list()[i], is_not_body);
480 }
481
482 return node;
483 }
484
486 {
487 if (node.nodeType() == NodeType::Symbol)
488 {
489 if (node.string() == "true")
490 return true;
491 else if (node.string() == "false" || node.string() == "nil")
492 return false;
493 }
494 else if (node.nodeType() == NodeType::Number && node.number() != 0.0)
495 return true;
496 else if (node.nodeType() == NodeType::String && node.string().size() != 0)
497 return true;
498 else if (node.nodeType() == NodeType::Spread)
499 throwMacroProcessingError("Can not determine the truth value of a spreaded symbol", node);
500 return false;
501 }
502
503 const Node* MacroProcessor::findNearestMacro(const std::string& name) const
504 {
505 if (m_macros.empty())
506 return nullptr;
507
508 for (auto it = m_macros.rbegin(); it != m_macros.rend(); ++it)
509 {
510 if (auto res = it->has(name); res != nullptr)
511 return res;
512 }
513 return nullptr;
514 }
515
516 void MacroProcessor::deleteNearestMacro(const std::string& name)
517 {
518 if (m_macros.empty())
519 return;
520
521 for (auto it = m_macros.rbegin(); it != m_macros.rend(); ++it)
522 {
523 if (it->remove(name))
524 {
525 // stop right here because we found one matching macro
526 return;
527 }
528 }
529 }
530
531 bool MacroProcessor::isPredefined(const std::string& symbol)
532 {
533 auto it = std::find(
534 m_predefined_macros.begin(),
536 symbol);
537
538 return it != m_predefined_macros.end();
539 }
540
542 {
543 applyMacro(node);
544
545 if (node.nodeType() == NodeType::List)
546 {
547 for (std::size_t i = 0; i < node.list().size(); ++i)
548 recurApply(node.list()[i]);
549 }
550 }
551
553 {
554 return node.nodeType() == NodeType::List &&
555 node.constList().size() > 0 &&
556 node.constList()[0].nodeType() == NodeType::Keyword &&
557 node.constList()[0].keyword() == Keyword::Begin;
558 }
559
560 void MacroProcessor::removeBegin(Node& node, std::size_t& i)
561 {
562 if (node.nodeType() == NodeType::List && node.list()[i].nodeType() == NodeType::List && node.list()[i].list().size() > 0)
563 {
564 Node lst = node.constList()[i];
565 Node first = lst.constList()[0];
566
567 if (first.nodeType() == NodeType::Keyword && first.keyword() == Keyword::Begin)
568 {
569 std::size_t previous = i;
570
571 for (std::size_t block_idx = 1, end = lst.constList().size(); block_idx < end; ++block_idx)
572 node.list().insert(node.constList().begin() + i + block_idx, lst.list()[block_idx]);
573
574 i += lst.constList().size() - 2; // -2 instead of -1 because it get incremented right after
575 node.list().erase(node.constList().begin() + previous);
576 }
577 }
578 }
579
580 bool MacroProcessor::isConstEval(const Node& node) const
581 {
582 switch (node.nodeType())
583 {
584 case NodeType::Symbol:
585 {
586 auto it = std::find(operators.begin(), operators.end(), node.string());
587 auto it2 = std::find_if(Builtins::builtins.begin(), Builtins::builtins.end(),
588 [&node](const std::pair<std::string, Value>& element) -> bool {
589 return node.string() == element.first;
590 });
591
592 return it != operators.end() ||
593 it2 != Builtins::builtins.end() ||
594 findNearestMacro(node.string()) != nullptr ||
595 node.string() == "list";
596 }
597
598 case NodeType::List:
599 return std::all_of(node.constList().begin(), node.constList().end(), [this](const Node& node) {
600 return isConstEval(node);
601 });
602
606 return false;
607
609 case NodeType::String:
610 case NodeType::Number:
611 case NodeType::Macro:
612 case NodeType::Spread:
613 case NodeType::Unused:
614 return true;
615 }
616
617 return false;
618 }
619
620
621 void MacroProcessor::throwMacroProcessingError(const std::string& message, const Node& node)
622 {
623 throw MacroProcessingError(makeNodeBasedErrorCtx(message, node));
624 }
625}
Host the declaration of all the ArkScript builtins.
Executor for Conditional Macros.
ArkScript homemade exceptions.
Executor for List Macros.
#define GEN_OP(str_name, op)
#define GEN_COMPARATOR(str_name, cond)
Handles the macros and their expansion in ArkScript source code.
Executor for Symbol Macros.
MacroProcessingError thrown by the compiler.
Definition: Exceptions.hpp:119
The class that initializes the MacroExecutors.
Definition: Pipeline.hpp:29
bool applyMacro(Node &node)
Passes node through all MacroExecutors sequentially.
Definition: Pipeline.cpp:9
void removeBegin(Node &node, std::size_t &i)
Remove a begin block added by a macro.
Definition: Processor.cpp:560
MacroProcessor(unsigned debug, uint16_t options) noexcept
Construct a new Macro Processor object.
Definition: Processor.cpp:15
void process(Node &node, unsigned depth)
Register macros in scopes and apply them as needed.
Definition: Processor.cpp:133
void unify(const std::unordered_map< std::string, Node > &map, Node &target, Node *parent, std::size_t index=0)
Unify a target node with a given map symbol => replacement node, recursively.
Definition: Processor.cpp:218
const Node & ast() const noexcept
Return the modified AST.
Definition: Processor.cpp:46
MacroExecutorPipeline m_executor_pipeline
Definition: Processor.hpp:62
void recurApply(Node &node)
Recursively apply macros on a given node.
Definition: Processor.cpp:541
std::vector< std::string > m_predefined_macros
Already existing macros, non-keywords, non-builtins.
Definition: Processor.hpp:63
void registerMacro(Node &node)
Registers macros based on their type.
Definition: Processor.cpp:51
Node evaluate(Node &node, bool is_not_body=false)
Evaluate only the macros.
Definition: Processor.cpp:245
const Node * findNearestMacro(const std::string &name) const
Find the nearest macro matching a given name.
Definition: Processor.cpp:503
Node m_ast
The modified AST.
Definition: Processor.hpp:60
bool isTruthy(const Node &node)
Check if a node can be evaluated to true.
Definition: Processor.cpp:485
bool hadBegin(const Node &node)
Check if a given node is a list node, and starts with a Begin.
Definition: Processor.cpp:552
void throwMacroProcessingError(const std::string &message, const Node &node)
Throw a macro processing error.
Definition: Processor.cpp:621
std::unordered_map< std::string, Node > m_defined_functions
Definition: Processor.hpp:64
bool isConstEval(const Node &node) const
Check if a node can be evaluated at compile time.
Definition: Processor.cpp:580
bool isPredefined(const std::string &symbol)
Check if a given symbol is a predefined macro or not.
Definition: Processor.cpp:531
bool applyMacro(Node &node)
Apply a macro on a given node.
Definition: Processor.cpp:213
void feed(const Node &ast)
Send the complete AST (after the inclusions and stuff), and work on it.
Definition: Processor.cpp:30
std::vector< MacroScope > m_macros
Handling macros in a scope fashion.
Definition: Processor.hpp:61
void registerFuncDef(Node &node)
Registers a function definition node.
Definition: Processor.cpp:115
unsigned m_debug
The debug level.
Definition: Processor.hpp:58
void deleteNearestMacro(const std::string &name)
Find the nearest macro matching a given name and delete it.
Definition: Processor.cpp:516
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
static const Node & getListNode()
Provide a statically initialized / correct and guaranteed to be initialized Node representing "Empty ...
Definition: Node.cpp:28
void setNodeType(NodeType type) noexcept
Set the Node Type object.
Definition: Node.cpp:131
const std::string & string() const noexcept
Return the string held by the value (if the node type allows it)
Definition: Node.cpp:92
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
static const Node & getNilNode()
Provide a statically initialized / correct and guaranteed to be initialized Node representing "Nil".
Definition: Node.cpp:22
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
static const Node & getTrueNode()
Provide a statically initialized / correct and guaranteed to be initialized Node representing "true".
Definition: Node.cpp:10
double number() const noexcept
Return the number held by the value (if the node type allows it)
Definition: Node.cpp:97
static const Node & getFalseNode()
Provide a statically initialized / correct and guaranteed to be initialized Node representing "false"...
Definition: Node.cpp:16
std::vector< Node > & list() noexcept
Return the list of sub-nodes held by the node.
Definition: Node.cpp:114
Create string error context for AST errors.
const std::vector< std::pair< std::string, Value > > builtins
std::string typeToString(const Node &node) noexcept
Definition: Node.hpp:267
constexpr std::array< std::string_view, 11 > nodeTypes
Definition: Common.hpp:43
constexpr std::array< std::string_view, 25 > operators
Definition: Common.hpp:89
std::string makeNodeBasedErrorCtx(const std::string &message, const Node &node)
Construct an error message based on a given node.
Keyword
The different keywords available.
Definition: Common.hpp:59