14 using namespace literals;
17 m_logger(
"ASTLowerer", debug)
28 Page { .index = 0, .is_temp =
false },
60 [&name](
const std::pair<std::string, Value>& element) ->
bool {
61 return name == element.first;
89 case NOT: [[fallthrough]];
90 case LEN: [[fallthrough]];
91 case EMPTY: [[fallthrough]];
92 case TAIL: [[fallthrough]];
93 case HEAD: [[fallthrough]];
94 case ISNIL: [[fallthrough]];
95 case TO_NUM: [[fallthrough]];
96 case TO_STR: [[fallthrough]];
148 if (!is_result_unused)
161 if (!is_result_unused)
163 static const std::optional<uint16_t> nil =
getBuiltin(
"nil");
176 compileIf(x, p, is_result_unused, is_terminal, var_name);
193 for (std::size_t i = 1, size = x.
constList().size(); i < size; ++i)
198 (i != size - 1) || is_result_unused,
200 is_terminal && (i == size - 1),
223 handleCalls(x, p, is_result_unused, is_terminal, var_name);
228 "NodeType `{}' not handled in ASTLowerer::compileExpression. Please fill an issue on GitHub: https://github.com/ArkScript-lang/Ark",
235 const std::string& name = x.
string();
244 if (maybe_local_idx.has_value())
250 if (is_result_unused)
252 warning(
"Statement has no effect", x);
259 std::string name = c0.
string();
263 const auto argc = x.
constList().size() - 1u;
265 if (argc < 2 &&
APPEND <= inst && inst <=
POP)
266 buildAndThrowError(fmt::format(
"Can not use {} with less than 2 arguments", name), c0);
267 if (inst <=
POP && std::cmp_greater(argc, std::numeric_limits<uint16_t>::max()))
270 buildAndThrowError(fmt::format(
"Expected 3 arguments (list, index, value) for {}, got {}", name, argc), c0);
272 buildAndThrowError(fmt::format(
"Expected 4 arguments (list, y, x, value) for {}, got {}", name, argc), c0);
275 for (std::size_t i = x.
constList().size() - 1u; i > 0; --i)
285 std::size_t inst_argc = 0;
296 inst_argc = argc - 1;
307 page(p).emplace_back(inst,
static_cast<uint16_t
>(inst_argc));
312 warning(
"Ignoring return value of function", x);
341 page(p).emplace_back(label_then);
348 page(p).emplace_back(label_end);
356 buildAndThrowError(
"Invalid node ; if it was computed by a macro, check that a node is returned", x);
359 std::size_t capture_inst_count = 0;
360 for (
const auto& node : x.
constList()[1].constList())
365 ++capture_inst_count;
368 const bool is_closure = capture_inst_count > 0;
377 const auto function_body_page =
Page { .index =
m_code_pages.size() - 1, .is_temp =
false };
382 for (
const auto& node : x.
constList()[1].constList())
395 page(function_body_page).emplace_back(
RET);
399 if (is_result_unused)
401 warning(
"Unused declared function", x);
411 buildAndThrowError(
"Invalid node ; if it was computed by a macro, check that a node is returned", x);
413 const std::string name = x.
constList()[1].string();
418 for (std::size_t idx = 2, end = x.
constList().size(); idx < end; ++idx)
435 buildAndThrowError(
"Invalid node ; if it was computed by a macro, check that a node is returned", x);
443 page(p).emplace_back(label_loop);
460 page(p).emplace_back(label_end);
470 for (std::size_t i = 0, end = package_node.
constList().size(); i < end; ++i)
472 path += package_node.
constList()[i].string();
487 constexpr std::size_t start_index = 1;
492 enum class ShortcircuitOp
497 const std::optional<ShortcircuitOp> maybe_shortcircuit =
500 ? std::make_optional(ShortcircuitOp::And)
502 ? std::make_optional(ShortcircuitOp::Or)
506 if (maybe_shortcircuit.has_value())
512 "Expected at least 2 arguments while compiling '{}', got {}",
521 for (std::size_t i = 2, end = x.
constList().size(); i < end; ++i)
523 switch (maybe_shortcircuit.value())
525 case ShortcircuitOp::And:
528 case ShortcircuitOp::Or:
539 page(p).emplace_back(label_shortcircuit);
541 else if (!maybe_operator.has_value())
546 for (std::size_t i = x.
constList().size() - 1; i >= start_index; --i)
551 buildAndThrowError(fmt::format(
"Invalid node inside tail call to `{}'", node.repr()), x);
556 page(p).back().setSourceLocation(node.filename(), node.line());
562 const auto proc_page =
Page { .index =
m_temp_pages.size() - 1u, .is_temp =
true };
569 for (
auto exp = x.
constList().begin() + start_index, exp_end = x.
constList().end(); exp != exp_end; ++exp)
578 page(p).push_back(inst);
582 std::size_t args_count = 0;
583 for (
auto it = x.
constList().begin() + 1, it_end = x.
constList().end(); it != it_end; ++it)
589 page(p).emplace_back(
CALL, args_count);
590 page(p).back().setSourceLocation(node.filename(), node.line());
596 auto op = maybe_operator.value();
599 is_result_unused =
false;
602 std::size_t exp_count = 0;
603 for (std::size_t index = start_index, size = x.
constList().size(); index < size; ++index)
608 buildAndThrowError(fmt::format(
"Invalid node inside call to operator `{}'", node.repr()), x);
616 page(p).emplace_back(op);
623 page(p).emplace_back(op);
629 page(p).emplace_back(op);
631 else if (exp_count <= 1)
642 case ADD: [[fallthrough]];
643 case SUB: [[fallthrough]];
644 case MUL: [[fallthrough]];
645 case DIV: [[fallthrough]];
646 case MOD: [[fallthrough]];
653 "`{}' requires 2 arguments, but got {}.",
661 if (is_result_unused)
672 it =
m_symbols.begin() +
static_cast<std::vector<std::string>::difference_type
>(
m_symbols.size() - 1);
675 const auto distance = std::distance(
m_symbols.begin(), it);
676 if (distance < std::numeric_limits<uint16_t>::max())
677 return static_cast<uint16_t
>(distance);
684 auto it = std::ranges::find(
m_values, v);
688 it =
m_values.begin() +
static_cast<std::vector<ValTableElem>::difference_type
>(
m_values.size() - 1);
691 const auto distance = std::distance(
m_values.begin(), it);
692 if (distance < std::numeric_limits<uint16_t>::max())
693 return static_cast<uint16_t
>(distance);
700 auto it = std::ranges::find(
m_values, v);
704 it =
m_values.begin() +
static_cast<std::vector<ValTableElem>::difference_type
>(
m_values.size() - 1);
707 const auto distance = std::distance(
m_values.begin(), it);
708 if (distance < std::numeric_limits<uint16_t>::max())
709 return static_cast<uint16_t
>(distance);
710 buildAndThrowError(
"Too many values (exceeds 65'536), aborting compilation.", current);
Host the declaration of all the ArkScript builtins.
User defined literals for Ark internals.
uint16_t addValue(const Node &x)
Register a given node in the value table.
uint16_t addSymbol(const Node &sym)
Register a given node in the symbol table.
void handleCalls(const Node &x, Page p, bool is_result_unused, bool is_terminal, const std::string &var_name)
static std::optional< Instruction > getListInstruction(const std::string &name) noexcept
Checking if a symbol is a list instruction.
std::vector< ValTableElem > m_values
void compileWhile(const Node &x, Page p)
static bool nodeProducesOutput(const Node &node)
std::vector< IR::Block > m_temp_pages
we need temporary code pages for some compilations passes
const std::vector< ValTableElem > & values() const noexcept
Return the value table pre-computed.
const std::vector< IR::Block > & intermediateRepresentation() const noexcept
Return the IR blocks (one per scope)
static bool isUnaryInst(Instruction inst) noexcept
Check if a given instruction is unary (takes only one argument)
IR::label_t m_current_label
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.
std::vector< IR::Block > m_code_pages
void compileLetMutSet(Keyword n, const Node &x, Page p)
ASTLowerer(unsigned debug)
Construct a new ASTLowerer object.
std::vector< std::string > m_symbols
void compileFunction(const Node &x, Page p, bool is_result_unused, const std::string &var_name)
void compileListInstruction(const Node &c0, const Node &x, Page p, bool is_result_unused)
static void buildAndThrowError(const std::string &message, const Node &node)
Throw a nice error message.
static bool isTernaryInst(Instruction inst) noexcept
Check if a given instruction is ternary (takes three arguments)
LocalsLocator m_locals_locator
void compileSymbol(const Node &x, Page p, bool is_result_unused)
void compileIf(const Node &x, Page p, bool is_result_unused, bool is_terminal, const std::string &var_name)
static void warning(const std::string &message, const Node &node)
Display a warning message.
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 compilePluginImport(const Node &x, Page p)
static std::optional< Instruction > getOperator(const std::string &name) noexcept
Checking if a symbol is an operator.
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.
static Entity Label(label_t value)
static Entity GotoIf(const Entity &label, bool cond)
static Entity Goto(const Entity &label)
void saveScopeLengthForBranch()
Save the current scope length before entering a branch, so that we can ignore variable definitions in...
std::optional< std::size_t > lookupLastScopeByName(const std::string &name)
Search for a local in the current scope. Returns std::nullopt in case of closure scopes or if the var...
void dropVarsForBranch()
Drop potentially defined variables in the last saved branch.
void deleteScope()
Delete the last scope.
void addLocal(const std::string &name)
Register a local in the current scope, triggered by a STORE instruction. If the local already exists,...
void createScope(ScopeType type=ScopeType::Default)
Create a new scope.
void traceStart(std::string &&trace_name)
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,...
const Namespace & constArkNamespace() const noexcept
Return the namespace held by the value (if the node type allows it)
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, 9 > listInstructions
constexpr std::array< std::string_view, 24 > operators
constexpr std::string_view And
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)
std::shared_ptr< Node > ast
A Compiler Value class helper to handle multiple types.