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);
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.position().start.line);
323 warning(
"Ignoring return value of function", x);
331 buildAndThrowError(
"Invalid condition: missing 'cond' and 'then' nodes, expected (if cond then)", x);
357 page(p).emplace_back(label_then);
364 page(p).emplace_back(label_end);
372 buildAndThrowError(
"Invalid node ; if it was computed by a macro, check that a node is returned", x);
375 std::size_t capture_inst_count = 0;
376 for (
const auto& node : x.
constList()[1].constList())
380 const uint16_t symbol_id =
addSymbol(node);
384 if (
const auto& maybe_nqn = node.getUnqualifiedName(); maybe_nqn.has_value() && maybe_nqn.value() != node.string())
394 ++capture_inst_count;
397 const bool is_closure = capture_inst_count > 0;
406 const auto function_body_page =
Page { .index =
m_code_pages.size() - 1, .is_temp =
false };
411 for (
const auto& node : x.
constList()[1].constList())
432 page(function_body_page).emplace_back(
RET);
436 if (is_result_unused)
438 warning(
"Unused declared function", x);
448 buildAndThrowError(
"Invalid node ; if it was computed by a macro, check that a node is returned", x);
450 const std::string name = x.
constList()[1].string();
454 buildAndThrowError(
"Can not define a variable using the same name as the function it is defined inside", x);
456 const bool is_function = x.
constList()[2].isFunction();
460 x.
list()[2].setFunctionKind(
false);
465 for (std::size_t idx = 2, end = x.
constList().size(); idx < end; ++idx)
484 buildAndThrowError(
"Invalid node ; if it was computed by a macro, check that a node is returned", x);
492 page(p).emplace_back(label_loop);
508 page(p).emplace_back(label_end);
518 for (std::size_t i = 0, end = package_node.
constList().size(); i < end; ++i)
520 path += package_node.
constList()[i].string();
544 for (
Node& value : std::ranges::drop_view(call.
list(), 1) | std::views::reverse)
552 message = fmt::format(
"Invalid node inside tail call to `{}'", node.repr());
554 message = fmt::format(
"Invalid node inside call to `{}'", node.repr());
562 constexpr std::size_t start_index = 1;
567 const std::optional<Instruction> maybe_shortcircuit =
576 if (maybe_shortcircuit.has_value())
582 "Expected at least 2 arguments while compiling '{}', got {}",
590 auto shortcircuit_entity =
IR::Entity::Goto(label_shortcircuit, maybe_shortcircuit.value());
591 page(p).emplace_back(shortcircuit_entity);
593 for (std::size_t i = 2, end = x.
constList().size(); i < end; ++i)
597 page(p).emplace_back(shortcircuit_entity);
600 page(p).emplace_back(label_shortcircuit);
602 else if (!maybe_operator.has_value())
619 const auto proc_page =
Page { .index =
m_temp_pages.size() - 1u, .is_temp =
true };
646 page(p).push_back(inst);
650 std::size_t args_count = 0;
651 for (
auto it = x.
constList().begin() + start_index, it_end = x.
constList().end(); it != it_end; ++it)
657 page(p).emplace_back(
CALL, args_count);
661 page(p).emplace_back(label_return);
667 auto op = maybe_operator.value();
670 is_result_unused =
false;
673 std::size_t exp_count = 0;
674 for (std::size_t index = start_index, size = x.
constList().size(); index < size; ++index)
687 page(p).emplace_back(op);
694 page(p).emplace_back(op);
700 page(p).emplace_back(op);
702 else if (exp_count <= 1)
713 case ADD: [[fallthrough]];
714 case SUB: [[fallthrough]];
715 case MUL: [[fallthrough]];
716 case DIV: [[fallthrough]];
717 case MOD: [[fallthrough]];
724 "`{}' requires 2 arguments, but got {}.",
732 if (is_result_unused)
743 it =
m_symbols.begin() +
static_cast<std::vector<std::string>::difference_type
>(
m_symbols.size() - 1);
746 const auto distance = std::distance(
m_symbols.begin(), it);
748 return static_cast<uint16_t
>(distance);
755 auto it = std::ranges::find(
m_values, v);
759 it =
m_values.begin() +
static_cast<std::vector<ValTableElem>::difference_type
>(
m_values.size() - 1);
762 const auto distance = std::distance(
m_values.begin(), it);
764 return static_cast<uint16_t
>(distance);
771 auto it = std::ranges::find(
m_values, v);
775 it =
m_values.begin() +
static_cast<std::vector<ValTableElem>::difference_type
>(
m_values.size() - 1);
778 const auto distance = std::distance(
m_values.begin(), it);
780 return static_cast<uint16_t
>(distance);
Host the declaration of all the ArkScript builtins.
Tools to report code errors nicely to the user.
ArkScript homemade exceptions.
User defined literals for Ark internals.
const String_t & 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)
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(const 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.
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 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,...
FileSpan position() const noexcept
Get the span of the node (start and end)
const Namespace & constArkNamespace() const noexcept
Return the namespace held by the value (if the node type allows it)
std::vector< Node > & list() noexcept
Return the list of sub-nodes held by the node.
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.
constexpr uint16_t MaxValue16Bits
CodeError thrown by the compiler (parser, macro processor, optimizer, and compiler itself)
std::size_t line
0-indexed line number
std::shared_ptr< Node > ast
A Compiler Value class helper to handle multiple types.