ArkScript
A small, fast, functional and scripting language for video games
Compiler.cpp
Go to the documentation of this file.
2
3#include <chrono>
4#include <limits>
5#include <utility>
6#include <filesystem>
7#include <algorithm>
8#include <fmt/core.h>
9#include <fmt/color.h>
10
11#include <Ark/Constants.hpp>
12#include <Ark/Literals.hpp>
13#include <Ark/Utils.hpp>
16
17namespace Ark::internal
18{
19 using namespace literals;
20
21 Compiler::Compiler(const unsigned debug) :
22 m_debug(debug)
23 {}
24
25 void Compiler::process(const Node& ast)
26 {
27 m_code_pages.emplace_back(); // create empty page
28
29 // gather symbols, values, and start to create code segments
31 ast,
32 /* current_page */ Page { .index = 0, .is_temp = false },
33 /* is_result_unused */ false,
34 /* is_terminal */ false);
35 }
36
37 const std::vector<IR::Block>& Compiler::intermediateRepresentation() const noexcept
38 {
39 return m_code_pages;
40 }
41
42 const std::vector<std::string>& Compiler::symbols() const noexcept
43 {
44 return m_symbols;
45 }
46
47 const std::vector<ValTableElem>& Compiler::values() const noexcept
48 {
49 return m_values;
50 }
51
52 std::optional<Instruction> Compiler::getOperator(const std::string& name) noexcept
53 {
54 const auto it = std::ranges::find(Language::operators, name);
55 if (it != Language::operators.end())
56 return static_cast<Instruction>(std::distance(Language::operators.begin(), it) + FIRST_OPERATOR);
57 return std::nullopt;
58 }
59
60 std::optional<uint16_t> Compiler::getBuiltin(const std::string& name) noexcept
61 {
62 const auto it = std::ranges::find_if(Builtins::builtins,
63 [&name](const std::pair<std::string, Value>& element) -> bool {
64 return name == element.first;
65 });
66 if (it != Builtins::builtins.end())
67 return static_cast<uint16_t>(std::distance(Builtins::builtins.begin(), it));
68 return std::nullopt;
69 }
70
71 std::optional<Instruction> Compiler::getListInstruction(const std::string& name) noexcept
72 {
73 const auto it = std::ranges::find(Language::listInstructions, name);
74 if (it != Language::listInstructions.end())
75 return static_cast<Instruction>(std::distance(Language::listInstructions.begin(), it) + LIST);
76 return std::nullopt;
77 }
78
80 {
81 if (node.nodeType() == NodeType::List && !node.constList().empty() && node.constList()[0].nodeType() == NodeType::Keyword)
82 return (node.constList()[0].keyword() == Keyword::Begin && node.constList().size() > 1) ||
83 node.constList()[0].keyword() == Keyword::Fun ||
84 node.constList()[0].keyword() == Keyword::If;
85 return true; // any other node, function call, symbol, number...
86 }
87
88 bool Compiler::isUnaryInst(const Instruction inst) noexcept
89 {
90 switch (inst)
91 {
92 case NOT: [[fallthrough]];
93 case LEN: [[fallthrough]];
94 case EMPTY: [[fallthrough]];
95 case TAIL: [[fallthrough]];
96 case HEAD: [[fallthrough]];
97 case ISNIL: [[fallthrough]];
98 case TO_NUM: [[fallthrough]];
99 case TO_STR: [[fallthrough]];
100 case TYPE:
101 return true;
102
103 default:
104 return false;
105 }
106 }
107
108 void Compiler::compilerWarning(const std::string& message, const Node& node)
109 {
110 fmt::println("{} {}", fmt::styled("Warning", fmt::fg(fmt::color::dark_orange)), Diagnostics::makeContextWithNode(message, node));
111 }
112
113 void Compiler::throwCompilerError(const std::string& message, const Node& node)
114 {
115 throw CodeError(message, node.filename(), node.line(), node.col(), node.repr());
116 }
117
118 void Compiler::compileExpression(const Node& x, const Page p, const bool is_result_unused, const bool is_terminal, const std::string& var_name)
119 {
120 // register symbols
121 if (x.nodeType() == NodeType::Symbol)
122 compileSymbol(x, p, is_result_unused);
123 else if (x.nodeType() == NodeType::Field)
124 {
125 // the parser guarantees us that there is at least 2 elements (eg: a.b)
126 compileSymbol(x.constList()[0], p, is_result_unused);
127 for (auto it = x.constList().begin() + 1, end = x.constList().end(); it != end; ++it)
128 {
129 uint16_t i = addSymbol(*it);
130 page(p).emplace_back(GET_FIELD, i);
131 }
132 }
133 // register values
134 else if (x.nodeType() == NodeType::String || x.nodeType() == NodeType::Number)
135 {
136 uint16_t i = addValue(x);
137
138 if (!is_result_unused)
139 page(p).emplace_back(LOAD_CONST, i);
140 }
141 // empty code block should be nil
142 else if (x.constList().empty())
143 {
144 if (!is_result_unused)
145 {
146 static const std::optional<uint16_t> nil = getBuiltin("nil");
147 page(p).emplace_back(BUILTIN, nil.value());
148 }
149 }
150 // list instructions
151 else if (const auto c0 = x.constList()[0]; c0.nodeType() == NodeType::Symbol && getListInstruction(c0.string()).has_value())
152 compileListInstruction(c0, x, p, is_result_unused);
153 // registering structures
154 else if (x.constList()[0].nodeType() == NodeType::Keyword)
155 {
156 switch (const Keyword keyword = x.constList()[0].keyword())
157 {
158 case Keyword::If:
159 compileIf(x, p, is_result_unused, is_terminal, var_name);
160 break;
161
162 case Keyword::Set:
163 [[fallthrough]];
164 case Keyword::Let:
165 [[fallthrough]];
166 case Keyword::Mut:
167 compileLetMutSet(keyword, x, p);
168 break;
169
170 case Keyword::Fun:
171 compileFunction(x, p, is_result_unused, var_name);
172 break;
173
174 case Keyword::Begin:
175 {
176 for (std::size_t i = 1, size = x.constList().size(); i < size; ++i)
178 x.constList()[i],
179 p,
180 // All the nodes in a begin (except for the last one) are producing a result that we want to drop.
181 (i != size - 1) || is_result_unused,
182 // If the begin is a terminal node, only its last node is terminal.
183 is_terminal && (i == size - 1),
184 var_name);
185 break;
186 }
187
188 case Keyword::While:
189 compileWhile(x, p);
190 break;
191
192 case Keyword::Import:
194 break;
195
196 case Keyword::Del:
197 page(p).emplace_back(DEL, addSymbol(x.constList()[1]));
198 break;
199 }
200 }
201 else if (x.nodeType() == NodeType::List)
202 {
203 // if we are here, we should have a function name
204 // push arguments first, then function name, then call it
205 handleCalls(x, p, is_result_unused, is_terminal, var_name);
206 }
207 else
208 throwCompilerError("boop", x); // FIXME
209 }
210
211 void Compiler::compileSymbol(const Node& x, const Page p, const bool is_result_unused)
212 {
213 const std::string& name = x.string();
214
215 if (const auto it_builtin = getBuiltin(name))
216 page(p).emplace_back(Instruction::BUILTIN, it_builtin.value());
217 else if (getOperator(name).has_value())
218 throwCompilerError(fmt::format("Found a free standing operator: `{}`", name), x);
219 else
220 page(p).emplace_back(LOAD_SYMBOL, addSymbol(x)); // using the variable
221
222 if (is_result_unused)
223 {
224 compilerWarning("Statement has no effect", x);
225 page(p).emplace_back(POP);
226 }
227 }
228
229 void Compiler::compileListInstruction(const Node& c0, const Node& x, const Page p, const bool is_result_unused)
230 {
231 std::string name = c0.string();
232 Instruction inst = getListInstruction(name).value();
233
234 // length of at least 1 since we got a symbol name
235 const auto argc = x.constList().size() - 1u;
236 // error, can not use append/concat/pop (and their in place versions) with a <2 length argument list
237 if (argc < 2 && inst != LIST)
238 throwCompilerError(fmt::format("Can not use {} with less than 2 arguments", name), c0);
239 if (std::cmp_greater(argc, std::numeric_limits<uint16_t>::max()))
240 throwCompilerError(fmt::format("Too many arguments ({}), exceeds 65'535", argc), x);
241
242 // compile arguments in reverse order
243 for (std::size_t i = x.constList().size() - 1u; i > 0; --i)
244 {
245 const auto node = x.constList()[i];
246 if (nodeProducesOutput(node))
247 compileExpression(node, p, false, false);
248 else
249 throwCompilerError(fmt::format("Invalid node inside call to {}", name), node);
250 }
251
252 // put inst and number of arguments
253 std::size_t inst_argc;
254 switch (inst)
255 {
256 case LIST:
257 inst_argc = argc;
258 break;
259
260 case APPEND:
261 case APPEND_IN_PLACE:
262 case CONCAT:
263 case CONCAT_IN_PLACE:
264 inst_argc = argc - 1;
265 break;
266
267 default:
268 inst_argc = 0;
269 break;
270 }
271 page(p).emplace_back(inst, static_cast<uint16_t>(inst_argc));
272
273 if (is_result_unused && name.back() != '!') // in-place functions never push a value
274 {
275 compilerWarning("Ignoring return value of function", x);
276 page(p).emplace_back(POP);
277 }
278 }
279
280 void Compiler::compileIf(const Node& x, const Page p, const bool is_result_unused, const bool is_terminal, const std::string& var_name)
281 {
282 // compile condition
283 compileExpression(x.constList()[1], p, false, false);
284
285 // jump only if needed to the if
286 const auto label_then = IR::Entity::Label();
287 page(p).emplace_back(IR::Entity::GotoIf(label_then, true));
288
289 // else code
290 if (x.constList().size() == 4) // we have an else clause
291 compileExpression(x.constList()[3], p, is_result_unused, is_terminal, var_name);
292
293 // when else is finished, jump to end
294 const auto label_end = IR::Entity::Label();
295 page(p).emplace_back(IR::Entity::Goto(label_end));
296
297 // absolute address to jump to if condition is true
298 page(p).emplace_back(label_then);
299 // if code
300 compileExpression(x.constList()[2], p, is_result_unused, is_terminal, var_name);
301 // set jump to end pos
302 page(p).emplace_back(label_end);
303 }
304
305 void Compiler::compileFunction(const Node& x, const Page p, const bool is_result_unused, const std::string& var_name)
306 {
307 if (const auto args = x.constList()[1]; args.nodeType() != NodeType::List)
308 throwCompilerError(fmt::format("Expected a well formed argument(s) list, got a {}", typeToString(args)), args);
309 if (x.constList().size() != 3)
310 throwCompilerError("Invalid node ; if it was computed by a macro, check that a node is returned", x);
311
312 // capture, if needed
313 bool is_closure = false;
314 for (const auto& node : x.constList()[1].constList())
315 {
316 if (node.nodeType() == NodeType::Capture)
317 {
318 page(p).emplace_back(CAPTURE, addSymbol(node));
319 is_closure = true;
320 }
321 }
322
323 // create new page for function body
324 m_code_pages.emplace_back();
325 const auto function_body_page = Page { .index = m_code_pages.size() - 1, .is_temp = false };
326 // save page_id into the constants table as PageAddr and load the const
327 page(p).emplace_back(is_closure ? MAKE_CLOSURE : LOAD_CONST, addValue(function_body_page.index, x));
328
329 // pushing arguments from the stack into variables in the new scope
330 for (const auto& node : x.constList()[1].constList())
331 {
332 if (node.nodeType() == NodeType::Symbol)
333 page(function_body_page).emplace_back(STORE, addSymbol(node));
334 }
335
336 // push body of the function
337 compileExpression(x.constList()[2], function_body_page, false, true, var_name);
338
339 // return last value on the stack
340 page(function_body_page).emplace_back(RET);
341
342 // if the computed function is unused, pop it
343 if (is_result_unused)
344 {
345 compilerWarning("Unused declared function", x);
346 page(p).emplace_back(POP);
347 }
348 }
349
350 void Compiler::compileLetMutSet(const Keyword n, const Node& x, const Page p)
351 {
352 if (const auto sym = x.constList()[1]; sym.nodeType() != NodeType::Symbol)
353 throwCompilerError(fmt::format("Expected a symbol, got a {}", typeToString(sym)), sym);
354 if (x.constList().size() != 3)
355 throwCompilerError("Invalid node ; if it was computed by a macro, check that a node is returned", x);
356
357 const std::string name = x.constList()[1].string();
358 uint16_t i = addSymbol(x.constList()[1]);
359
360 // put value before symbol id
361 // starting at index = 2 because x is a (let|mut|set variable ...) node
362 for (std::size_t idx = 2, end = x.constList().size(); idx < end; ++idx)
363 compileExpression(x.constList()[idx], p, false, false, name);
364
365 if (n == Keyword::Let || n == Keyword::Mut)
366 page(p).emplace_back(STORE, i);
367 else
368 page(p).emplace_back(SET_VAL, i);
369 }
370
371 void Compiler::compileWhile(const Node& x, const Page p)
372 {
373 if (x.constList().size() != 3)
374 throwCompilerError("Invalid node ; if it was computed by a macro, check that a node is returned", x);
375
376 // save current position to jump there at the end of the loop
377 const auto label_loop = IR::Entity::Label();
378 page(p).emplace_back(label_loop);
379 // push condition
380 compileExpression(x.constList()[1], p, false, false);
381 // absolute jump to end of block if condition is false
382 const auto label_end = IR::Entity::Label();
383 page(p).emplace_back(IR::Entity::GotoIf(label_end, false));
384 // push code to page
385 compileExpression(x.constList()[2], p, true, false);
386
387 // loop, jump to the condition
388 page(p).emplace_back(IR::Entity::Goto(label_loop));
389
390 // absolute address to jump to if condition is false
391 page(p).emplace_back(label_end);
392 }
393
395 {
396 std::string path;
397 const Node package_node = x.constList()[1];
398 for (std::size_t i = 0, end = package_node.constList().size(); i < end; ++i)
399 {
400 path += package_node.constList()[i].string();
401 if (i + 1 != end)
402 path += "/";
403 }
404 path += ".arkm";
405
406 // register plugin path in the constants table
407 uint16_t id = addValue(Node(NodeType::String, path));
408 // add plugin instruction + id of the constant referring to the plugin path
409 page(p).emplace_back(PLUGIN, id);
410 }
411
412 void Compiler::handleCalls(const Node& x, const Page p, bool is_result_unused, const bool is_terminal, const std::string& var_name)
413 {
414 constexpr std::size_t start_index = 1;
415
416 const auto node = x.constList()[0];
417 const std::optional<Instruction> maybe_operator = node.nodeType() == NodeType::Symbol ? getOperator(node.string()) : std::nullopt;
418
419 enum class ShortcircuitOp
420 {
421 And,
422 Or
423 };
424 const std::optional<ShortcircuitOp> maybe_shortcircuit =
425 node.nodeType() == NodeType::Symbol
426 ? (node.string() == Language::And
427 ? std::make_optional(ShortcircuitOp::And)
428 : (node.string() == Language::Or
429 ? std::make_optional(ShortcircuitOp::Or)
430 : std::nullopt))
431 : std::nullopt;
432
433 if (maybe_shortcircuit.has_value())
434 {
435 // short circuit implementation
436
437 compileExpression(x.constList()[1], p, false, false);
438 page(p).emplace_back(DUP);
439
440 const auto label_shortcircuit = IR::Entity::Label();
441 for (std::size_t i = 2, end = x.constList().size(); i < end; ++i)
442 {
443 switch (maybe_shortcircuit.value())
444 {
445 case ShortcircuitOp::And:
446 page(p).emplace_back(IR::Entity::GotoIf(label_shortcircuit, false));
447 break;
448 case ShortcircuitOp::Or:
449 page(p).emplace_back(IR::Entity::GotoIf(label_shortcircuit, true));
450 break;
451 }
452 page(p).emplace_back(POP);
453
454 compileExpression(x.constList()[i], p, false, false);
455 if (i + 1 != end)
456 page(p).emplace_back(DUP);
457 }
458
459 page(p).emplace_back(label_shortcircuit);
460 }
461 else if (!maybe_operator.has_value())
462 {
463 if (is_terminal && x.constList()[0].nodeType() == NodeType::Symbol && var_name == x.constList()[0].string())
464 {
465 // push the arguments in reverse order
466 for (std::size_t i = x.constList().size() - 1; i >= start_index; --i)
467 {
468 if (nodeProducesOutput(x.constList()[i]))
469 compileExpression(x.constList()[i], p, false, false);
470 else
471 throwCompilerError(fmt::format("Invalid node inside tail call to `{}'", node.repr()), x);
472 }
473
474 // jump to the top of the function
475 page(p).emplace_back(JUMP, 0_u16);
476 return; // skip the potential Instruction::POP at the end
477 }
478 else
479 {
480 m_temp_pages.emplace_back();
481 const auto proc_page = Page { .index = m_temp_pages.size() - 1u, .is_temp = true };
482 // closure chains have been handled (eg: closure.field.field.function)
483 compileExpression(node, proc_page, false, false); // storing proc
484 if (m_temp_pages.back().empty())
485 throwCompilerError(fmt::format("Can not call {}", x.constList()[0].repr()), x);
486
487 // push arguments on current page
488 for (auto exp = x.constList().begin() + start_index, exp_end = x.constList().end(); exp != exp_end; ++exp)
489 {
490 if (nodeProducesOutput(*exp))
491 compileExpression(*exp, p, false, false);
492 else
493 throwCompilerError(fmt::format("Invalid node inside call to `{}'", node.repr()), x);
494 }
495 // push proc from temp page
496 for (const auto& inst : m_temp_pages.back())
497 page(p).push_back(inst);
498 m_temp_pages.pop_back();
499
500 // number of arguments
501 std::size_t args_count = 0;
502 for (auto it = x.constList().begin() + 1, it_end = x.constList().end(); it != it_end; ++it)
503 {
504 if (it->nodeType() != NodeType::Capture)
505 args_count++;
506 }
507 // call the procedure
508 page(p).emplace_back(CALL, args_count);
509 }
510 }
511 else // operator
512 {
513 // retrieve operator
514 auto op = maybe_operator.value();
515
516 if (op == ASSERT)
517 is_result_unused = false;
518
519 // push arguments on current page
520 std::size_t exp_count = 0;
521 for (std::size_t index = start_index, size = x.constList().size(); index < size; ++index)
522 {
523 if (nodeProducesOutput(x.constList()[index]))
524 compileExpression(x.constList()[index], p, false, false);
525 else
526 throwCompilerError(fmt::format("Invalid node inside call to operator `{}'", node.repr()), x);
527
528 if ((index + 1 < size && x.constList()[index + 1].nodeType() != NodeType::Capture) || index + 1 == size)
529 exp_count++;
530
531 // in order to be able to handle things like (op A B C D...)
532 // which should be transformed into A B op C op D op...
533 if (exp_count >= 2)
534 page(p).emplace_back(op);
535 }
536
537 if (isUnaryInst(op))
538 {
539 if (exp_count != 1)
540 throwCompilerError(fmt::format("Operator needs one argument, but was called with {}", exp_count), x.constList()[0]);
541 page(p).emplace_back(op);
542 }
543 else if (exp_count <= 1)
544 {
545 throwCompilerError(fmt::format("Operator needs two arguments, but was called with {}", exp_count), x.constList()[0]);
546 }
547
548 // need to check we didn't push the (op A B C D...) things for operators not supporting it
549 if (exp_count > 2)
550 {
551 switch (op)
552 {
553 // authorized instructions
554 case ADD: [[fallthrough]];
555 case SUB: [[fallthrough]];
556 case MUL: [[fallthrough]];
557 case DIV: [[fallthrough]];
558 case MOD:
559 break;
560
561 default:
563 fmt::format(
564 "can not create a chained expression (of length {}) for operator `{}'. You most likely forgot a `)'.",
565 exp_count,
566 Language::operators[static_cast<std::size_t>(op - FIRST_OPERATOR)]),
567 x);
568 }
569 }
570 }
571
572 if (is_result_unused)
573 page(p).emplace_back(POP);
574 }
575
576 uint16_t Compiler::addSymbol(const Node& sym)
577 {
578 // otherwise, add the symbol, and return its id in the table
579 auto it = std::ranges::find(m_symbols, sym.string());
580 if (it == m_symbols.end())
581 {
582 m_symbols.push_back(sym.string());
583 it = m_symbols.begin() + static_cast<std::vector<std::string>::difference_type>(m_symbols.size() - 1);
584 }
585
586 const auto distance = std::distance(m_symbols.begin(), it);
587 if (distance < std::numeric_limits<uint16_t>::max())
588 return static_cast<uint16_t>(distance);
589 throwCompilerError("Too many symbols (exceeds 65'536), aborting compilation.", sym);
590 }
591
592 uint16_t Compiler::addValue(const Node& x)
593 {
594 const ValTableElem v(x);
595 auto it = std::ranges::find(m_values, v);
596 if (it == m_values.end())
597 {
598 m_values.push_back(v);
599 it = m_values.begin() + static_cast<std::vector<ValTableElem>::difference_type>(m_values.size() - 1);
600 }
601
602 const auto distance = std::distance(m_values.begin(), it);
603 if (distance < std::numeric_limits<uint16_t>::max())
604 return static_cast<uint16_t>(distance);
605 throwCompilerError("Too many values (exceeds 65'536), aborting compilation.", x);
606 }
607
608 uint16_t Compiler::addValue(const std::size_t page_id, const Node& current)
609 {
610 const ValTableElem v(page_id);
611 auto it = std::ranges::find(m_values, v);
612 if (it == m_values.end())
613 {
614 m_values.push_back(v);
615 it = m_values.begin() + static_cast<std::vector<ValTableElem>::difference_type>(m_values.size() - 1);
616 }
617
618 const auto distance = std::distance(m_values.begin(), it);
619 if (distance < std::numeric_limits<uint16_t>::max())
620 return static_cast<uint16_t>(distance);
621 throwCompilerError("Too many values (exceeds 65'536), aborting compilation.", current);
622 }
623}
Lots of utilities about string, filesystem and more.
Host the declaration of all the ArkScript builtins.
ArkScript compiler is in charge of transforming the AST into bytecode.
Constants used by ArkScript.
User defined literals for Ark internals.
Handles the macros and their expansion in ArkScript source code.
static void compilerWarning(const std::string &message, const Node &node)
Display a warning message.
Definition Compiler.cpp:108
uint16_t addValue(const Node &x)
Register a given node in the value table.
Definition Compiler.cpp:592
std::vector< IR::Block > m_code_pages
Definition Compiler.hpp:83
std::vector< IR::Block > m_temp_pages
we need temporary code pages for some compilations passes
Definition Compiler.hpp:84
void compileExpression(const Node &x, Page p, bool is_result_unused, bool is_terminal, const std::string &var_name="")
Compile an expression (a node) recursively.
Definition Compiler.cpp:118
Compiler(unsigned debug)
Construct a new Compiler object.
Definition Compiler.cpp:21
static void throwCompilerError(const std::string &message, const Node &node)
Throw a nice error message.
Definition Compiler.cpp:113
void handleCalls(const Node &x, Page p, bool is_result_unused, bool is_terminal, const std::string &var_name)
Definition Compiler.cpp:412
void compilePluginImport(const Node &x, Page p)
Definition Compiler.cpp:394
void compileWhile(const Node &x, Page p)
Definition Compiler.cpp:371
void compileLetMutSet(Keyword n, const Node &x, Page p)
Definition Compiler.cpp:350
static bool isUnaryInst(Instruction inst) noexcept
Check if a given instruction is unary (takes only one argument)
Definition Compiler.cpp:88
const std::vector< ValTableElem > & values() const noexcept
Return the value table pre-computed.
Definition Compiler.cpp:47
static bool nodeProducesOutput(const Node &node)
Definition Compiler.cpp:79
std::vector< ValTableElem > m_values
Definition Compiler.hpp:82
void compileListInstruction(const Node &c0, const Node &x, Page p, bool is_result_unused)
Definition Compiler.cpp:229
IR::Block & page(const Page page) noexcept
helper functions to get a temp or finalized code page
Definition Compiler.hpp:94
const std::vector< std::string > & symbols() const noexcept
Return the symbol table pre-computed.
Definition Compiler.cpp:42
uint16_t addSymbol(const Node &sym)
Register a given node in the symbol table.
Definition Compiler.cpp:576
std::vector< std::string > m_symbols
Definition Compiler.hpp:81
void process(const Node &ast)
Start the compilation.
Definition Compiler.cpp:25
static std::optional< uint16_t > getBuiltin(const std::string &name) noexcept
Checking if a symbol is a builtin.
Definition Compiler.cpp:60
void compileIf(const Node &x, Page p, bool is_result_unused, bool is_terminal, const std::string &var_name)
Definition Compiler.cpp:280
static std::optional< Instruction > getOperator(const std::string &name) noexcept
Checking if a symbol is an operator.
Definition Compiler.cpp:52
static std::optional< Instruction > getListInstruction(const std::string &name) noexcept
Checking if a symbol is a list instruction.
Definition Compiler.cpp:71
void compileSymbol(const Node &x, Page p, bool is_result_unused)
Definition Compiler.cpp:211
void compileFunction(const Node &x, Page p, bool is_result_unused, const std::string &var_name)
Definition Compiler.cpp:305
const std::vector< IR::Block > & intermediateRepresentation() const noexcept
Return the IR blocks (one per scope)
Definition Compiler.cpp:37
static Entity Label()
Definition Entity.cpp:20
static Entity GotoIf(const Entity &label, bool cond)
Definition Entity.cpp:36
static Entity Goto(const Entity &label)
Definition Entity.cpp:28
A node of an Abstract Syntax Tree for ArkScript.
Definition Node.hpp:30
NodeType nodeType() const noexcept
Return the node type.
Definition Node.cpp:63
const std::string & filename() const noexcept
Return the filename in which this node was created.
Definition Node.cpp:120
const std::string & string() const noexcept
Return the string held by the value (if the node type allows it)
Definition Node.cpp:33
const std::vector< Node > & constList() const noexcept
Return the list of sub-nodes held by the node.
Definition Node.cpp:58
std::string repr() const noexcept
Compute a representation of the node without any comments or additional sugar, colors,...
Definition Node.cpp:135
std::size_t col() const noexcept
Get the column at which this node was created.
Definition Node.cpp:115
std::size_t line() const noexcept
Get the line at which this node was created.
Definition Node.cpp:110
ARK_API std::string makeContextWithNode(const std::string &message, const internal::Node &node)
Helper used by the compiler to generate a colorized context from a node.
ARK_API const std::vector< std::pair< std::string, Value > > builtins
constexpr std::array< std::string_view, 7 > listInstructions
Definition Common.hpp:95
constexpr std::string_view And
Definition Common.hpp:108
constexpr std::array< std::string_view, 23 > operators
Definition Common.hpp:126
constexpr std::string_view Or
Definition Common.hpp:109
std::string typeToString(const Node &node) noexcept
Definition Node.hpp:211
Keyword
The different keywords available.
Definition Common.hpp:58
Instruction
The different bytecodes are stored here.
CodeError thrown by the compiler (parser, macro processor, optimizer, and compiler itself)
A Compiler Value class helper to handle multiple types.