ArkScript
A small, fast, functional and scripting language for video games
Function.cpp
Go to the documentation of this file.
2
3#include <fmt/core.h>
4
5namespace Ark::internal
6{
8 {
9 return node.nodeType() == NodeType::List && !node.constList().empty() && node.constList()[0].nodeType() == NodeType::Symbol;
10 }
11
13 {
14 Node& first = node.list()[0];
15
16 if (const Node* macro = findNearestMacro(first.string()); macro != nullptr)
17 {
18 if (macro->constList().size() == 2)
19 applyMacroProxy(first);
20 // ($ name (args) body)
21 else if (macro->constList().size() == 3)
22 {
23 Node temp_body = macro->constList()[2];
24 Node args = macro->constList()[1];
25 std::size_t args_needed = args.list().size();
26 std::size_t args_given = node.constList().size() - 1; // remove the first (the name of the macro)
27 std::string macro_name = macro->constList()[0].string();
28 const bool has_spread = args_needed > 0 && args.list().back().nodeType() == NodeType::Spread;
29
30 // bind node->list() to temp_body using macro->constList()[1]
31 std::unordered_map<std::string, Node> args_applied;
32 std::size_t j = 0;
33 for (std::size_t i = 1, end = node.constList().size(); i < end; ++i)
34 {
35 // by breaking early if we have too many arguments, the args_applied/args_needed check will fail
36 if (j >= args_needed)
37 break;
38
39 const std::string& arg_name = args.list()[j].string();
40 if (args.list()[j].nodeType() == NodeType::Symbol)
41 {
42 args_applied[arg_name] = node.constList()[i];
43 ++j;
44 }
45 else if (args.list()[j].nodeType() == NodeType::Spread)
46 {
47 if (!args_applied.contains(arg_name))
48 {
49 args_applied[arg_name] = Node(NodeType::List);
50 args_applied[arg_name].push_back(getListNode());
51 }
52 // do not move j because we checked before that the spread is always the last one
53 args_applied[arg_name].push_back(node.constList()[i]);
54 }
55 }
56
57 // check argument count
58 if (args_applied.size() + 1 == args_needed && has_spread)
59 {
60 // just a spread we didn't assign
61 args_applied[args.list().back().string()] = Node(NodeType::List);
62 args_applied[args.list().back().string()].push_back(getListNode());
63 }
64
65 if (args_given != args_needed && !has_spread)
66 throwMacroProcessingError(fmt::format("Macro `{}' got {} argument(s) but needed {}", macro_name, args_given, args_needed), node);
67 if (args_applied.size() != args_needed && has_spread)
68 // args_needed - 1 because we do not count the spread as a required argument
69 throwMacroProcessingError(fmt::format("Macro `{}' got {} argument(s) but needed at least {}", macro_name, args_applied.size(), args_needed - 1), node);
70
71 if (!args_applied.empty())
72 unify(args_applied, temp_body, nullptr);
73
74 node = evaluate(temp_body, false);
75 applyMacroProxy(node); // todo: this seems useless
76 return true;
77 }
78 }
79 else if (isPredefined(first.string()))
80 {
81 node = evaluate(node, false);
82 return true;
83 }
84
85 return false;
86 }
87}
Executor for List Macros.
bool canHandle(Node &node) override
Checks if the executor can apply a macro on the passed Node.
Definition: Function.cpp:7
bool applyMacro(Node &node) override
Executes macros in the Node if the Executor can handle it.
Definition: Function.cpp:12
bool applyMacroProxy(Node &node) const
Execute a node, trying to emplace macros calls.
Definition: Executor.cpp:42
void throwMacroProcessingError(const std::string &message, const Node &node) const
Throw a macro processing error.
Definition: Executor.cpp:37
void unify(const std::unordered_map< std::string, Node > &, Node &, Node *) const
Applies the spread operator.
Definition: Executor.cpp:32
Node evaluate(Node &node, bool is_not_body) const
Evaluate only the macros.
Definition: Executor.cpp:27
bool isPredefined(const std::string &symbol) const
Check if a given symbol is a predefined macro.
Definition: Executor.cpp:47
const Node * findNearestMacro(const std::string &name) const
Find the nearest macro matching a giving name.
Definition: Executor.cpp:12
A node of an Abstract Syntax Tree for ArkScript.
Definition: Node.hpp:30
NodeType nodeType() const noexcept
Return the node type.
Definition: Node.cpp:71
const std::string & string() const noexcept
Return the string held by the value (if the node type allows it)
Definition: Node.cpp:41
const std::vector< Node > & constList() const noexcept
Return the list of sub-nodes held by the node.
Definition: Node.cpp:66
std::vector< Node > & list() noexcept
Return the list of sub-nodes held by the node.
Definition: Node.cpp:61
const Node & getListNode()
Definition: Node.cpp:299