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 std::ranges::transform(
183 node.constList()[2].constList(),
184 std::back_inserter(syms),
185 [](const auto& sym) {
186 return fmt::format("{:?}", sym.string());
187 });
188
189 json += fmt::format(
190 R"({{"type": "Import", "package": "{}", "glob": {}, "symbols": [{}]}})",
191 package,
192 is_glob,
193 fmt::join(syms, ", "));
194 break;
195 }
196
197 case Keyword::Del:
198 {
199 // (del value)
200 json += fmt::format(
201 R"({{"type": "Del", "value": {}}})",
202 _compile(node.constList()[1]));
203 break;
204 }
205 }
206 }
207 else if (node.constList().size() > 1 && node.constList()[0].nodeType() == NodeType::Symbol)
208 {
209 // (foo bar 1)
210 json += fmt::format(
211 R"({{"type": "FunctionCall", "name": {}, "args": {}}})",
212 _compile(node.constList()[0]),
213 toJsonList(node, 1));
214 }
215 else
216 json += toJsonList(node, 0);
217
218 break;
219 }
220
221 case NodeType::Macro:
222 {
223 if (const auto& first = node.constList()[0]; first.nodeType() == NodeType::Symbol)
224 {
225 json += fmt::format(
226 R"({{"type": "Macro", "name": {}, )",
227 _compile(node.constList()[0]));
228 if (node.constList().size() == 2)
229 json += fmt::format(R"("value": {}}})", _compile(node.constList()[1]));
230 else
231 json += fmt::format(
232 R"("args": {}, "body": {}}})",
233 toJsonList(node.constList()[1], 0),
234 _compile(node.constList()[2]));
235 }
236 else if (first.nodeType() == NodeType::Keyword)
237 {
238 if (first.keyword() == Keyword::If)
239 json += fmt::format(
240 R"({{"type": "MacroCondition", "condition": {}, "then": {}, "else": {}}})",
241 _compile(node.constList()[1]),
242 _compile(node.constList()[2]),
243 node.constList().size() == 4 ? _compile(node.constList()[3]) : R"({"type": "Symbol", "name": "nil"})");
244 }
245 break;
246 }
247
248 case NodeType::Unused:
249 break;
250
251 default:
252 throw Ark::Error(fmt::format(
253 "Not handled NodeType::{} ({} at {}:{}), please report this error on GitHub",
254 nodeTypes[static_cast<std::size_t>(node.nodeType())].data(),
255 node.filename(),
256 node.line(),
257 node.col()));
258 }
259 return json;
260}
261
262std::string JsonCompiler::toJsonList(const Node& node, const std::size_t start)
263{
264 std::vector<std::string> json;
265 for (std::size_t i = start, end = node.constList().size(); i < end; ++i)
266 {
267 if (node.constList()[i].nodeType() != NodeType::Unused)
268 json.push_back(_compile(node.constList()[i]));
269 }
270 return fmt::format("[{}]", fmt::join(json, ", "));
271}
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:30
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:59