Replace table iterations with new for-loop syntax
[jackhill/mal.git] / cpp / Core.cpp
CommitLineData
dc9b184b
ST
1#include "MAL.h"
2#include "Environment.h"
3#include "StaticList.h"
4#include "Types.h"
5
cb252845 6#include <chrono>
ad50bab2 7#include <fstream>
ad0fc149
ST
8#include <iostream>
9
dc9b184b
ST
10#define CHECK_ARGS_IS(expected) \
11 checkArgsIs(name.c_str(), expected, \
12 std::distance(argsBegin, argsEnd))
13
14#define CHECK_ARGS_BETWEEN(min, max) \
15 checkArgsBetween(name.c_str(), min, max, \
16 std::distance(argsBegin, argsEnd))
17
18#define CHECK_ARGS_AT_LEAST(expected) \
19 checkArgsAtLeast(name.c_str(), expected, \
20 std::distance(argsBegin, argsEnd))
21
ad0fc149
ST
22static String printValues(malValueIter begin, malValueIter end,
23 const String& sep, bool readably);
24
dc9b184b
ST
25static StaticList<malBuiltIn*> handlers;
26
27#define ARG(type, name) type* name = VALUE_CAST(type, *argsBegin++)
28
29#define FUNCNAME(uniq) builtIn ## uniq
30#define HRECNAME(uniq) handler ## uniq
31#define BUILTIN_DEF(uniq, symbol) \
32 static malBuiltIn::ApplyFunc FUNCNAME(uniq); \
33 static StaticList<malBuiltIn*>::Node HRECNAME(uniq) \
34 (handlers, new malBuiltIn(symbol, FUNCNAME(uniq))); \
35 malValuePtr FUNCNAME(uniq)(const String& name, \
36 malValueIter argsBegin, malValueIter argsEnd, malEnvPtr env)
37
38#define BUILTIN(symbol) BUILTIN_DEF(__LINE__, symbol)
39
ad0fc149
ST
40#define BUILTIN_ISA(symbol, type) \
41 BUILTIN(symbol) { \
42 CHECK_ARGS_IS(1); \
43 return mal::boolean(DYNAMIC_CAST(type, *argsBegin)); \
44 }
45
2f61492a
ST
46#define BUILTIN_IS(op, constant) \
47 BUILTIN(op) { \
48 CHECK_ARGS_IS(1); \
49 return mal::boolean(*argsBegin == mal::constant()); \
50 }
51
dc9b184b
ST
52#define BUILTIN_INTOP(op, checkDivByZero) \
53 BUILTIN(#op) { \
54 CHECK_ARGS_IS(2); \
55 ARG(malInteger, lhs); \
56 ARG(malInteger, rhs); \
57 if (checkDivByZero) { \
58 ASSERT(rhs->value() != 0, "Division by zero"); \
59 } \
60 return mal::integer(lhs->value() op rhs->value()); \
61 }
62
cb252845 63BUILTIN_ISA("atom?", malAtom);
2f61492a 64BUILTIN_ISA("keyword?", malKeyword);
ad0fc149 65BUILTIN_ISA("list?", malList);
2f61492a
ST
66BUILTIN_ISA("map?", malHash);
67BUILTIN_ISA("sequential?", malSequence);
68BUILTIN_ISA("symbol?", malSymbol);
69BUILTIN_ISA("vector?", malVector);
ad0fc149 70
dc9b184b
ST
71BUILTIN_INTOP(+, false);
72BUILTIN_INTOP(/, true);
73BUILTIN_INTOP(*, false);
74BUILTIN_INTOP(%, true);
75
2f61492a
ST
76BUILTIN_IS("true?", trueValue);
77BUILTIN_IS("false?", falseValue);
78BUILTIN_IS("nil?", nilValue);
79
dc9b184b
ST
80BUILTIN("-")
81{
82 int argCount = CHECK_ARGS_BETWEEN(1, 2);
83 ARG(malInteger, lhs);
84 if (argCount == 1) {
85 return mal::integer(- lhs->value());
86 }
87
88 ARG(malInteger, rhs);
89 return mal::integer(lhs->value() - rhs->value());
90}
91
ad0fc149
ST
92BUILTIN("<=")
93{
94 CHECK_ARGS_IS(2);
95 ARG(malInteger, lhs);
96 ARG(malInteger, rhs);
97
98 return mal::boolean(lhs->value() <= rhs->value());
99}
100
101BUILTIN("=")
102{
103 CHECK_ARGS_IS(2);
104 const malValue* lhs = (*argsBegin++).ptr();
105 const malValue* rhs = (*argsBegin++).ptr();
106
107 return mal::boolean(lhs->isEqualTo(rhs));
108}
109
2f61492a
ST
110BUILTIN("apply")
111{
112 CHECK_ARGS_AT_LEAST(2);
113 malValuePtr op = *argsBegin++; // this gets checked in APPLY
114
115 // Copy the first N-1 arguments in.
116 malValueVec args(argsBegin, argsEnd-1);
117
118 // Then append the argument as a list.
119 const malSequence* lastArg = VALUE_CAST(malSequence, *(argsEnd-1));
120 for (int i = 0; i < lastArg->count(); i++) {
121 args.push_back(lastArg->item(i));
122 }
123
124 return APPLY(op, args.begin(), args.end(), env->getRoot());
125}
126
127BUILTIN("assoc")
128{
129 CHECK_ARGS_AT_LEAST(1);
130 ARG(malHash, hash);
131
132 return hash->assoc(argsBegin, argsEnd);
133}
134
cb252845
ST
135BUILTIN("atom")
136{
137 CHECK_ARGS_IS(1);
138
139 return mal::atom(*argsBegin);
140}
141
5f8f337a
ST
142BUILTIN("concat")
143{
144 int count = 0;
145 for (auto it = argsBegin; it != argsEnd; ++it) {
146 const malSequence* seq = VALUE_CAST(malSequence, *it);
147 count += seq->count();
148 }
149
150 malValueVec* items = new malValueVec(count);
151 int offset = 0;
152 for (auto it = argsBegin; it != argsEnd; ++it) {
153 const malSequence* seq = STATIC_CAST(malSequence, *it);
154 std::copy(seq->begin(), seq->end(), items->begin() + offset);
155 offset += seq->count();
156 }
157
158 return mal::list(items);
159}
160
cb252845
ST
161BUILTIN("conj")
162{
163 CHECK_ARGS_AT_LEAST(1);
164 ARG(malSequence, seq);
165
166 return seq->conj(argsBegin, argsEnd);
167}
168
5f8f337a
ST
169BUILTIN("cons")
170{
171 CHECK_ARGS_IS(2);
172 malValuePtr first = *argsBegin++;
173 ARG(malSequence, rest);
174
175 malValueVec* items = new malValueVec(1 + rest->count());
176 items->at(0) = first;
177 std::copy(rest->begin(), rest->end(), items->begin() + 1);
178
179 return mal::list(items);
180}
181
2f61492a
ST
182BUILTIN("contains?")
183{
184 CHECK_ARGS_IS(2);
185 if (*argsBegin == mal::nilValue()) {
186 return *argsBegin;
187 }
188 ARG(malHash, hash);
189 return mal::boolean(hash->contains(*argsBegin));
190}
191
ad0fc149
ST
192BUILTIN("count")
193{
194 CHECK_ARGS_IS(1);
195 if (*argsBegin == mal::nilValue()) {
196 return mal::integer(0);
197 }
198
199 ARG(malSequence, seq);
200 return mal::integer(seq->count());
201}
202
cb252845
ST
203BUILTIN("deref")
204{
205 CHECK_ARGS_IS(1);
206 ARG(malAtom, atom);
207
208 return atom->deref();
209}
210
2f61492a
ST
211BUILTIN("dissoc")
212{
213 CHECK_ARGS_AT_LEAST(1);
214 ARG(malHash, hash);
215
216 return hash->dissoc(argsBegin, argsEnd);
217}
218
ad0fc149
ST
219BUILTIN("empty?")
220{
221 CHECK_ARGS_IS(1);
222 ARG(malSequence, seq);
223
224 return mal::boolean(seq->isEmpty());
225}
226
ad50bab2
ST
227BUILTIN("eval")
228{
229 CHECK_ARGS_IS(1);
230 return EVAL(*argsBegin, env->getRoot());
231}
232
3a8e11b8
ST
233BUILTIN("first")
234{
235 CHECK_ARGS_IS(1);
236 ARG(malSequence, seq);
237 return seq->first();
238}
239
2f61492a
ST
240BUILTIN("get")
241{
242 CHECK_ARGS_IS(2);
243 if (*argsBegin == mal::nilValue()) {
244 return *argsBegin;
245 }
246 ARG(malHash, hash);
247 return hash->get(*argsBegin);
248}
249
dc9b184b
ST
250BUILTIN("hash-map")
251{
252 return mal::hash(argsBegin, argsEnd);
253}
254
2f61492a
ST
255BUILTIN("keys")
256{
257 CHECK_ARGS_IS(1);
258 ARG(malHash, hash);
259 return hash->keys();
260}
261
262BUILTIN("keyword")
263{
264 CHECK_ARGS_IS(1);
265 ARG(malString, token);
266 return mal::keyword(":" + token->value());
267}
268
cb252845
ST
269BUILTIN("meta")
270{
271 CHECK_ARGS_IS(1);
272 malValuePtr obj = *argsBegin++;
273
274 return obj->meta();
275}
276
3a8e11b8
ST
277BUILTIN("nth")
278{
279 CHECK_ARGS_IS(2);
280 ARG(malSequence, seq);
281 ARG(malInteger, index);
282
283 int i = index->value();
284 ASSERT(i >= 0 && i < seq->count(), "Index out of range");
285
286 return seq->item(i);
287}
288
ad0fc149
ST
289BUILTIN("pr-str")
290{
291 return mal::string(printValues(argsBegin, argsEnd, " ", true));
292}
293
294BUILTIN("println")
295{
296 std::cout << printValues(argsBegin, argsEnd, " ", false) << "\n";
297 return mal::nilValue();
298}
299
300BUILTIN("prn")
301{
302 std::cout << printValues(argsBegin, argsEnd, " ", true) << "\n";
303 return mal::nilValue();
304}
305
ad50bab2
ST
306BUILTIN("read-string")
307{
308 CHECK_ARGS_IS(1);
309 ARG(malString, str);
310
311 return readStr(str->value());
312}
313
cb252845
ST
314BUILTIN("readline")
315{
316 CHECK_ARGS_IS(1);
317 ARG(malString, str);
318
319 return readline(str->value());
320}
321
322BUILTIN("reset!")
323{
324 CHECK_ARGS_IS(2);
325 ARG(malAtom, atom);
326 return atom->reset(*argsBegin);
327}
328
3a8e11b8
ST
329BUILTIN("rest")
330{
331 CHECK_ARGS_IS(1);
332 ARG(malSequence, seq);
333 return seq->rest();
334}
335
ad50bab2
ST
336BUILTIN("slurp")
337{
338 CHECK_ARGS_IS(1);
339 ARG(malString, filename);
340
341 std::ios_base::openmode openmode =
342 std::ios::ate | std::ios::in | std::ios::binary;
343 std::ifstream file(filename->value().c_str(), openmode);
344 ASSERT(!file.fail(), "Cannot open %s", filename->value().c_str());
345
346 String data;
347 data.reserve(file.tellg());
348 file.seekg(0, std::ios::beg);
349 data.append(std::istreambuf_iterator<char>(file.rdbuf()),
350 std::istreambuf_iterator<char>());
351
352 return mal::string(data);
353}
354
ad0fc149
ST
355BUILTIN("str")
356{
357 return mal::string(printValues(argsBegin, argsEnd, "", false));
358}
359
2f61492a
ST
360BUILTIN("symbol")
361{
362 CHECK_ARGS_IS(1);
363 ARG(malString, token);
364 return mal::symbol(token->value());
365}
366
367BUILTIN("throw")
368{
369 CHECK_ARGS_IS(1);
370 throw *argsBegin;
371}
372
cb252845
ST
373BUILTIN("time-ms")
374{
375 CHECK_ARGS_IS(0);
376
377 using namespace std::chrono;
378 milliseconds ms = duration_cast<milliseconds>(
379 high_resolution_clock::now().time_since_epoch()
380 );
381
382 return mal::integer(ms.count());
383}
384
2f61492a
ST
385BUILTIN("vals")
386{
387 CHECK_ARGS_IS(1);
388 ARG(malHash, hash);
389 return hash->values();
390}
391
392BUILTIN("vector")
393{
394 return mal::vector(argsBegin, argsEnd);
395}
396
cb252845
ST
397BUILTIN("with-meta")
398{
399 CHECK_ARGS_IS(2);
400 malValuePtr obj = *argsBegin++;
401 malValuePtr meta = *argsBegin++;
402 return obj->withMeta(meta);
403}
404
dc9b184b
ST
405void installCore(malEnvPtr env) {
406 for (auto it = handlers.begin(), end = handlers.end(); it != end; ++it) {
407 malBuiltIn* handler = *it;
408 env->set(handler->name(), handler);
409 }
410}
ad0fc149
ST
411
412static String printValues(malValueIter begin, malValueIter end,
413 const String& sep, bool readably)
414{
415 String out;
416
417 if (begin != end) {
418 out += (*begin)->print(readably);
419 ++begin;
420 }
421
422 for ( ; begin != end; ++begin) {
423 out += sep;
424 out += (*begin)->print(readably);
425 }
426
427 return out;
428}