16 using namespace literals;
19 m_logger(
"ASTLowerer", debug)
30 Page { .index = 0, .is_temp =
false },
62 [&name](
const std::pair<std::string, Value>& element) ->
bool {
63 return name == element.first;
92 node.
constList().front().string() !=
"assert";
100 case NOT: [[fallthrough]];
101 case LEN: [[fallthrough]];
102 case EMPTY: [[fallthrough]];
103 case TAIL: [[fallthrough]];
104 case HEAD: [[fallthrough]];
105 case ISNIL: [[fallthrough]];
106 case TO_NUM: [[fallthrough]];
107 case TO_STR: [[fallthrough]];
159 if (!is_result_unused)
172 if (!is_result_unused)
174 static const std::optional<uint16_t> nil =
getBuiltin(
"nil");
187 compileIf(x, p, is_result_unused, is_terminal);
204 for (std::size_t i = 1, size = x.
list().size(); i < size; ++i)
209 (i != size - 1) || is_result_unused,
211 is_terminal && (i == size - 1));
238 "NodeType `{}' not handled in ASTLowerer::compileExpression. Please fill an issue on GitHub: https://github.com/ArkScript-lang/Ark",
245 const std::string& name = x.
string();
254 if (maybe_local_idx.has_value())
260 if (is_result_unused)
262 warning(
"Statement has no effect", x);
270 std::string name = x.
constList()[0].string();
274 const auto argc = x.
constList().size() - 1u;
276 if (argc < 2 &&
APPEND <= inst && inst <=
POP)
277 buildAndThrowError(fmt::format(
"Can not use {} with less than 2 arguments", name), head);
278 if (inst <=
POP && std::cmp_greater(argc, std::numeric_limits<uint16_t>::max()))
281 buildAndThrowError(fmt::format(
"Expected 3 arguments (list, index, value) for {}, got {}", name, argc), head);
283 buildAndThrowError(fmt::format(
"Expected 4 arguments (list, y, x, value) for {}, got {}", name, argc), head);
286 for (std::size_t i = x.
constList().size() - 1u; i > 0; --i)
296 std::size_t inst_argc = 0;
307 inst_argc = argc - 1;
318 page(p).emplace_back(inst,
static_cast<uint16_t
>(inst_argc));
319 page(p).back().setSourceLocation(head.filename(), head.line());
323 warning(
"Ignoring return value of function", x);
352 page(p).emplace_back(label_then);
359 page(p).emplace_back(label_end);
367 buildAndThrowError(
"Invalid node ; if it was computed by a macro, check that a node is returned", x);
370 std::size_t capture_inst_count = 0;
371 for (
const auto& node : x.
constList()[1].constList())
376 ++capture_inst_count;
379 const bool is_closure = capture_inst_count > 0;
388 const auto function_body_page =
Page { .index =
m_code_pages.size() - 1, .is_temp =
false };
393 for (
const auto& node : x.
constList()[1].constList())
414 page(function_body_page).emplace_back(
RET);
418 if (is_result_unused)
420 warning(
"Unused declared function", x);
430 buildAndThrowError(
"Invalid node ; if it was computed by a macro, check that a node is returned", x);
432 const std::string name = x.
constList()[1].string();
435 const bool is_function = x.
constList()[2].isFunction();
439 x.
list()[2].setFunctionKind(
false);
444 for (std::size_t idx = 2, end = x.
constList().size(); idx < end; ++idx)
463 buildAndThrowError(
"Invalid node ; if it was computed by a macro, check that a node is returned", x);
471 page(p).emplace_back(label_loop);
487 page(p).emplace_back(label_end);
497 for (std::size_t i = 0, end = package_node.
constList().size(); i < end; ++i)
499 path += package_node.
constList()[i].string();
523 for (
Node& value : std::ranges::drop_view(call.
list(), 1) | std::views::reverse)
531 message = fmt::format(
"Invalid node inside tail call to `{}'", node.repr());
533 message = fmt::format(
"Invalid node inside call to `{}'", node.repr());
541 constexpr std::size_t start_index = 1;
546 const std::optional<Instruction> maybe_shortcircuit =
555 if (maybe_shortcircuit.has_value())
561 "Expected at least 2 arguments while compiling '{}', got {}",
569 auto shortcircuit_entity =
IR::Entity::Goto(label_shortcircuit, maybe_shortcircuit.value());
570 page(p).emplace_back(shortcircuit_entity);
572 for (std::size_t i = 2, end = x.
constList().size(); i < end; ++i)
576 page(p).emplace_back(shortcircuit_entity);
579 page(p).emplace_back(label_shortcircuit);
581 else if (!maybe_operator.has_value())
595 const auto proc_page =
Page { .index =
m_temp_pages.size() - 1u, .is_temp =
true };
622 page(p).push_back(inst);
626 std::size_t args_count = 0;
627 for (
auto it = x.
constList().begin() + start_index, it_end = x.
constList().end(); it != it_end; ++it)
633 page(p).emplace_back(
CALL, args_count);
637 page(p).emplace_back(label_return);
643 auto op = maybe_operator.value();
646 is_result_unused =
false;
649 std::size_t exp_count = 0;
650 for (std::size_t index = start_index, size = x.
constList().size(); index < size; ++index)
663 page(p).emplace_back(op);
670 page(p).emplace_back(op);
676 page(p).emplace_back(op);
678 else if (exp_count <= 1)
689 case ADD: [[fallthrough]];
690 case SUB: [[fallthrough]];
691 case MUL: [[fallthrough]];
692 case DIV: [[fallthrough]];
693 case MOD: [[fallthrough]];
700 "`{}' requires 2 arguments, but got {}.",
708 if (is_result_unused)
719 it =
m_symbols.begin() +
static_cast<std::vector<std::string>::difference_type
>(
m_symbols.size() - 1);
722 const auto distance = std::distance(
m_symbols.begin(), it);
723 if (distance < std::numeric_limits<uint16_t>::max())
724 return static_cast<uint16_t
>(distance);
731 auto it = std::ranges::find(
m_values, v);
735 it =
m_values.begin() +
static_cast<std::vector<ValTableElem>::difference_type
>(
m_values.size() - 1);
738 const auto distance = std::distance(
m_values.begin(), it);
739 if (distance < std::numeric_limits<uint16_t>::max())
740 return static_cast<uint16_t
>(distance);
747 auto it = std::ranges::find(
m_values, v);
751 it =
m_values.begin() +
static_cast<std::vector<ValTableElem>::difference_type
>(
m_values.size() - 1);
754 const auto distance = std::distance(
m_values.begin(), it);
755 if (distance < std::numeric_limits<uint16_t>::max())
756 return static_cast<uint16_t
>(distance);
757 buildAndThrowError(
"Too many values (exceeds 65'536), aborting compilation.", current);
Host the declaration of all the ArkScript builtins.
User defined literals for Ark internals.
const std::string & string() const
uint16_t addValue(const Node &x)
Register a given node in the value table.
void pushFunctionCallArguments(Node &call, Page p, bool is_tail_call)
uint16_t addSymbol(const Node &sym)
Register a given node in the symbol table.
static std::optional< Instruction > getListInstruction(const std::string &name) noexcept
Checking if a symbol is a list instruction.
std::vector< ValTableElem > m_values
void compileListInstruction(Node &x, Page p, bool is_result_unused)
static bool nodeProducesOutput(const Node &node)
std::vector< IR::Block > m_temp_pages
we need temporary code pages for some compilations passes
void compileLetMutSet(Keyword n, Node &x, Page p)
void handleCalls(Node &x, Page p, bool is_result_unused, bool is_terminal)
const std::vector< ValTableElem > & values() const noexcept
Return the value table pre-computed.
void compileIf(Node &x, Page p, bool is_result_unused, bool is_terminal)
void process(Node &ast)
Start the compilation.
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
std::vector< IR::Block > m_code_pages
ASTLowerer(unsigned debug)
Construct a new ASTLowerer object.
void compileWhile(Node &x, Page p)
void compilePluginImport(Node &x, Page p)
std::vector< std::string > m_symbols
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)
void compileExpression(Node &x, Page p, bool is_result_unused, bool is_terminal)
Compile an expression (a node) recursively.
LocalsLocator m_locals_locator
std::stack< std::string > m_opened_vars
stack of vars we are currently declaring
void compileSymbol(Node &x, Page p, bool is_result_unused)
static void warning(const std::string &message, const Node &node)
Display a warning message.
void compileFunction(Node &x, Page p, bool is_result_unused)
static std::optional< uint16_t > getBuiltin(const std::string &name) noexcept
Checking if a symbol is a builtin.
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 Goto(const Entity &label, Instruction inst=Instruction::JUMP)
static Entity Label(label_t value)
static Entity GotoIf(const Entity &label, bool cond)
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.
bool isAnonymousFunction() const noexcept
Check if a node is an anonymous function.
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.
std::vector< Node > & list() noexcept
Return the list of sub-nodes held by the node.
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::array UpdateRef
All the builtins that modify in place a variable.
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.