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