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 
13 namespace 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<ListExecutor>(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()[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 it's 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()[first_node.string()] = node;
97  return;
98  }
99  }
100  // !{if cond then else}
101  else if (std::size_t sz = node.constList().size(); sz == 3 || sz == 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  if (kw != Keyword::Let && kw != Keyword::Mut && kw != Keyword::Set)
121  return;
122 
123  const Node& inner = node.constList()[2];
124  if (inner.nodeType() != NodeType::List)
125  return;
126 
127  if (inner.constList()[0].nodeType() == NodeType::Keyword && inner.constList()[0].keyword() == Keyword::Fun)
128  m_defined_functions[node.constList()[1].string()] = inner.constList()[1];
129  }
130  }
131 
132  void MacroProcessor::process(Node& node, unsigned depth)
133  {
134  bool has_created = false;
135 
136  if (node.nodeType() == NodeType::List)
137  {
138  // register known functions
139  registerFuncDef(node);
140 
141  // recursive call
142  std::size_t i = 0;
143  while (i < node.list().size())
144  {
145  if (node.list()[i].nodeType() == NodeType::Macro)
146  {
147  // create a scope only if needed
148  if ((!m_macros.empty() && !m_macros.back().empty() && static_cast<unsigned>(m_macros.back()["#depth"].number()) < depth) || !has_created)
149  {
150  has_created = true;
151  m_macros.emplace_back();
152  m_macros.back()["#depth"] = Node(static_cast<double>(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().empty() && static_cast<unsigned>(m_macros.back()["#depth"].number()) == 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;
233  subnode.setNodeType(NodeType::Symbol);
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  Node* macro = findNearestMacro(node.string());
250  if (macro != nullptr && macro->list().size() == 2)
251  return macro->list()[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 (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 num_idx = static_cast<long>(idx.number());
354  long sz = static_cast<long>(sublist.list().size());
355  long offset = 0;
356  if (sz > 0 && sublist.list()[0] == Node::getListNode())
357  {
358  num_idx = (num_idx >= 0) ? num_idx + 1 : num_idx;
359  offset = -1;
360  }
361 
362  if (num_idx < 0 && sz + num_idx >= 0 && -num_idx < sz)
363  return sublist.list()[sz + num_idx];
364  else if (num_idx >= 0 && num_idx + offset < sz)
365  return sublist.list()[num_idx];
366  }
367  }
368  else if (name == "head")
369  {
370  if (node.list().size() > 2)
371  throwMacroProcessingError("When expanding `head' inside a macro, got " + std::to_string(node.list().size() - 1) + " arguments, needed only 1", node);
372  else if (node.list()[1].nodeType() == NodeType::List)
373  {
374  Node& sublist = node.list()[1];
375  if (sublist.constList().size() > 0 && sublist.constList()[0] == Node::getListNode())
376  {
377  if (sublist.constList().size() > 1)
378  {
379  const Node sublistCopy = sublist.constList()[1];
380  node = sublistCopy;
381  }
382  else
383  node = Node::getNilNode();
384  }
385  else if (sublist.list().size() > 0)
386  node = sublist.constList()[0];
387  else
388  node = Node::getNilNode();
389  }
390  }
391  else if (name == "tail")
392  {
393  if (node.list().size() > 2)
394  throwMacroProcessingError("When expanding `tail' inside a macro, got " + std::to_string(node.list().size() - 1) + " arguments, needed only 1", node);
395  else if (node.list()[1].nodeType() == NodeType::List)
396  {
397  Node sublist = node.list()[1];
398  if (sublist.list().size() > 0 && sublist.list()[0] == Node::getListNode())
399  {
400  if (sublist.list().size() > 1)
401  {
402  sublist.list().erase(sublist.constList().begin() + 1);
403  node = sublist;
404  }
405  else
406  {
407  node = Node(NodeType::List);
409  }
410  }
411  else if (sublist.list().size() > 0)
412  {
413  sublist.list().erase(sublist.constList().begin());
414  node = sublist;
415  }
416  else
417  {
418  node = Node(NodeType::List);
420  }
421  }
422  }
423  else if (name == "symcat")
424  {
425  if (node.list().size() <= 2)
426  throwMacroProcessingError("When expanding `symcat', expected at least 2 arguments, got " + std::to_string(node.list().size() - 1) + " arguments", node);
427  if (node.list()[1].nodeType() != NodeType::Symbol)
428  throwMacroProcessingError("When expanding `symcat', expected the first argument to be a Symbol, got a " + typeToString(node.list()[1]), node);
429 
430  std::string sym = node.list()[1].string();
431 
432  for (std::size_t i = 2, end = node.list().size(); i < end; ++i)
433  {
434  Node ev = evaluate(node.list()[i], /* is_not_body */ true);
435 
436  switch (ev.nodeType())
437  {
438  case NodeType::Number:
439  sym += std::to_string(static_cast<long int>(ev.number())); // we don't want '.' in identifiers
440  break;
441 
442  case NodeType::String:
443  case NodeType::Symbol:
444  sym += ev.string();
445  break;
446 
447  default:
448  throwMacroProcessingError("When expanding `symcat', expected either a Number, String or Symbol, got a " + typeToString(ev), ev);
449  }
450  }
451 
453  node.setString(sym);
454  }
455  else if (name == "argcount")
456  {
457  Node sym = node.constList()[1];
458  if (sym.nodeType() == NodeType::Symbol)
459  {
460  if (auto it = m_defined_functions.find(sym.string()); it != m_defined_functions.end())
461  node = Node(static_cast<long>(it->second.constList().size()));
462  else
463  throwMacroProcessingError("When expanding `argcount', expected a known function name, got unbound variable " + sym.string(), sym);
464  }
465  else if (sym.nodeType() == NodeType::List && sym.list().size() == 3 && sym.list()[0].nodeType() == NodeType::Keyword && sym.list()[0].keyword() == Keyword::Fun)
466  node = Node(static_cast<long>(sym.list()[1].list().size()));
467  else
468  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);
469  }
470  }
471 
472  if (node.nodeType() == NodeType::List && node.constList().size() >= 1)
473  {
474  for (std::size_t i = 0; i < node.list().size(); ++i)
475  node.list()[i] = evaluate(node.list()[i], is_not_body);
476  }
477 
478  return node;
479  }
480 
481  bool MacroProcessor::isTruthy(const Node& node)
482  {
483  if (node.nodeType() == NodeType::Symbol)
484  {
485  if (node.string() == "true")
486  return true;
487  else if (node.string() == "false" || node.string() == "nil")
488  return false;
489  }
490  else if (node.nodeType() == NodeType::Number && node.number() != 0.0)
491  return true;
492  else if (node.nodeType() == NodeType::String && node.string().size() != 0)
493  return true;
494  else if (node.nodeType() == NodeType::Spread)
495  throwMacroProcessingError("Can not determine the truth value of a spreaded symbol", node);
496  return false;
497  }
498 
499  Node* MacroProcessor::findNearestMacro(const std::string& name)
500  {
501  if (m_macros.empty())
502  return nullptr;
503 
504  for (auto it = m_macros.rbegin(); it != m_macros.rend(); ++it)
505  {
506  if (it->size() != 0)
507  {
508  if (auto res = it->find(name); res != it->end())
509  return &res->second;
510  }
511  }
512  return nullptr;
513  }
514 
515  void MacroProcessor::deleteNearestMacro(const std::string& name)
516  {
517  if (m_macros.empty())
518  return;
519 
520  for (auto it = m_macros.rbegin(); it != m_macros.rend(); ++it)
521  {
522  if (it->size() != 0)
523  {
524  if (auto res = it->find(name); res != it->end())
525  {
526  // stop right here because we found one matching macro
527  it->erase(res);
528  return;
529  }
530  }
531  }
532  }
533 
534  bool MacroProcessor::isPredefined(const std::string& symbol)
535  {
536  auto it = std::find(
537  m_predefined_macros.begin(),
538  m_predefined_macros.end(),
539  symbol);
540 
541  return it != m_predefined_macros.end();
542  }
543 
545  {
546  applyMacro(node);
547 
548  if (node.nodeType() == NodeType::List)
549  {
550  for (std::size_t i = 0; i < node.list().size(); ++i)
551  recurApply(node.list()[i]);
552  }
553  }
554 
555  bool MacroProcessor::hadBegin(const Node& node)
556  {
557  return node.nodeType() == NodeType::List &&
558  node.constList().size() > 0 &&
559  node.constList()[0].nodeType() == NodeType::Keyword &&
560  node.constList()[0].keyword() == Keyword::Begin;
561  }
562 
563  void MacroProcessor::removeBegin(Node& node, std::size_t& i)
564  {
565  if (node.nodeType() == NodeType::List && node.list()[i].nodeType() == NodeType::List && node.list()[i].list().size() > 0)
566  {
567  Node lst = node.constList()[i];
568  Node first = lst.constList()[0];
569 
570  if (first.nodeType() == NodeType::Keyword && first.keyword() == Keyword::Begin)
571  {
572  std::size_t previous = i;
573 
574  for (std::size_t block_idx = 1, end = lst.constList().size(); block_idx < end; ++block_idx)
575  node.list().insert(node.constList().begin() + i + block_idx, lst.list()[block_idx]);
576 
577  i += lst.constList().size() - 2; // -2 instead of -1 because it get incremented right after
578  node.list().erase(node.constList().begin() + previous);
579  }
580  }
581  }
582 
583  bool MacroProcessor::isConstEval(const Node& node) const
584  {
585  switch (node.nodeType())
586  {
587  case NodeType::Symbol:
588  {
589  auto it = std::find(operators.begin(), operators.end(), node.string());
590  auto it2 = std::find_if(Builtins::builtins.begin(), Builtins::builtins.end(),
591  [&node](const std::pair<std::string, Value>& element) -> bool {
592  return node.string() == element.first;
593  });
594 
595  return it != operators.end() ||
596  it2 != Builtins::builtins.end() ||
597  const_cast<MacroProcessor*>(this)->findNearestMacro(node.string()) != nullptr ||
598  node.string() == "list";
599  }
600 
601  case NodeType::List:
602  return std::all_of(node.constList().begin(), node.constList().end(), [this](const Node& node) {
603  return isConstEval(node);
604  });
605 
606  case NodeType::Capture:
607  case NodeType::GetField:
608  case NodeType::Closure:
609  return false;
610 
611  case NodeType::Keyword:
612  case NodeType::String:
613  case NodeType::Number:
614  case NodeType::Macro:
615  case NodeType::Spread:
616  case NodeType::Unused:
617  return true;
618  }
619 
620  return false;
621  }
622 
623 
624  void MacroProcessor::throwMacroProcessingError(const std::string& message, const Node& node)
625  {
626  throw MacroProcessingError(makeNodeBasedErrorCtx(message, node));
627  }
628 }
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
The class handling the macros definitions and calls, given an AST.
Definition: Processor.hpp:30
void removeBegin(Node &node, std::size_t &i)
Remove a begin block added by a macro.
Definition: Processor.cpp:563
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:132
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:61
void recurApply(Node &node)
Recursively apply macros on a given node.
Definition: Processor.cpp:544
std::vector< std::string > m_predefined_macros
Already existing macros, non-keywords, non-builtins.
Definition: Processor.hpp:62
void registerMacro(Node &node)
Registers macros based on their type.
Definition: Processor.cpp:51
std::vector< std::unordered_map< std::string, Node > > m_macros
Handling macros in a scope fashion.
Definition: Processor.hpp:60
Node evaluate(Node &node, bool is_not_body=false)
Evaluate only the macros.
Definition: Processor.cpp:245
Node m_ast
The modified AST.
Definition: Processor.hpp:59
bool isTruthy(const Node &node)
Check if a node can be evaluated to true.
Definition: Processor.cpp:481
bool hadBegin(const Node &node)
Check if a given node is a list node, and starts with a Begin.
Definition: Processor.cpp:555
void throwMacroProcessingError(const std::string &message, const Node &node)
Throw a macro processing error.
Definition: Processor.cpp:624
std::unordered_map< std::string, Node > m_defined_functions
Definition: Processor.hpp:63
bool isConstEval(const Node &node) const
Check if a node can be evaluated at compile time.
Definition: Processor.cpp:583
bool isPredefined(const std::string &symbol)
Check if a given symbol is a predefined macro or not.
Definition: Processor.cpp:534
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
Node * findNearestMacro(const std::string &name)
Find the nearest macro matching a given name.
Definition: Processor.cpp:499
void registerFuncDef(Node &node)
Registers a function definition node.
Definition: Processor.cpp:115
unsigned m_debug
The debug level.
Definition: Processor.hpp:57
void deleteNearestMacro(const std::string &name)
Find the nearest macro matching a given name and delete it.
Definition: Processor.cpp:515
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