16 m_debug(debug), m_options(options)
20 { std::make_shared<SymbolExecutor>(
this),
21 std::make_shared<ConditionalExecutor>(
this),
22 std::make_shared<FunctionExecutor>(
this) });
24 m_predefined_macros = {
33 std::cout <<
"Processing macros...\n";
41 std::cout <<
"(MacroProcessor) AST after processing macros\n";
42 std::cout <<
m_ast <<
'\n';
65 if (first_node.
string() !=
"undef")
82 bool had_spread =
false;
101 else if (std::size_t size = node.
constList().size(); size == 3 || size == 4)
135 bool has_created =
false;
144 while (i < node.
list().size())
156 bool removed_begin =
false;
163 removed_begin =
true;
173 bool added_begin =
false;
222 if (
auto p = map.find(target.
string()); p != map.end())
227 for (std::size_t i = 0, end = target.
list().size(); i < end; ++i)
228 unify(map, target.
list()[i], &target, i);
232 Node subnode = target;
234 unify(map, subnode, parent);
239 for (std::size_t i = 1, end = subnode.
list().size(); i < end; ++i)
240 parent->
list().insert(parent->
list().begin() + index + i, subnode.
list()[i]);
241 parent->
list().erase(parent->
list().begin() + index);
250 if (macro !=
nullptr && macro->
constList().size() == 2)
257#define GEN_NOT_BODY(str_name, error_handler, ret) \
258 else if (name == str_name && is_not_body) \
260 if (node.list().size() != 3) error_handler; \
261 Node one = evaluate(node.list()[1], is_not_body), \
262 two = evaluate(node.list()[2], is_not_body); \
266#define GEN_COMPARATOR(str_name, cond) GEN_NOT_BODY( \
268 throwMacroProcessingError("Interpreting a `" str_name "' condition with " + \
269 std::to_string(node.list().size() - 1) + " arguments, instead of 2.", \
271 (cond) ? Node::getTrueNode() : Node::getFalseNode())
273#define GEN_OP(str_name, op) GEN_NOT_BODY( \
275 throwMacroProcessingError("Interpreting a `" str_name "' operation with " + \
276 std::to_string(node.list().size() - 1) + " arguments, instead of 2.", \
278 (one.nodeType() == two.nodeType() && two.nodeType() == NodeType::Number) ? Node(one.number() op two.number()) : node)
280 const std::string& name = node.
list()[0].string();
297 else if (name ==
"not" && is_not_body)
299 if (node.
list().size() != 2)
300 throwMacroProcessingError(
"Interpreting a `not' condition with " + std::to_string(node.
list().size() - 1) +
" arguments, instead of 1.", node);
304 else if (name ==
"and" && is_not_body)
306 if (node.
list().size() < 3)
307 throwMacroProcessingError(
"Interpreting a `and' chain with " + std::to_string(node.
list().size() - 1) +
" arguments, expected at least 2.", node);
309 for (std::size_t i = 1, end = node.
list().size(); i < end; ++i)
316 else if (name ==
"or" && is_not_body)
318 if (node.
list().size() < 3)
319 throwMacroProcessingError(
"Interpreting a `or' chain with " + std::to_string(node.
list().size() - 1) +
" arguments, expected at least 2.", node);
321 for (std::size_t i = 1, end = node.
list().size(); i < end; ++i)
328 else if (name ==
"len")
330 if (node.
list().size() > 2)
331 throwMacroProcessingError(
"When expanding `len' inside a macro, got " + std::to_string(node.
list().size() - 1) +
" arguments, needed only 1", node);
337 node =
Node(
static_cast<long>(lst.list().size()) - 1);
339 node =
Node(
static_cast<long>(lst.list().size()));
343 else if (name ==
"@")
345 if (node.
list().size() != 3)
353 long size =
static_cast<long>(sublist.
list().size());
354 long real_size = size;
355 long num_idx =
static_cast<long>(idx.
number());
364 num_idx = num_idx >= 0 ? num_idx : size + num_idx;
367 return sublist.
list()[num_idx];
369 throwMacroProcessingError(
"Index (" + std::to_string(
static_cast<long>(idx.
number())) +
") out of range (list size: " + std::to_string(real_size) +
")", node);
372 else if (name ==
"head")
374 if (node.
list().size() > 2)
375 throwMacroProcessingError(
"When expanding `head' inside a macro, got " + std::to_string(node.
list().size() - 1) +
" arguments, needed only 1", node);
389 else if (sublist.
list().size() > 0)
395 else if (name ==
"tail")
397 if (node.
list().size() > 2)
398 throwMacroProcessingError(
"When expanding `tail' inside a macro, got " + std::to_string(node.
list().size() - 1) +
" arguments, needed only 1", node);
404 if (sublist.
list().size() > 1)
415 else if (sublist.
list().size() > 0)
427 else if (name ==
"symcat")
429 if (node.
list().size() <= 2)
430 throwMacroProcessingError(
"When expanding `symcat', expected at least 2 arguments, got " + std::to_string(node.
list().size() - 1) +
" arguments", node);
434 std::string sym = node.
list()[1].string();
436 for (std::size_t i = 2, end = node.
list().size(); i < end; ++i)
443 sym += std::to_string(
static_cast<long int>(ev.
number()));
459 else if (name ==
"argcount")
465 node =
Node(
static_cast<long>(it->second.constList().size()));
470 node =
Node(
static_cast<long>(sym.
list()[1].list().size()));
478 for (std::size_t i = 0; i < node.
list().size(); ++i)
489 if (node.
string() ==
"true")
491 else if (node.
string() ==
"false" || node.
string() ==
"nil")
510 if (
auto res = it->has(name); res !=
nullptr)
523 if (it->remove(name))
547 for (std::size_t i = 0; i < node.
list().size(); ++i)
569 std::size_t previous = i;
571 for (std::size_t block_idx = 1, end = lst.
constList().size(); block_idx < end; ++block_idx)
572 node.
list().insert(node.
constList().begin() + i + block_idx, lst.
list()[block_idx]);
588 [&node](
const std::pair<std::string, Value>& element) ->
bool {
589 return node.
string() == element.first;
600 return isConstEval(node);
Host the declaration of all the ArkScript builtins.
Executor for Conditional Macros.
ArkScript homemade exceptions.
Executor for List Macros.
#define GEN_OP(str_name, op)
#define GEN_COMPARATOR(str_name, cond)
Handles the macros and their expansion in ArkScript source code.
Executor for Symbol Macros.
MacroProcessingError thrown by the compiler.
The class that initializes the MacroExecutors.
bool applyMacro(Node &node)
Passes node through all MacroExecutors sequentially.
void removeBegin(Node &node, std::size_t &i)
Remove a begin block added by a macro.
MacroProcessor(unsigned debug, uint16_t options) noexcept
Construct a new Macro Processor object.
void process(Node &node, unsigned depth)
Register macros in scopes and apply them as needed.
void unify(const std::unordered_map< std::string, Node > &map, Node &target, Node *parent, std::size_t index=0)
Unify a target node with a given map symbol => replacement node, recursively.
const Node & ast() const noexcept
Return the modified AST.
MacroExecutorPipeline m_executor_pipeline
void recurApply(Node &node)
Recursively apply macros on a given node.
std::vector< std::string > m_predefined_macros
Already existing macros, non-keywords, non-builtins.
void registerMacro(Node &node)
Registers macros based on their type.
Node evaluate(Node &node, bool is_not_body=false)
Evaluate only the macros.
const Node * findNearestMacro(const std::string &name) const
Find the nearest macro matching a given name.
Node m_ast
The modified AST.
bool isTruthy(const Node &node)
Check if a node can be evaluated to true.
bool hadBegin(const Node &node)
Check if a given node is a list node, and starts with a Begin.
void throwMacroProcessingError(const std::string &message, const Node &node)
Throw a macro processing error.
std::unordered_map< std::string, Node > m_defined_functions
bool isConstEval(const Node &node) const
Check if a node can be evaluated at compile time.
bool isPredefined(const std::string &symbol)
Check if a given symbol is a predefined macro or not.
bool applyMacro(Node &node)
Apply a macro on a given node.
void feed(const Node &ast)
Send the complete AST (after the inclusions and stuff), and work on it.
std::vector< MacroScope > m_macros
Handling macros in a scope fashion.
void registerFuncDef(Node &node)
Registers a function definition node.
unsigned m_debug
The debug level.
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.
static const Node & getListNode()
Provide a statically initialized / correct and guaranteed to be initialized Node representing "Empty ...
void setNodeType(NodeType type) noexcept
Set the Node Type object.
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)
static const Node & getNilNode()
Provide a statically initialized / correct and guaranteed to be initialized Node representing "Nil".
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.
static const Node & getTrueNode()
Provide a statically initialized / correct and guaranteed to be initialized Node representing "true".
double number() const noexcept
Return the number held by the value (if the node type allows it)
static const Node & getFalseNode()
Provide a statically initialized / correct and guaranteed to be initialized Node representing "false"...
std::vector< Node > & list() noexcept
Return the list of sub-nodes held by the node.
Create string error context for AST errors.
const std::vector< std::pair< std::string, Value > > builtins
std::string typeToString(const Node &node) noexcept
constexpr std::array< std::string_view, 11 > nodeTypes
constexpr std::array< std::string_view, 25 > operators
std::string makeNodeBasedErrorCtx(const std::string &message, const Node &node)
Construct an error message based on a given node.
Keyword
The different keywords available.