diff --git a/include/Ark/Compiler/AST/Node.hpp b/include/Ark/Compiler/AST/Node.hpp index d1e08fb03..c70163986 100644 --- a/include/Ark/Compiler/AST/Node.hpp +++ b/include/Ark/Compiler/AST/Node.hpp @@ -96,6 +96,13 @@ namespace Ark::internal */ [[nodiscard]] bool isListLike() const noexcept; + /** + * @brief Copy a node to the current one, while keeping the filename and position in the file + * + * @param source node to copy type and value from + */ + void updateValueAndType(const Node& source) noexcept; + /** * @brief Set the Node Type object * diff --git a/include/Ark/Compiler/Macros/Executor.hpp b/include/Ark/Compiler/Macros/Executor.hpp index b3c5005a0..14ea3fd59 100644 --- a/include/Ark/Compiler/Macros/Executor.hpp +++ b/include/Ark/Compiler/Macros/Executor.hpp @@ -2,7 +2,7 @@ * @file Executor.hpp * @author Ray John Alovera (rakista112@gmail.com), Alexandre Plateau (lexplt.dev@gmail.com) * @brief The base class for all MacroExecutors - * @version 2.0 + * @version 3.0 * @date 2024-03-03 * * @copyright Copyright (c) 2021-2024 @@ -63,14 +63,6 @@ namespace Ark::internal unsigned int m_debug; MacroProcessor* m_processor; ///< This is a non-owned pointer. - /** - * @brief Set the file attributes of the output node based on the original node - * @param origin original node - * @param output output node which will be modified - * @param macro modified node, macro applied - */ - void setWithFileAttributes(const Node origin, Node& output, const Node& macro); - /** * @brief Find the nearest macro matching a giving name * @@ -82,13 +74,22 @@ namespace Ark::internal [[nodiscard]] const Node* findNearestMacro(const std::string& name) const; /** - * @brief Registers macros based on their type + * @brief Apply a macro on a given node + * @details Proxy function for MacroProcessor::applyMacro + * + * @param node + * @param depth + */ + void applyMacroProxy(Node& node, unsigned depth); + + /** + * @brief Registers macros based on their type, expand conditional macros * @details Validate macros and register them by their name - * Proxy function for MacroProcessor::registerMacro + * Proxy function for MacroProcessor::handleMacroNode * * @param node A node of type Macro */ - void registerMacro(Node& node) const; + void handleMacroNode(Node& node) const; /** * @brief Check if a node can be evaluated to true @@ -111,12 +112,6 @@ namespace Ark::internal */ Node evaluate(Node& node, unsigned depth, bool is_not_body) const; - /** - * @brief Applies the spread operator - * @details Proxy function for MacroProcessor::unify - */ - void unify(const std::unordered_map&, Node&, Node*) const; - /** * @brief Throw a macro processing error * @details Proxy function for MacroProcessor::throwMacroProcessingError @@ -125,17 +120,6 @@ namespace Ark::internal * @param node the node in which there is an error */ [[noreturn]] static void throwMacroProcessingError(const std::string& message, const Node& node); - - /** - * @brief Execute a node, trying to emplace macros calls - * @details Proxy function for MacroProcessor::applyMacro - * - * @param node - * @param depth - * @return true - * @return false - */ - bool applyMacroProxy(Node& node, unsigned depth) const; }; } diff --git a/include/Ark/Compiler/Macros/Executors/Function.hpp b/include/Ark/Compiler/Macros/Executors/Function.hpp index e7184c9df..55c81da34 100644 --- a/include/Ark/Compiler/Macros/Executors/Function.hpp +++ b/include/Ark/Compiler/Macros/Executors/Function.hpp @@ -2,7 +2,7 @@ * @file Function.hpp * @author Ray John Alovera (rakista112@gmail.com), Alexandre Plateau (lexplt.dev@gmail.com) * @brief Executor for List Macros - * @version 2.1 + * @version 3.0 * @date 2021-05-04 * * @copyright Copyright (c) 2021-2024 @@ -40,6 +40,9 @@ namespace Ark::internal * @return true if the executor can handle the given node */ [[nodiscard]] bool canHandle(Node& node) override; + + private: + void unify(const std::unordered_map& map, Node& target, Node* parent, std::size_t index = 0, std::size_t unify_depth = 0); }; } diff --git a/include/Ark/Compiler/Macros/Processor.hpp b/include/Ark/Compiler/Macros/Processor.hpp index 4fae3a06f..2ffe3542c 100644 --- a/include/Ark/Compiler/Macros/Processor.hpp +++ b/include/Ark/Compiler/Macros/Processor.hpp @@ -2,7 +2,7 @@ * @file Processor.hpp * @author Alexandre Plateau (lexplt.dev@gmail.com) * @brief Handles the macros and their expansion in ArkScript source code - * @version 2.1 + * @version 3.0 * @date 2021-02-18 * * @copyright Copyright (c) 2021-2024 @@ -57,6 +57,7 @@ namespace Ark::internal private: Node m_ast; ///< The modified AST std::vector m_macros; ///< Handling macros in a scope fashion + std::shared_ptr m_conditional_executor; std::vector> m_executors; std::unordered_map m_defined_functions; @@ -83,13 +84,6 @@ namespace Ark::internal */ void deleteNearestMacro(const std::string& name); - /** - * @brief Recursively apply macros on a given node - * - * @param node - */ - void recurApply(Node& node); - /** * @brief Check if a given node is a list node, and starts with a Begin * @@ -97,7 +91,7 @@ namespace Ark::internal * @return true if it starts with a Begin * @return false */ - static bool hadBegin(const Node& node); + static bool isBeginNode(const Node& node); /** * @brief Remove a begin block added by a macro @@ -117,12 +111,12 @@ namespace Ark::internal [[nodiscard]] bool isConstEval(const Node& node) const; /** - * @brief Registers macros based on their type + * @brief Registers macros based on their type, expand conditional macros * @details Validate macros and register them by their name * * @param node A node of type Macro */ - void registerMacro(Node& node); + void handleMacroNode(Node& node); /** * @brief Registers a function definition node @@ -149,25 +143,6 @@ namespace Ark::internal */ bool applyMacro(Node& node, unsigned depth); - /** - * @brief Unify a target node with a given map symbol => replacement node, recursively - * - * @param map - * @param target - * @param parent - * @param index position of target inside parent->list() - * @param unify_depth call depth to unify, to avoid deep recursive unification - */ - void unify(const std::unordered_map& map, Node& target, Node* parent, std::size_t index, std::size_t unify_depth = 0); - - /** - * @brief Set the file attributes of the output node based on the original node - * @param origin original node - * @param output output node which will be modified - * @param macro modified node, macro applied - */ - void setWithFileAttributes(const Node origin, Node& output, const Node& macro); - /** * @brief Check if the given node has exactly the provided argument count, otherwise throws an error * diff --git a/src/arkreactor/Compiler/AST/Node.cpp b/src/arkreactor/Compiler/AST/Node.cpp index be9d1039e..5a4415390 100644 --- a/src/arkreactor/Compiler/AST/Node.cpp +++ b/src/arkreactor/Compiler/AST/Node.cpp @@ -70,6 +70,12 @@ namespace Ark::internal return m_type == NodeType::List || m_type == NodeType::Macro; } + void Node::updateValueAndType(const Node& source) noexcept + { + m_type = source.m_type; + m_value = source.m_value; + } + void Node::setNodeType(const NodeType type) noexcept { m_type = type; diff --git a/src/arkreactor/Compiler/AST/Optimizer.cpp b/src/arkreactor/Compiler/AST/Optimizer.cpp index 99e395284..2a1f5a388 100644 --- a/src/arkreactor/Compiler/AST/Optimizer.cpp +++ b/src/arkreactor/Compiler/AST/Optimizer.cpp @@ -10,7 +10,7 @@ namespace Ark::internal { m_logger.traceStart("process"); m_ast = ast; - // FIXME activate this removeUnused(); + // todo: activate this removeUnused(); m_logger.traceEnd(); } diff --git a/src/arkreactor/Compiler/Compiler.cpp b/src/arkreactor/Compiler/Compiler.cpp index 5cf9a0ee3..3168b3fac 100644 --- a/src/arkreactor/Compiler/Compiler.cpp +++ b/src/arkreactor/Compiler/Compiler.cpp @@ -207,7 +207,11 @@ namespace Ark::internal handleCalls(x, p, is_result_unused, is_terminal, var_name); } else - throwCompilerError("boop", x); // FIXME + throwCompilerError( + fmt::format( + "NodeType `{}' not handled in Compiler::compileExpression. Please fill an issue on GitHub: https://github.com/ArkScript-lang/Ark", + typeToString(x)), + x); } void Compiler::compileSymbol(const Node& x, const Page p, const bool is_result_unused) diff --git a/src/arkreactor/Compiler/Macros/Executor.cpp b/src/arkreactor/Compiler/Macros/Executor.cpp index c3c23ad81..4065d40e6 100644 --- a/src/arkreactor/Compiler/Macros/Executor.cpp +++ b/src/arkreactor/Compiler/Macros/Executor.cpp @@ -9,21 +9,19 @@ namespace Ark::internal m_processor(processor) {} - void MacroExecutor::setWithFileAttributes(const Node origin, Node& output, const Node& macro) + const Node* MacroExecutor::findNearestMacro(const std::string& name) const { - output = macro; - output.setFilename(origin.filename()); - output.setPos(origin.line(), origin.col()); + return m_processor->findNearestMacro(name); } - const Node* MacroExecutor::findNearestMacro(const std::string& name) const + void MacroExecutor::applyMacroProxy(Node& node, unsigned depth) { - return m_processor->findNearestMacro(name); + m_processor->applyMacro(node, depth); } - void MacroExecutor::registerMacro(Node& node) const + void MacroExecutor::handleMacroNode(Node& node) const { - m_processor->registerMacro(node); + m_processor->handleMacroNode(node); } bool MacroExecutor::isTruthy(const Node& node) const @@ -36,18 +34,8 @@ namespace Ark::internal return m_processor->evaluate(node, depth, is_not_body); } - void MacroExecutor::unify(const std::unordered_map& map, Node& target, Node* parent) const - { - m_processor->unify(map, target, parent, /* index= */ 0, /* unify_depth= */ 0); - } - void MacroExecutor::throwMacroProcessingError(const std::string& message, const Node& node) { MacroProcessor::throwMacroProcessingError(message, node); } - - bool MacroExecutor::applyMacroProxy(Node& node, const unsigned depth) const - { - return m_processor->applyMacro(node, depth); - } } diff --git a/src/arkreactor/Compiler/Macros/Executors/Conditional.cpp b/src/arkreactor/Compiler/Macros/Executors/Conditional.cpp index 057928122..b25cc443e 100644 --- a/src/arkreactor/Compiler/Macros/Executors/Conditional.cpp +++ b/src/arkreactor/Compiler/Macros/Executors/Conditional.cpp @@ -11,9 +11,9 @@ namespace Ark::internal // evaluate cond if (isTruthy(temp)) - setWithFileAttributes(node, node, if_true); + node.updateValueAndType(if_true); else if (node.constList().size() > 3) - setWithFileAttributes(node, node, if_false); + node.updateValueAndType(if_false); else { // remove node because nothing matched @@ -22,7 +22,7 @@ namespace Ark::internal } if (node.nodeType() == NodeType::Macro) - registerMacro(node); + handleMacroNode(node); return true; } diff --git a/src/arkreactor/Compiler/Macros/Executors/Function.cpp b/src/arkreactor/Compiler/Macros/Executors/Function.cpp index 146b93d1f..926eaffd1 100644 --- a/src/arkreactor/Compiler/Macros/Executors/Function.cpp +++ b/src/arkreactor/Compiler/Macros/Executors/Function.cpp @@ -1,8 +1,11 @@ +#include #include #include #include +#include + namespace Ark::internal { bool FunctionExecutor::canHandle(Node& node) @@ -12,77 +15,114 @@ namespace Ark::internal bool FunctionExecutor::applyMacro(Node& node, const unsigned depth) { - Node& first = node.list()[0]; + const Node& first = node.list()[0]; - if (const Node* macro = findNearestMacro(first.string()); macro != nullptr) + // ($ name (args) body) + if (const Node* macro = findNearestMacro(first.string()); macro != nullptr && macro->constList().size() == 3) { - if (macro->constList().size() == 2) - applyMacroProxy(first, depth + 1); - // ($ name (args) body) - else if (macro->constList().size() == 3) + Node temp_body = macro->constList()[2]; + Node args = macro->constList()[1]; + const std::size_t args_needed = args.list().size(); + const std::size_t args_given = node.constList().size() - 1; // remove the first (the name of the macro) + const std::string macro_name = macro->constList()[0].string(); + // thanks to the parser, we are guaranted that the spread will be in last position, if any + const bool has_spread = args_needed > 0 && args.list().back().nodeType() == NodeType::Spread; + + // save the args given to the macro by giving them a name (from the macro args block), + // and a value (in nocde.constList()) + std::unordered_map args_applied; + std::size_t j = 0; + for (std::size_t i = 1, end = node.constList().size(); i < end; ++i) { - Node temp_body = macro->constList()[2]; - Node args = macro->constList()[1]; - std::size_t args_needed = args.list().size(); - std::size_t args_given = node.constList().size() - 1; // remove the first (the name of the macro) - std::string macro_name = macro->constList()[0].string(); - const bool has_spread = args_needed > 0 && args.list().back().nodeType() == NodeType::Spread; - - // bind node->list() to temp_body using macro->constList()[1] - std::unordered_map args_applied; - std::size_t j = 0; - for (std::size_t i = 1, end = node.constList().size(); i < end; ++i) - { - // by breaking early if we have too many arguments, the args_applied/args_needed check will fail - if (j >= args_needed) - break; + // by breaking early if we have too many arguments, the args_applied/args_needed check will fail + if (j >= args_needed) + break; - const std::string& arg_name = args.list()[j].string(); - if (args.list()[j].nodeType() == NodeType::Symbol) - { - args_applied[arg_name] = node.constList()[i]; - ++j; - } - else if (args.list()[j].nodeType() == NodeType::Spread) + const std::string& arg_name = args.list()[j].string(); + if (args.list()[j].nodeType() == NodeType::Symbol) + { + args_applied[arg_name] = node.constList()[i]; + ++j; + } + else if (args.list()[j].nodeType() == NodeType::Spread) + { + if (!args_applied.contains(arg_name)) { - if (!args_applied.contains(arg_name)) - { - args_applied[arg_name] = Node(NodeType::List); - args_applied[arg_name].push_back(getListNode()); - } - // do not move j because we checked before that the spread is always the last one - args_applied[arg_name].push_back(node.constList()[i]); + args_applied[arg_name] = Node(NodeType::List); + args_applied[arg_name].push_back(getListNode()); } + // do not move j because we checked before that the spread is always the last one + args_applied[arg_name].push_back(node.constList()[i]); } + } - // check argument count - if (args_applied.size() + 1 == args_needed && has_spread) - { - // just a spread we didn't assign - args_applied[args.list().back().string()] = Node(NodeType::List); - args_applied[args.list().back().string()].push_back(getListNode()); - } + // check argument count + if (args_applied.size() + 1 == args_needed && has_spread) + { + // just a spread we didn't assign + args_applied[args.list().back().string()] = Node(NodeType::List); + args_applied[args.list().back().string()].push_back(getListNode()); + } - if (args_given != args_needed && !has_spread) - throwMacroProcessingError(fmt::format("Macro `{}' got {} argument(s) but needed {}", macro_name, args_given, args_needed), node); - if (args_applied.size() != args_needed && has_spread) - // args_needed - 1 because we do not count the spread as a required argument - throwMacroProcessingError(fmt::format("Macro `{}' got {} argument(s) but needed at least {}", macro_name, args_applied.size(), args_needed - 1), node); + if (args_given != args_needed && !has_spread) + throwMacroProcessingError(fmt::format("Macro `{}' got {} argument(s) but needed {}", macro_name, args_given, args_needed), node); + if (args_applied.size() != args_needed && has_spread) + // args_needed - 1 because we do not count the spread as a required argument + throwMacroProcessingError(fmt::format("Macro `{}' got {} argument(s) but needed at least {}", macro_name, args_applied.size(), args_needed - 1), node); - if (!args_applied.empty()) - unify(args_applied, temp_body, nullptr); + if (!args_applied.empty()) + unify(args_applied, temp_body, nullptr); - setWithFileAttributes(node, node, evaluate(temp_body, depth + 1, false)); - applyMacroProxy(node, depth + 1); // todo: this seems useless - return true; - } + node.updateValueAndType(evaluate(temp_body, depth + 1, false)); + applyMacroProxy(node, depth + 1); + return true; } - else if (std::ranges::find(Language::macros, first.string()) != Language::macros.end()) + + if (std::ranges::find(Language::macros, first.string()) != Language::macros.end()) { - setWithFileAttributes(node, node, evaluate(node, depth + 1, false)); + node.updateValueAndType(evaluate(node, depth + 1, false)); return true; } return false; } + + void FunctionExecutor::unify(const std::unordered_map& map, Node& target, Node* parent, const std::size_t index, const std::size_t unify_depth) + { + if (unify_depth > MaxMacroUnificationDepth) + throwMacroProcessingError( + fmt::format( + "Max macro unification depth reached ({}). You may have a macro trying to evaluate itself, try splitting your code in multiple nodes.", + MaxMacroUnificationDepth), + *parent); + + if (target.nodeType() == NodeType::Symbol) + { + if (const auto p = map.find(target.string()); p != map.end()) + target = p->second; + } + else if (target.isListLike()) + { + for (std::size_t i = 0; i < target.list().size(); ++i) + unify(map, target.list()[i], &target, i, unify_depth + 1); + } + else if (target.nodeType() == NodeType::Spread) + { + assert(parent != nullptr && "Parent node should be defined when unifying a spread"); + + Node sub_node = target; + sub_node.setNodeType(NodeType::Symbol); + unify(map, sub_node, parent, 0, unify_depth + 1); + + if (sub_node.nodeType() != NodeType::List) + throwMacroProcessingError(fmt::format("Can not unify a {} to a Spread", typeToString(sub_node)), sub_node); + + for (std::size_t i = 1, end = sub_node.list().size(); i < end; ++i) + parent->list().insert( + parent->list().begin() + static_cast::difference_type>(index + i), + sub_node.list()[i]); + // remove the spread + parent->list().erase(parent->list().begin() + static_cast::difference_type>(index)); + } + } } diff --git a/src/arkreactor/Compiler/Macros/Executors/Symbol.cpp b/src/arkreactor/Compiler/Macros/Executors/Symbol.cpp index 9db8a3f8c..7f8cc4da5 100644 --- a/src/arkreactor/Compiler/Macros/Executors/Symbol.cpp +++ b/src/arkreactor/Compiler/Macros/Executors/Symbol.cpp @@ -14,8 +14,9 @@ namespace Ark::internal // ($ name value) if (macro->constList().size() == 2) { - setWithFileAttributes(node, node, macro->constList()[1]); + node.updateValueAndType(macro->constList()[1]); evaluate(node, depth + 1, false); + applyMacroProxy(node, depth + 1); return true; } } diff --git a/src/arkreactor/Compiler/Macros/Processor.cpp b/src/arkreactor/Compiler/Macros/Processor.cpp index c4ff91921..16ba637a2 100644 --- a/src/arkreactor/Compiler/Macros/Processor.cpp +++ b/src/arkreactor/Compiler/Macros/Processor.cpp @@ -21,8 +21,9 @@ namespace Ark::internal Pass("MacroProcessor", debug) { // create executors pipeline + m_conditional_executor = std::make_shared(this); m_executors.emplace_back(std::make_shared(this)); - m_executors.emplace_back(std::make_shared(this)); + m_executors.emplace_back(m_conditional_executor); m_executors.emplace_back(std::make_shared(this)); } @@ -46,7 +47,7 @@ namespace Ark::internal return m_ast; } - void MacroProcessor::registerMacro(Node& node) + void MacroProcessor::handleMacroNode(Node& node) { // a macro needs at least 2 nodes, name + value is the minimal form // this is guaranted by the parser @@ -66,8 +67,12 @@ namespace Ark::internal assert(node.list()[1].nodeType() == NodeType::List && "Invalid macro argument's list"); m_macros.back().add(first_node.string(), node); } + // in case we had a conditional, we need to evaluate and expand it + else if (m_conditional_executor->canHandle(node)) + m_conditional_executor->applyMacro(node, 0); } + // todo find a better way to do this void MacroProcessor::registerFuncDef(const Node& node) { if (node.nodeType() == NodeType::List && node.constList().size() == 3 && node.constList()[0].nodeType() == NodeType::Keyword) @@ -108,7 +113,11 @@ namespace Ark::internal std::size_t i = 0; while (i < node.list().size()) { - if (node.list()[i].nodeType() == NodeType::Macro) + const std::size_t pos = i; + const bool had_begin = isBeginNode(node.list()[pos]); + bool added_begin = false; + + if (node.list()[pos].nodeType() == NodeType::Macro) { // create a scope only if needed if ((!m_macros.empty() && !m_macros.back().empty() && m_macros.back().depth() < depth) || !has_created) @@ -117,46 +126,41 @@ namespace Ark::internal m_macros.emplace_back(depth); } - const bool had = hadBegin(node.list()[i]); - - // register the macro we encountered - registerMacro(node.list()[i]); - recurApply(node.list()[i]); - - // if we now have a surrounding (begin ...) and didn't have one before, remove it - if (hadBegin(node.list()[i]) && !had) - removeBegin(node, i); - // if there is an unused node or a leftover macro need, we need to get rid of it in the final ast - else if (node.list()[i].nodeType() == NodeType::Macro || node.list()[i].nodeType() == NodeType::Unused) - node.list().erase(node.constList().begin() + static_cast::difference_type>(i)); + handleMacroNode(node.list()[pos]); + added_begin = isBeginNode(node.list()[pos]) && !had_begin; } else // running on non-macros { - bool added_begin = false; + applyMacro(node.list()[pos], 0); + added_begin = isBeginNode(node.list()[pos]) && !had_begin; - const bool had = hadBegin(node.list()[i]); - applyMacro(node.list()[i], 0); - recurApply(node.list()[i]); - - if (hadBegin(node.list()[i]) && !had) - added_begin = true; - else if (node.list()[i].nodeType() == NodeType::Unused) - node.list().erase(node.constList().begin() + static_cast::difference_type>(i)); - - if (node.nodeType() == NodeType::List && i < node.constList().size()) + if (node.list()[pos].nodeType() == NodeType::Unused) + node.list().erase(node.constList().begin() + static_cast::difference_type>(pos)); + else + // Go forward only if it isn't a macro, because we delete macros + // while running on the AST. Also, applying a macro can result in + // nodes being marked unused, and delete them immediately. When + // that happens, we can't increment i, otherwise we delete a node, + // advance, resulting in a node being skipped! + ++i; + + // process subnodes if any + if (node.nodeType() == NodeType::List && pos < node.constList().size()) { - processNode(node.list()[i], depth + 1); + processNode(node.list()[pos], depth + 1); // needed if we created a function node from a macro - registerFuncDef(node.list()[i]); + registerFuncDef(node.list()[pos]); } + } - // remove begins in macros - if (added_begin && i < node.constList().size()) - removeBegin(node, i); - - // go forward only if it isn't a macro, because we delete macros - // while running on the AST - ++i; + if (pos < node.constList().size()) + { + // if we now have a surrounding (begin ...) and didn't have one before, remove it + if (added_begin) + removeBegin(node, pos); + // if there is an unused node or a leftover macro need, we need to get rid of it in the final ast + else if (node.list()[pos].nodeType() == NodeType::Macro || node.list()[pos].nodeType() == NodeType::Unused) + node.list().erase(node.constList().begin() + static_cast::difference_type>(pos)); } } @@ -186,49 +190,6 @@ namespace Ark::internal return false; } - void MacroProcessor::unify(const std::unordered_map& map, Node& target, Node* parent, const std::size_t index, const std::size_t unify_depth) - { - if (unify_depth > MaxMacroUnificationDepth) - throwMacroProcessingError( - fmt::format( - "Max macro unification depth reached ({}). You may have a macro trying to evaluate itself, try splitting your code in multiple nodes.", - MaxMacroUnificationDepth), - *parent); - - if (target.nodeType() == NodeType::Symbol) - { - if (const auto p = map.find(target.string()); p != map.end()) - target = p->second; - } - else if (target.isListLike()) - { - for (std::size_t i = 0; i < target.list().size(); ++i) - unify(map, target.list()[i], &target, i, unify_depth + 1); - } - else if (target.nodeType() == NodeType::Spread) - { - Node sub_node = target; - sub_node.setNodeType(NodeType::Symbol); - unify(map, sub_node, parent, 0, unify_depth + 1); - - if (sub_node.nodeType() != NodeType::List) - throwMacroProcessingError(fmt::format("Can not unify a {} to a Spread", typeToString(sub_node)), sub_node); - - for (std::size_t i = 1, end = sub_node.list().size(); i < end; ++i) - parent->list().insert( - parent->list().begin() + static_cast::difference_type>(index + i), - sub_node.list()[i]); - parent->list().erase(parent->list().begin() + static_cast::difference_type>(index)); // remove the spread - } - } - - void MacroProcessor::setWithFileAttributes(const Node origin, Node& output, const Node& macro) - { - output = macro; - output.setFilename(origin.filename()); - output.setPos(origin.line(), origin.col()); - } - void MacroProcessor::checkMacroArgCount(const Node& node, std::size_t expected, const std::string& name, const std::string& kind) { const std::size_t argcount = node.constList().size(); @@ -371,9 +332,9 @@ namespace Ark::internal if (isConstEval(lst)) { if (!lst.list().empty() && lst.list()[0] == getListNode()) - setWithFileAttributes(node, node, Node(static_cast(lst.list().size()) - 1)); + node.updateValueAndType(Node(static_cast(lst.list().size()) - 1)); else - setWithFileAttributes(node, node, Node(static_cast(lst.list().size()))); + node.updateValueAndType(Node(static_cast(lst.list().size()))); } } } @@ -385,12 +346,12 @@ namespace Ark::internal { // only apply len at compile time if we can if (!lst.list().empty() && lst.list()[0] == getListNode()) - setWithFileAttributes(node, node, lst.list().size() - 1 == 0 ? getTrueNode() : getFalseNode()); + node.updateValueAndType(lst.list().size() - 1 == 0 ? getTrueNode() : getFalseNode()); else - setWithFileAttributes(node, node, lst.list().empty() ? getTrueNode() : getFalseNode()); + node.updateValueAndType(lst.list().empty() ? getTrueNode() : getFalseNode()); } else if (lst == getNilNode()) - setWithFileAttributes(node, node, getTrueNode()); + node.updateValueAndType(getTrueNode()); } else if (name == "@") { @@ -438,15 +399,15 @@ namespace Ark::internal if (sublist.constList().size() > 1) { const Node sublistCopy = sublist.constList()[1]; - setWithFileAttributes(node, node, sublistCopy); + node.updateValueAndType(sublistCopy); } else - setWithFileAttributes(node, node, getNilNode()); + node.updateValueAndType(getNilNode()); } else if (!sublist.list().empty()) - setWithFileAttributes(node, node, sublist.constList()[0]); + node.updateValueAndType(sublist.constList()[0]); else - setWithFileAttributes(node, node, getNilNode()); + node.updateValueAndType(getNilNode()); } } else if (name == "tail") @@ -461,11 +422,11 @@ namespace Ark::internal if (sublist.list().size() > 1) { sublist.list().erase(sublist.constList().begin() + 1); - setWithFileAttributes(node, node, sublist); + node.updateValueAndType(sublist); } else { - setWithFileAttributes(node, node, Node(NodeType::List)); + node.updateValueAndType(Node(NodeType::List)); node.push_back(getListNode()); } } @@ -473,11 +434,11 @@ namespace Ark::internal { sublist.list().erase(sublist.constList().begin()); sublist.list().insert(sublist.list().begin(), getListNode()); - setWithFileAttributes(node, node, sublist); + node.updateValueAndType(sublist); } else { - setWithFileAttributes(node, node, Node(NodeType::List)); + node.updateValueAndType(Node(NodeType::List)); node.push_back(getListNode()); } } @@ -521,19 +482,19 @@ namespace Ark::internal if (sym.nodeType() == NodeType::Symbol) { if (const auto maybe_func = lookupDefinedFunction(sym.string()); maybe_func.has_value()) - setWithFileAttributes(node, node, Node(static_cast(maybe_func->constList().size()))); + node.updateValueAndType(Node(static_cast(maybe_func->constList().size()))); else throwMacroProcessingError(fmt::format("When expanding `{}', expected a known function name, got unbound variable {}", Language::Argcount, sym.string()), sym); } else if (sym.nodeType() == NodeType::List && sym.constList().size() == 3 && sym.constList()[0].nodeType() == NodeType::Keyword && sym.constList()[0].keyword() == Keyword::Fun) - setWithFileAttributes(node, node, Node(static_cast(sym.constList()[1].constList().size()))); + node.updateValueAndType(Node(static_cast(sym.constList()[1].constList().size()))); else throwMacroProcessingError(fmt::format("When trying to apply `{}', got a {} instead of a Symbol or Function", Language::Argcount, typeToString(sym)), sym); } else if (name == Language::Repr) { const Node ast = node.constList()[1]; - setWithFileAttributes(node, node, Node(NodeType::String, ast.repr())); + node.updateValueAndType(Node(NodeType::String, ast.repr())); } else if (name == Language::Paste) { @@ -565,7 +526,7 @@ namespace Ark::internal if (node.nodeType() == NodeType::List && !node.constList().empty()) { for (auto& child : node.list()) - setWithFileAttributes(child, child, evaluate(child, depth + 1, is_not_body)); + child.updateValueAndType(evaluate(child, depth + 1, is_not_body)); } if (node.nodeType() == NodeType::Spread) @@ -625,16 +586,7 @@ namespace Ark::internal } } - void MacroProcessor::recurApply(Node& node) - { - if (applyMacro(node, 0) && node.isListLike()) - { - for (auto& child : node.list()) - recurApply(child); - } - } - - bool MacroProcessor::hadBegin(const Node& node) + bool MacroProcessor::isBeginNode(const Node& node) { return node.nodeType() == NodeType::List && !node.constList().empty() && diff --git a/src/arkreactor/Compiler/Package/ImportSolver.cpp b/src/arkreactor/Compiler/Package/ImportSolver.cpp index 68ca8b2d2..eace7b096 100644 --- a/src/arkreactor/Compiler/Package/ImportSolver.cpp +++ b/src/arkreactor/Compiler/Package/ImportSolver.cpp @@ -95,7 +95,7 @@ namespace Ark::internal // modules are already handled, we can safely replace the node x = m_modules[package].ast; if (!m_modules[package].has_been_processed) - x = findAndReplaceImports(x).first; // FIXME? + x = findAndReplaceImports(x).first; // todo: ? return std::make_pair(x, !m_modules[package].has_been_processed); }