ArkScript
A small, fast, functional and scripting language for video games
JsonCompiler.cpp
Go to the documentation of this file.
2
3#include <string>
4#include <vector>
5#include <ranges>
6#include <Ark/Exceptions.hpp>
7
8#include <fmt/ranges.h>
9#include <fmt/format.h>
10
11using namespace Ark::internal;
12
13JsonCompiler::JsonCompiler(const unsigned debug, const std::vector<std::filesystem::path>& lib_env, const uint16_t features) :
14 m_welder(debug, lib_env, features)
15{}
16
17void JsonCompiler::feed(const std::string& filename)
18{
20}
21
23{
24 return _compile(m_welder.ast());
25}
26
27std::string JsonCompiler::_compile(const Node& node)
28{
29 std::string json;
30
31 switch (node.nodeType())
32 {
33 case NodeType::Symbol:
34 {
35 json += fmt::format(
36 R"({{"type": "Symbol", "name": "{}"}})",
37 node.string());
38 break;
39 }
40
41 case NodeType::Spread:
42 {
43 json += fmt::format(
44 R"({{"type": "Spread", "name": "{}"}})",
45 node.string());
46 break;
47 }
48
49 case NodeType::Capture:
50 {
51 json += fmt::format(
52 R"({{"type": "Capture", "name": "{}"}})",
53 node.string());
54 break;
55 }
56
57 case NodeType::Field:
58 {
59 json += R"({"type": "Field", "children": )";
60 json += toJsonList(node, 0) + "}";
61 break;
62 }
63
64 case NodeType::String:
65 {
66 json += fmt::format(
67 R"({{"type": "String", "value": "{}"}})",
68 node.string());
69 break;
70 }
71
72 case NodeType::Number:
73 {
74 json += fmt::format(
75 R"({{"type": "Number", "value": {}}})",
76 node.number());
77 break;
78 }
79
80 case NodeType::List:
81 {
82 if (!node.constList().empty() && node.constList()[0].nodeType() == NodeType::Keyword)
83 {
84 Node keyword = node.constList()[0];
85 switch (keyword.keyword())
86 {
87 case Keyword::Fun:
88 {
89 // (fun (args) (body))
90 std::string args;
91 Node args_node = node.constList()[1];
92 if (args_node.nodeType() == NodeType::List)
93 {
94 args = "[";
95 for (std::size_t i = 0, end = args_node.constList().size(); i < end; ++i)
96 {
97 args += _compile(args_node.constList()[i]);
98 if (end > 1 && i != end - 1)
99 args += ", ";
100 }
101 args += "]";
102 }
103 else
104 args = _compile(args_node);
105
106 json += fmt::format(
107 R"({{"type": "Fun", "args": {}, "body": {}}})",
108 args, _compile(node.constList()[2]));
109 break;
110 }
111
112 case Keyword::Let:
113 {
114 // (let name value)
115 json += fmt::format(
116 R"({{"type": "Let", "name": {}, "value": {}}})",
117 _compile(node.constList()[1]), _compile(node.constList()[2]));
118 break;
119 }
120
121 case Keyword::Mut:
122 {
123 // (mut name value)
124 json += fmt::format(
125 R"({{"type": "Mut", "name": {}, "value": {}}})",
126 _compile(node.constList()[1]), _compile(node.constList()[2]));
127 break;
128 }
129
130 case Keyword::Set:
131 {
132 // (set name value)
133 json += fmt::format(
134 R"({{"type": "Set", "name": {}, "value": {}}})",
135 _compile(node.constList()[1]), _compile(node.constList()[2]));
136 break;
137 }
138
139 case Keyword::If:
140 {
141 // (if condition then else)
142 if (node.constList().size() == 4)
143 json += fmt::format(
144 R"({{"type": "If", "condition": {}, "then": {}, "else": {}}})",
145 _compile(node.constList()[1]), _compile(node.constList()[2]), _compile(node.constList()[3]));
146 else
147 json += fmt::format(
148 R"({{"type": "If", "condition": {}, "then": {}}})",
149 _compile(node.constList()[1]), _compile(node.constList()[2]));
150 break;
151 }
152
153 case Keyword::While:
154 {
155 // (while condition body)
156 json += fmt::format(
157 R"({{"type": "While", "condition": {}, "body": {}}})",
158 _compile(node.constList()[1]), _compile(node.constList()[2]));
159 break;
160 }
161
162 case Keyword::Begin:
163 {
164 // (begin body)
165 json += R"({"type": "Begin", "children": )";
166 json += toJsonList(node, 1) + "}";
167 break;
168 }
169
170 case Keyword::Import:
171 {
172 // (import pkg.value)
173 // (import pkg.value :sym)
174 // (import pkg.value:*)
175 std::string package = node.constList()[1].constList().front().string();
176 for (const auto& sym : node.constList()[1].constList() | std::views::drop(1))
177 package += "." + sym.string();
178
179 bool is_glob = node.constList()[2].nodeType() == NodeType::Symbol && node.constList()[2].string() == "*";
180 std::vector<std::string> syms;
181 if (node.constList()[2].nodeType() == NodeType::List)
182 {
183 for (const auto& sym : node.constList()[2].constList())
184 syms.push_back('"' + sym.string() + '"');
185 }
186 json += fmt::format(
187 R"({{"type": "Import", "package": "{}", "glob": {}, "symbols": [{}]}})",
188 package,
189 is_glob,
190 fmt::join(syms, ", "));
191 break;
192 }
193
194 case Keyword::Del:
195 {
196 // (del value)
197 json += fmt::format(
198 R"({{"type": "Del", "value": {}}})",
199 _compile(node.constList()[1]));
200 break;
201 }
202 }
203 }
204 else if (node.constList().size() > 1 && node.constList()[0].nodeType() == NodeType::Symbol)
205 {
206 // (foo bar 1)
207 json += fmt::format(
208 R"({{"type": "FunctionCall", "name": {}, "args": {}}})",
209 _compile(node.constList()[0]),
210 toJsonList(node, 1));
211 }
212 else
213 json += toJsonList(node, 0);
214
215 break;
216 }
217
218 case NodeType::Macro:
219 {
220 if (const auto& first = node.constList()[0]; first.nodeType() == NodeType::Symbol)
221 {
222 json += fmt::format(
223 R"({{"type": "Macro", "name": {}, )",
224 _compile(node.constList()[0]));
225 if (node.constList().size() == 2)
226 json += fmt::format(R"("value": {}}})", _compile(node.constList()[1]));
227 else
228 json += fmt::format(
229 R"("args": {}, "body": {}}})",
230 toJsonList(node.constList()[1], 0),
231 _compile(node.constList()[2]));
232 }
233 else if (first.nodeType() == NodeType::Keyword)
234 {
235 if (first.keyword() == Keyword::If)
236 json += fmt::format(
237 R"({{"type": "MacroCondition", "condition": {}, "then": {}, "else": {}}})",
238 _compile(node.constList()[1]),
239 _compile(node.constList()[2]),
240 node.constList().size() == 4 ? _compile(node.constList()[3]) : R"({"type": "Symbol", "name": "nil"})");
241 }
242 break;
243 }
244
245 case NodeType::Unused:
246 break;
247
248 default:
249 throw Ark::Error(fmt::format(
250 "Not handled NodeType::{} ({} at {}:{}), please report this error on GitHub",
251 nodeTypes[static_cast<std::size_t>(node.nodeType())].data(),
252 node.filename(),
253 node.line(),
254 node.col()));
255 }
256 return json;
257}
258
259std::string JsonCompiler::toJsonList(const Node& node, const std::size_t start)
260{
261 std::vector<std::string> json;
262 for (std::size_t i = start, end = node.constList().size(); i < end; ++i)
263 {
264 if (node.constList()[i].nodeType() != NodeType::Unused)
265 json.push_back(_compile(node.constList()[i]));
266 }
267 return fmt::format("[{}]", fmt::join(json, ", "));
268}
ArkScript homemade exceptions.
bool computeASTFromFile(const std::string &filename)
Definition Welder.cpp:35
const internal::Node & ast() const noexcept
Definition Welder.cpp:96
A node of an Abstract Syntax Tree for ArkScript.
Definition Node.hpp:31
NodeType nodeType() const noexcept
Return the node type.
Definition Node.cpp:77
const std::string & filename() const noexcept
Return the filename in which this node was created.
Definition Node.cpp:145
const std::string & string() const noexcept
Return the string held by the value (if the node type allows it)
Definition Node.cpp:37
const std::vector< Node > & constList() const noexcept
Return the list of sub-nodes held by the node.
Definition Node.cpp:72
Keyword keyword() const noexcept
Return the keyword held by the value (if the node type allows it)
Definition Node.cpp:47
std::size_t col() const noexcept
Get the column at which this node was created.
Definition Node.cpp:140
double number() const noexcept
Return the number held by the value (if the node type allows it)
Definition Node.cpp:42
std::size_t line() const noexcept
Get the line at which this node was created.
Definition Node.cpp:135
void feed(const std::string &filename)
Feed the different variables with information taken from the given source code file.
JsonCompiler(unsigned debug, const std::vector< std::filesystem::path > &lib_env, uint16_t features=0)
Construct a new JsonCompiler object.
std::string compile()
Start the compilation.
std::string toJsonList(const Ark::internal::Node &node, std::size_t start)
Convert a NodeType::List to a JSON list.
std::string _compile(const Ark::internal::Node &node)
Compile a single node and return its representation.
Ark::Welder m_welder
constexpr std::array< std::string_view, 11 > nodeTypes
Node types as string, in the same order as the enum NodeType.
Definition Common.hpp:44