21 Pass(
"MacroProcessor", debug)
24 m_conditional_executor = std::make_shared<ConditionalExecutor>(
this);
25 m_executors.emplace_back(std::make_shared<SymbolExecutor>(
this));
26 m_executors.emplace_back(m_conditional_executor);
27 m_executors.emplace_back(std::make_shared<FunctionExecutor>(
this));
54 assert(node.
constList().size() >= 2 &&
"Invalid macro, missing value");
56 const Node& first_node = node.
list()[0];
112 "Max recursion depth reached ({}). You most likely have a badly defined recursive macro calling itself without a proper exit condition",
118 bool has_created =
false;
120 while (i < node.
list().size())
122 const std::size_t pos = i;
125 bool added_begin =
false;
130 if ((!
m_macros.empty() && !
m_macros.back().empty() &&
m_macros.back().depth() < depth && !is_processing_namespace) ||
131 (!has_created && !is_processing_namespace) ||
132 (
m_macros.empty() && is_processing_namespace))
149 node.
list().erase(node.
constList().begin() +
static_cast<std::vector<Node>::difference_type
>(pos));
150 else if (!added_begin)
174 node.
list().erase(node.
constList().begin() +
static_cast<std::vector<Node>::difference_type
>(pos));
179 if (!
m_macros.empty() &&
m_macros.back().depth() == depth && !is_processing_namespace)
198 "Max macro processing depth reached ({}). You may have a macro trying to evaluate itself, try splitting your code in multiple nodes.",
204 if (executor->canHandle(node))
207 const bool applied = executor->applyMacro(node, depth);
219 const std::size_t argcount = node.
constList().size();
220 if (argcount != expected + 1)
225 "When expanding `{}' inside a macro, got {} argument{}, expected {}",
228 argcount > 2 ?
"s" :
"",
234 "Interpreting a `{}'{} with {} argument{}, expected {}.",
236 kind.empty() ? kind :
" " + kind,
238 argcount > 2 ?
"s" :
"",
246 const std::size_t argcount = node.
constList().size();
247 if (argcount < expected + 1)
250 "Interpreting a `{}'{} with {} argument{}, expected at least {}.",
252 kind.empty() ? kind :
" " + kind,
254 argcount > 2 ?
"s" :
"",
264 if (macro !=
nullptr && macro->
constList().size() == 2)
270 const std::string& name = node.
list()[0].string();
271 const std::size_t argcount = node.
list().size() - 1;
279 else if (name ==
"=" && is_not_body)
286 else if (name ==
"!=" && is_not_body)
293 else if (name ==
"<" && is_not_body)
300 else if (name ==
">" && is_not_body)
307 else if (name ==
"<=" && is_not_body)
314 else if (name ==
">=" && is_not_body)
321 else if (name ==
"+" && is_not_body)
325 for (
auto& child : node.
list() | std::ranges::views::drop(1))
334 else if (name ==
"-" && is_not_body)
342 for (
auto& child : node.
list() | std::ranges::views::drop(2))
351 else if (name ==
"*" && is_not_body)
355 for (
auto& child : node.
list() | std::ranges::views::drop(1))
364 else if (name ==
"/" && is_not_body)
372 for (
auto& child : node.
list() | std::ranges::views::drop(2))
381 else if (name ==
"not" && is_not_body)
388 if (node.
list().size() < 3)
391 for (std::size_t i = 1, end = node.
list().size(); i < end; ++i)
400 if (node.
list().size() < 3)
403 for (std::size_t i = 1, end = node.
list().size(); i < end; ++i)
410 else if (name ==
"$len")
421 else if (name ==
"$empty?")
428 if (!lst.list().empty() && lst.list()[0] ==
getListNode())
436 else if (name ==
"$at")
446 const std::size_t size = sublist.
list().size();
447 std::size_t real_size = size;
448 long num_idx =
static_cast<long>(idx.
number());
459 if (num_idx >= 0 && std::cmp_less(num_idx, size))
460 output = sublist.
list()[
static_cast<std::size_t
>(num_idx)];
461 else if (
const auto c =
static_cast<long>(size) + num_idx; num_idx < 0 && std::cmp_less(c, size) && c >= 0)
462 output = sublist.
list()[
static_cast<std::size_t
>(c)];
469 else if (name ==
"$head")
485 else if (!sublist.
list().empty())
490 else if (name ==
"$tail")
498 if (sublist.
list().size() > 1)
509 else if (!sublist.
list().empty())
523 if (node.
list().size() <= 2)
527 std::string sym = node.
list()[1].string();
529 for (std::size_t i = 2, end = node.
list().size(); i < end; ++i)
548 "When expanding `{}', expected either a Number, String or Symbol, got a {}: {}",
585 if (node.
list().size() != 2)
591 if (node.
list().size() != 2)
604 "When expanding `{}', got a {}. Can not un-define a macro without a valid name",
610 if (node.
list().size() != 2)
620 for (
auto& child : node.
list())
621 child.updateValueAndType(
evaluate(child, depth + 1, is_not_body));
634 if (node.
string() ==
"true")
658 for (
const auto& m_macro : std::ranges::reverse_view(
m_macros))
660 if (
const auto res = m_macro.has(name); res !=
nullptr)
671 for (
auto& m_macro : std::ranges::reverse_view(
m_macros))
673 if (m_macro.remove(name))
698 const std::size_t previous = i;
700 for (std::size_t block_idx = 1, end = lst.
constList().size(); block_idx < end; ++block_idx)
702 node.
constList().begin() +
static_cast<std::vector<Node>::difference_type
>(i + block_idx),
703 lst.
list()[block_idx]);
705 node.
list().erase(node.
constList().begin() +
static_cast<std::vector<Node>::difference_type
>(previous));
712 const std::optional<CodeErrorContext> maybe_context = [
this]() -> std::optional<CodeErrorContext> {
732 "When expanding `{}', expected '{}' to be a {}, got a {}: {}",
735 std::string(
nodeTypes[
static_cast<std::size_t
>(expected)]),
Host the declaration of all the ArkScript builtins.
Executor for Conditional Macros.
Constants used by ArkScript.
ArkScript homemade exceptions.
The base class for all MacroExecutors.
Executor for List Macros.
Handles the macros and their expansion in ArkScript source code.
Executor for Symbol Macros.
void debug(const Logger::MessageAndLocation &data, Args &&... args)
Write a debug level log using fmtlib.
void trace(const char *fmt, Args &&... args)
Write a trace level log using fmtlib.
void traceStart(std::string &&trace_name)
void checkMacroArgCountGe(const Node &node, std::size_t expected, const std::string &name, const std::string &kind="") const
Check if the given node has at least the provided argument count, otherwise throws an error.
Node evaluate(Node &node, unsigned depth, bool is_not_body=false)
Evaluate only the macros.
void registerFuncDef(const Node &node)
Registers a function definition node.
std::shared_ptr< MacroExecutor > m_conditional_executor
const Node & ast() const noexcept
Return the modified AST.
bool applyMacro(Node &node, unsigned depth)
Apply a macro on a given node.
static bool isBeginNode(const Node &node)
Check if a given node is a list node, and starts with a Begin.
static void removeBegin(Node &node, std::size_t i)
Remove a begin block added by a macro.
std::optional< Node > lookupDefinedFunction(const std::string &name) const
Return std::nullopt if the function isn't registered, otherwise return its node.
std::vector< Node > m_macros_being_applied
Stack of macros being applied. The last one is the current one we are working on.
const Node * findNearestMacro(const std::string &name) const
Find the nearest macro matching a given name.
Node m_ast
The modified AST.
void throwMacroProcessingError(const std::string &message, const Node &node) const
Throw a macro processing error.
void checkMacroTypeError(const std::string ¯o, const std::string &arg, NodeType expected, const Node &actual) const
void handleMacroNode(Node &node, unsigned depth)
Registers macros based on their type, expand conditional macros.
std::unordered_map< std::string, Node > m_defined_functions
std::vector< std::shared_ptr< MacroExecutor > > m_executors
void checkMacroArgCountEq(const Node &node, std::size_t expected, const std::string &name, bool is_expansion=false, const std::string &kind="") const
Check if the given node has exactly the provided argument count, otherwise throws an error.
MacroProcessor(unsigned debug) noexcept
Construct a new Macro Processor object.
void processNode(Node &node, unsigned depth, bool is_processing_namespace=false)
Register macros in scopes and apply them as needed.
bool isTruthy(const Node &node) const
Check if a node can be evaluated to true.
void process(const Node &ast)
Send the complete AST and work on it.
std::vector< MacroScope > m_macros
Handling macros in a scope fashion.
void deleteNearestMacro(const std::string &name)
Find the nearest macro matching a given name and delete it.
A node of an Abstract Syntax Tree for ArkScript.
NodeType nodeType() const noexcept
Return the node type.
void setNodeType(NodeType type) noexcept
Set the Node Type object.
bool isListLike() const noexcept
Check if the node is a list like node.
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.
Keyword keyword() const noexcept
Return the keyword held by the value (if the node type allows it)
Namespace & arkNamespace() noexcept
Return the namespace held by the value (if the node type allows it)
std::string repr() const noexcept
Compute a representation of the node without any comments or additional sugar, colors,...
void setPositionFrom(const Node &source) noexcept
Position the current node at a given span in a file.
std::ostream & debugPrint(std::ostream &os) const noexcept
Print a node to an output stream with added type annotations.
FileSpan position() const noexcept
Get the span of the node (start and end)
void push_back(const Node &node) noexcept
Every node has a list as well as a value so we can push_back on all node no matter their type.
void setString(const std::string &value) noexcept
Set the String object.
double number() const noexcept
Return the number held by the value (if the node type allows it)
void updateValueAndType(const Node &source) noexcept
Copy a node to the current one, while keeping the filename and position in the file.
std::vector< Node > & list() noexcept
Return the list of sub-nodes held by the node.
An interface to describe compiler passes.
constexpr std::string_view AsIs
constexpr std::string_view Type
constexpr std::string_view Argcount
constexpr std::string_view And
constexpr std::string_view Repr
constexpr std::string_view Or
constexpr std::string_view Symcat
constexpr std::string_view Undef
std::string typeToString(const Node &node) noexcept
NodeType
The different node types available.
const Node & getNilNode()
Keyword
The different keywords available.
constexpr std::array< std::string_view, 13 > nodeTypes
Node types as string, in the same order as the enum NodeType.
const Node & getFalseNode()
const Node & getListNode()
const Node & getTrueNode()
constexpr std::size_t MaxMacroProcessingDepth
Controls the number of recursive calls to MacroProcessor::processNode.
std::string to_string(const Ark::ValueType type) noexcept
CodeError thrown by the compiler (parser, macro processor, optimizer, and compiler itself)
std::shared_ptr< Node > ast