ArkScript
A small, lisp-inspired, functional scripting language
JsonCompiler.cpp
Go to the documentation of this file.
2
3#include <string>
4#include <vector>
5#include <ranges>
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::MutArg:
42 {
43 json += fmt::format(
44 R"({{"type": "MutArg", "name": "{}"}})",
45 node.string());
46 break;
47 }
48
49 case NodeType::RefArg:
50 {
51 json += fmt::format(
52 R"({{"type": "RefArg", "name": "{}"}})",
53 node.string());
54 break;
55 }
56
57 case NodeType::Spread:
58 {
59 json += fmt::format(
60 R"({{"type": "Spread", "name": "{}"}})",
61 node.string());
62 break;
63 }
64
65 case NodeType::Capture:
66 {
67 json += fmt::format(
68 R"({{"type": "Capture", "name": "{}"}})",
69 node.string());
70 break;
71 }
72
73 case NodeType::Field:
74 {
75 json += R"({"type": "Field", "children": )";
76 json += toJsonList(node, 0) + "}";
77 break;
78 }
79
80 case NodeType::String:
81 {
82 json += fmt::format(
83 R"({{"type": "String", "value": "{}"}})",
84 node.string());
85 break;
86 }
87
88 case NodeType::Number:
89 {
90 json += fmt::format(
91 R"({{"type": "Number", "value": {}}})",
92 node.number());
93 break;
94 }
95
96 case NodeType::List:
97 {
98 if (!node.constList().empty() && node.constList()[0].nodeType() == NodeType::Keyword)
99 {
100 Node keyword = node.constList()[0];
101 switch (keyword.keyword())
102 {
103 case Keyword::Fun:
104 {
105 // (fun (args) (body))
106 std::string args;
107 Node args_node = node.constList()[1];
108 if (args_node.nodeType() == NodeType::List)
109 {
110 args = "[";
111 for (std::size_t i = 0, end = args_node.constList().size(); i < end; ++i)
112 {
113 args += _compile(args_node.constList()[i]);
114 if (end > 1 && i != end - 1)
115 args += ", ";
116 }
117 args += "]";
118 }
119 else
120 args = _compile(args_node);
121
122 json += fmt::format(
123 R"({{"type": "Fun", "args": {}, "body": {}}})",
124 args, _compile(node.constList()[2]));
125 break;
126 }
127
128 case Keyword::Let:
129 {
130 // (let name value)
131 json += fmt::format(
132 R"({{"type": "Let", "name": {}, "value": {}}})",
133 _compile(node.constList()[1]), _compile(node.constList()[2]));
134 break;
135 }
136
137 case Keyword::Mut:
138 {
139 // (mut name value)
140 json += fmt::format(
141 R"({{"type": "Mut", "name": {}, "value": {}}})",
142 _compile(node.constList()[1]), _compile(node.constList()[2]));
143 break;
144 }
145
146 case Keyword::Set:
147 {
148 // (set name value)
149 json += fmt::format(
150 R"({{"type": "Set", "name": {}, "value": {}}})",
151 _compile(node.constList()[1]), _compile(node.constList()[2]));
152 break;
153 }
154
155 case Keyword::If:
156 {
157 // (if condition then else)
158 if (node.constList().size() == 4)
159 json += fmt::format(
160 R"({{"type": "If", "condition": {}, "then": {}, "else": {}}})",
161 _compile(node.constList()[1]), _compile(node.constList()[2]), _compile(node.constList()[3]));
162 else
163 json += fmt::format(
164 R"({{"type": "If", "condition": {}, "then": {}}})",
165 _compile(node.constList()[1]), _compile(node.constList()[2]));
166 break;
167 }
168
169 case Keyword::While:
170 {
171 // (while condition body)
172 json += fmt::format(
173 R"({{"type": "While", "condition": {}, "body": {}}})",
174 _compile(node.constList()[1]), _compile(node.constList()[2]));
175 break;
176 }
177
178 case Keyword::Begin:
179 {
180 // (begin body)
181 json += R"({"type": "Begin", "children": )";
182 json += toJsonList(node, 1) + "}";
183 break;
184 }
185
186 case Keyword::Import:
187 {
188 // (import pkg.value)
189 // (import pkg.value :sym)
190 // (import pkg.value:*)
191 std::string package = node.constList()[1].constList().front().string();
192 for (const auto& sym : node.constList()[1].constList() | std::views::drop(1))
193 package += "." + sym.string();
194
195 bool is_glob = node.constList()[2].nodeType() == NodeType::Symbol && node.constList()[2].string() == "*";
196 std::vector<std::string> syms;
197 if (node.constList()[2].nodeType() == NodeType::List)
198 std::ranges::transform(
199 node.constList()[2].constList(),
200 std::back_inserter(syms),
201 [](const auto& sym) {
202 return fmt::format("{:?}", sym.string());
203 });
204
205 json += fmt::format(
206 R"({{"type": "Import", "package": "{}", "glob": {}, "symbols": [{}]}})",
207 package,
208 is_glob,
209 fmt::join(syms, ", "));
210 break;
211 }
212
213 case Keyword::Del:
214 {
215 // (del value)
216 json += fmt::format(
217 R"({{"type": "Del", "value": {}}})",
218 _compile(node.constList()[1]));
219 break;
220 }
221 }
222 }
223 else if (node.constList().size() > 1 && node.constList()[0].nodeType() == NodeType::Symbol)
224 {
225 // (foo bar 1)
226 json += fmt::format(
227 R"({{"type": "FunctionCall", "name": {}, "args": {}}})",
228 _compile(node.constList()[0]),
229 toJsonList(node, 1));
230 }
231 else
232 json += toJsonList(node, 0);
233
234 break;
235 }
236
237 case NodeType::Macro:
238 {
239 if (const auto& first = node.constList()[0]; first.nodeType() == NodeType::Symbol)
240 {
241 json += fmt::format(
242 R"({{"type": "Macro", "name": {}, )",
243 _compile(node.constList()[0]));
244 if (node.constList().size() == 2)
245 json += fmt::format(R"("value": {}}})", _compile(node.constList()[1]));
246 else
247 json += fmt::format(
248 R"("args": {}, "body": {}}})",
249 toJsonList(node.constList()[1], 0),
250 _compile(node.constList()[2]));
251 }
252 else if (first.nodeType() == NodeType::Keyword)
253 {
254 if (first.keyword() == Keyword::If)
255 json += fmt::format(
256 R"({{"type": "MacroCondition", "condition": {}, "then": {}, "else": {}}})",
257 _compile(node.constList()[1]),
258 _compile(node.constList()[2]),
259 node.constList().size() == 4 ? _compile(node.constList()[3]) : R"({"type": "Symbol", "name": "nil"})");
260 }
261 break;
262 }
263
264 case NodeType::Unused:
265 break;
266
267 default:
268 throw Ark::Error(fmt::format(
269 "Not handled NodeType::{} ({} at {}:{}), please report this error on GitHub",
270 nodeTypes[static_cast<std::size_t>(node.nodeType())].data(),
271 node.filename(),
272 node.position().start.line,
273 node.position().start.column));
274 }
275 return json;
276}
277
278std::string JsonCompiler::toJsonList(const Node& node, const std::size_t start)
279{
280 std::vector<std::string> json;
281 for (std::size_t i = start, end = node.constList().size(); i < end; ++i)
282 {
283 if (node.constList()[i].nodeType() != NodeType::Unused)
284 json.push_back(_compile(node.constList()[i]));
285 }
286 return fmt::format("[{}]", fmt::join(json, ", "));
287}
ArkScript homemade exceptions.
bool computeASTFromFile(const std::string &filename)
Definition Welder.cpp:36
const internal::Node & ast() const noexcept
Definition Welder.cpp:97
A node of an Abstract Syntax Tree for ArkScript.
Definition Node.hpp:32
NodeType nodeType() const noexcept
Return the node type.
Definition Node.cpp:78
const std::string & filename() const noexcept
Return the filename in which this node was created.
Definition Node.cpp:164
const std::string & string() const noexcept
Return the string held by the value (if the node type allows it)
Definition Node.cpp:38
const std::vector< Node > & constList() const noexcept
Return the list of sub-nodes held by the node.
Definition Node.cpp:73
Keyword keyword() const noexcept
Return the keyword held by the value (if the node type allows it)
Definition Node.cpp:48
FileSpan position() const noexcept
Get the span of the node (start and end)
Definition Node.cpp:159
double number() const noexcept
Return the number held by the value (if the node type allows it)
Definition Node.cpp:43
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, 13 > nodeTypes
Node types as string, in the same order as the enum NodeType.
Definition Common.hpp:61
std::size_t column
0-indexed column number
Definition Position.hpp:23
std::size_t line
0-indexed line number
Definition Position.hpp:22