19 using namespace literals;
32 Page { .index = 0, .is_temp =
false },
63 [&name](
const std::pair<std::string, Value>& element) ->
bool {
64 return name == element.first;
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]];
138 if (!is_result_unused)
144 if (!is_result_unused)
146 static const std::optional<uint16_t> nil =
getBuiltin(
"nil");
159 compileIf(x, p, is_result_unused, is_terminal, var_name);
176 for (std::size_t i = 1, size = x.
constList().size(); i < size; ++i)
181 (i != size - 1) || is_result_unused,
183 is_terminal && (i == size - 1),
205 handleCalls(x, p, is_result_unused, is_terminal, var_name);
213 const std::string& name = x.
string();
222 if (is_result_unused)
231 std::string name = c0.
string();
235 const auto argc = x.
constList().size() - 1u;
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()))
243 for (std::size_t i = x.
constList().size() - 1u; i > 0; --i)
253 std::size_t inst_argc;
264 inst_argc = argc - 1;
271 page(p).emplace_back(inst,
static_cast<uint16_t
>(inst_argc));
273 if (is_result_unused && name.back() !=
'!')
298 page(p).emplace_back(label_then);
302 page(p).emplace_back(label_end);
310 throwCompilerError(
"Invalid node ; if it was computed by a macro, check that a node is returned", x);
313 bool is_closure =
false;
314 for (
const auto& node : x.
constList()[1].constList())
325 const auto function_body_page =
Page { .index =
m_code_pages.size() - 1, .is_temp =
false };
330 for (
const auto& node : x.
constList()[1].constList())
340 page(function_body_page).emplace_back(
RET);
343 if (is_result_unused)
355 throwCompilerError(
"Invalid node ; if it was computed by a macro, check that a node is returned", x);
357 const std::string name = x.
constList()[1].string();
362 for (std::size_t idx = 2, end = x.
constList().size(); idx < end; ++idx)
374 throwCompilerError(
"Invalid node ; if it was computed by a macro, check that a node is returned", x);
378 page(p).emplace_back(label_loop);
391 page(p).emplace_back(label_end);
398 for (std::size_t i = 0, end = package_node.
constList().size(); i < end; ++i)
400 path += package_node.
constList()[i].string();
414 constexpr std::size_t start_index = 1;
419 enum class ShortcircuitOp
424 const std::optional<ShortcircuitOp> maybe_shortcircuit =
427 ? std::make_optional(ShortcircuitOp::And)
429 ? std::make_optional(ShortcircuitOp::Or)
433 if (maybe_shortcircuit.has_value())
441 for (std::size_t i = 2, end = x.
constList().size(); i < end; ++i)
443 switch (maybe_shortcircuit.value())
445 case ShortcircuitOp::And:
448 case ShortcircuitOp::Or:
459 page(p).emplace_back(label_shortcircuit);
461 else if (!maybe_operator.has_value())
466 for (std::size_t i = x.
constList().size() - 1; i >= start_index; --i)
471 throwCompilerError(fmt::format(
"Invalid node inside tail call to `{}'", node.repr()), x);
481 const auto proc_page =
Page { .index =
m_temp_pages.size() - 1u, .is_temp =
true };
488 for (
auto exp = x.
constList().begin() + start_index, exp_end = x.
constList().end(); exp != exp_end; ++exp)
497 page(p).push_back(inst);
501 std::size_t args_count = 0;
502 for (
auto it = x.
constList().begin() + 1, it_end = x.
constList().end(); it != it_end; ++it)
508 page(p).emplace_back(
CALL, args_count);
514 auto op = maybe_operator.value();
517 is_result_unused =
false;
520 std::size_t exp_count = 0;
521 for (std::size_t index = start_index, size = x.
constList().size(); index < size; ++index)
526 throwCompilerError(fmt::format(
"Invalid node inside call to operator `{}'", node.repr()), x);
534 page(p).emplace_back(op);
541 page(p).emplace_back(op);
543 else if (exp_count <= 1)
554 case ADD: [[fallthrough]];
555 case SUB: [[fallthrough]];
556 case MUL: [[fallthrough]];
557 case DIV: [[fallthrough]];
564 "can not create a chained expression (of length {}) for operator `{}'. You most likely forgot a `)'.",
572 if (is_result_unused)
583 it =
m_symbols.begin() +
static_cast<std::vector<std::string>::difference_type
>(
m_symbols.size() - 1);
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);
595 auto it = std::ranges::find(
m_values, v);
599 it =
m_values.begin() +
static_cast<std::vector<ValTableElem>::difference_type
>(
m_values.size() - 1);
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);
611 auto it = std::ranges::find(
m_values, v);
615 it =
m_values.begin() +
static_cast<std::vector<ValTableElem>::difference_type
>(
m_values.size() - 1);
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);
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.
uint16_t addValue(const Node &x)
Register a given node in the value table.
std::vector< IR::Block > m_code_pages
std::vector< IR::Block > m_temp_pages
we need temporary code pages for some compilations passes
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.
Compiler(unsigned debug)
Construct a new Compiler object.
static void throwCompilerError(const std::string &message, const Node &node)
Throw a nice error message.
void handleCalls(const Node &x, Page p, bool is_result_unused, bool is_terminal, const std::string &var_name)
void compilePluginImport(const Node &x, Page p)
void compileWhile(const Node &x, Page p)
void compileLetMutSet(Keyword n, const Node &x, Page p)
static bool isUnaryInst(Instruction inst) noexcept
Check if a given instruction is unary (takes only one argument)
const std::vector< ValTableElem > & values() const noexcept
Return the value table pre-computed.
static bool nodeProducesOutput(const Node &node)
std::vector< ValTableElem > m_values
void compileListInstruction(const Node &c0, const Node &x, Page p, bool is_result_unused)
IR::Block & page(const Page page) noexcept
helper functions to get a temp or finalized code page
const std::vector< std::string > & symbols() const noexcept
Return the symbol table pre-computed.
uint16_t addSymbol(const Node &sym)
Register a given node in the symbol table.
std::vector< std::string > m_symbols
void process(const Node &ast)
Start the compilation.
static std::optional< uint16_t > getBuiltin(const std::string &name) noexcept
Checking if a symbol is a builtin.
void compileIf(const Node &x, Page p, bool is_result_unused, bool is_terminal, const std::string &var_name)
static std::optional< Instruction > getOperator(const std::string &name) noexcept
Checking if a symbol is an operator.
static std::optional< Instruction > getListInstruction(const std::string &name) noexcept
Checking if a symbol is a list instruction.
void compileSymbol(const Node &x, Page p, bool is_result_unused)
void compileFunction(const Node &x, Page p, bool is_result_unused, const std::string &var_name)
const std::vector< IR::Block > & intermediateRepresentation() const noexcept
Return the IR blocks (one per scope)
static Entity GotoIf(const Entity &label, bool cond)
static Entity Goto(const Entity &label)
A node of an Abstract Syntax Tree for ArkScript.
NodeType nodeType() const noexcept
Return the node type.
const std::string & filename() const noexcept
Return the filename in which this node was created.
const std::string & string() const noexcept
Return the string held by the value (if the node type allows it)
const std::vector< Node > & constList() const noexcept
Return the list of sub-nodes held by the node.
std::string repr() const noexcept
Compute a representation of the node without any comments or additional sugar, colors,...
std::size_t col() const noexcept
Get the column at which this node was created.
std::size_t line() const noexcept
Get the line at which this node was created.
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
constexpr std::string_view And
constexpr std::array< std::string_view, 23 > operators
constexpr std::string_view Or
std::string typeToString(const Node &node) noexcept
Keyword
The different keywords available.
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.