Merge pull request #406 from chr15m/lib-alias-hacks
[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, \
494c1608 36 malValueIter argsBegin, malValueIter argsEnd)
dc9b184b
ST
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) { \
0997015d 58 MAL_CHECK(rhs->value() != 0, "Division by zero"); \
dc9b184b
ST
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 66BUILTIN_ISA("map?", malHash);
d53749f2 67BUILTIN_ISA("number?", malInteger);
2f61492a 68BUILTIN_ISA("sequential?", malSequence);
b43cf8d6 69BUILTIN_ISA("string?", malString);
2f61492a
ST
70BUILTIN_ISA("symbol?", malSymbol);
71BUILTIN_ISA("vector?", malVector);
ad0fc149 72
dc9b184b
ST
73BUILTIN_INTOP(+, false);
74BUILTIN_INTOP(/, true);
75BUILTIN_INTOP(*, false);
76BUILTIN_INTOP(%, true);
77
2f61492a
ST
78BUILTIN_IS("true?", trueValue);
79BUILTIN_IS("false?", falseValue);
80BUILTIN_IS("nil?", nilValue);
81
dc9b184b
ST
82BUILTIN("-")
83{
84 int argCount = CHECK_ARGS_BETWEEN(1, 2);
85 ARG(malInteger, lhs);
86 if (argCount == 1) {
87 return mal::integer(- lhs->value());
88 }
89
90 ARG(malInteger, rhs);
91 return mal::integer(lhs->value() - rhs->value());
92}
93
ad0fc149
ST
94BUILTIN("<=")
95{
96 CHECK_ARGS_IS(2);
97 ARG(malInteger, lhs);
98 ARG(malInteger, rhs);
99
100 return mal::boolean(lhs->value() <= rhs->value());
101}
102
946ea85c
NB
103BUILTIN(">=")
104{
105 CHECK_ARGS_IS(2);
106 ARG(malInteger, lhs);
107 ARG(malInteger, rhs);
108
109 return mal::boolean(lhs->value() >= rhs->value());
110}
111
112BUILTIN("<")
113{
114 CHECK_ARGS_IS(2);
115 ARG(malInteger, lhs);
116 ARG(malInteger, rhs);
117
118 return mal::boolean(lhs->value() < rhs->value());
119}
120
121BUILTIN(">")
122{
123 CHECK_ARGS_IS(2);
124 ARG(malInteger, lhs);
125 ARG(malInteger, rhs);
126
127 return mal::boolean(lhs->value() > rhs->value());
128}
129
ad0fc149
ST
130BUILTIN("=")
131{
132 CHECK_ARGS_IS(2);
133 const malValue* lhs = (*argsBegin++).ptr();
134 const malValue* rhs = (*argsBegin++).ptr();
135
136 return mal::boolean(lhs->isEqualTo(rhs));
137}
138
2f61492a
ST
139BUILTIN("apply")
140{
141 CHECK_ARGS_AT_LEAST(2);
142 malValuePtr op = *argsBegin++; // this gets checked in APPLY
143
144 // Copy the first N-1 arguments in.
145 malValueVec args(argsBegin, argsEnd-1);
146
147 // Then append the argument as a list.
148 const malSequence* lastArg = VALUE_CAST(malSequence, *(argsEnd-1));
149 for (int i = 0; i < lastArg->count(); i++) {
150 args.push_back(lastArg->item(i));
151 }
152
494c1608 153 return APPLY(op, args.begin(), args.end());
2f61492a
ST
154}
155
156BUILTIN("assoc")
157{
158 CHECK_ARGS_AT_LEAST(1);
159 ARG(malHash, hash);
160
161 return hash->assoc(argsBegin, argsEnd);
162}
163
cb252845
ST
164BUILTIN("atom")
165{
166 CHECK_ARGS_IS(1);
167
168 return mal::atom(*argsBegin);
169}
170
5f8f337a
ST
171BUILTIN("concat")
172{
173 int count = 0;
174 for (auto it = argsBegin; it != argsEnd; ++it) {
175 const malSequence* seq = VALUE_CAST(malSequence, *it);
176 count += seq->count();
177 }
178
179 malValueVec* items = new malValueVec(count);
180 int offset = 0;
181 for (auto it = argsBegin; it != argsEnd; ++it) {
182 const malSequence* seq = STATIC_CAST(malSequence, *it);
183 std::copy(seq->begin(), seq->end(), items->begin() + offset);
184 offset += seq->count();
185 }
186
187 return mal::list(items);
188}
189
cb252845
ST
190BUILTIN("conj")
191{
192 CHECK_ARGS_AT_LEAST(1);
193 ARG(malSequence, seq);
194
195 return seq->conj(argsBegin, argsEnd);
196}
197
5f8f337a
ST
198BUILTIN("cons")
199{
200 CHECK_ARGS_IS(2);
201 malValuePtr first = *argsBegin++;
202 ARG(malSequence, rest);
203
204 malValueVec* items = new malValueVec(1 + rest->count());
205 items->at(0) = first;
206 std::copy(rest->begin(), rest->end(), items->begin() + 1);
207
208 return mal::list(items);
209}
210
2f61492a
ST
211BUILTIN("contains?")
212{
213 CHECK_ARGS_IS(2);
214 if (*argsBegin == mal::nilValue()) {
215 return *argsBegin;
216 }
217 ARG(malHash, hash);
218 return mal::boolean(hash->contains(*argsBegin));
219}
220
ad0fc149
ST
221BUILTIN("count")
222{
223 CHECK_ARGS_IS(1);
224 if (*argsBegin == mal::nilValue()) {
225 return mal::integer(0);
226 }
227
228 ARG(malSequence, seq);
229 return mal::integer(seq->count());
230}
231
cb252845
ST
232BUILTIN("deref")
233{
234 CHECK_ARGS_IS(1);
235 ARG(malAtom, atom);
236
237 return atom->deref();
238}
239
2f61492a
ST
240BUILTIN("dissoc")
241{
242 CHECK_ARGS_AT_LEAST(1);
243 ARG(malHash, hash);
244
245 return hash->dissoc(argsBegin, argsEnd);
246}
247
ad0fc149
ST
248BUILTIN("empty?")
249{
250 CHECK_ARGS_IS(1);
251 ARG(malSequence, seq);
252
253 return mal::boolean(seq->isEmpty());
254}
255
ad50bab2
ST
256BUILTIN("eval")
257{
258 CHECK_ARGS_IS(1);
494c1608 259 return EVAL(*argsBegin, NULL);
ad50bab2
ST
260}
261
3a8e11b8
ST
262BUILTIN("first")
263{
264 CHECK_ARGS_IS(1);
86604244
DM
265 if (*argsBegin == mal::nilValue()) {
266 return mal::nilValue();
267 }
3a8e11b8
ST
268 ARG(malSequence, seq);
269 return seq->first();
270}
271
d53749f2
ST
272BUILTIN("fn?")
273{
274 CHECK_ARGS_IS(1);
275 malValuePtr arg = *argsBegin++;
276
277 // Lambdas are functions, unless they're macros.
278 if (const malLambda* lambda = DYNAMIC_CAST(malLambda, arg)) {
279 return mal::boolean(!lambda->isMacro());
280 }
281 // Builtins are functions.
282 return mal::boolean(DYNAMIC_CAST(malBuiltIn, arg));
283}
284
2f61492a
ST
285BUILTIN("get")
286{
287 CHECK_ARGS_IS(2);
288 if (*argsBegin == mal::nilValue()) {
289 return *argsBegin;
290 }
291 ARG(malHash, hash);
292 return hash->get(*argsBegin);
293}
294
dc9b184b
ST
295BUILTIN("hash-map")
296{
86eae5ec 297 return mal::hash(argsBegin, argsEnd, true);
dc9b184b
ST
298}
299
2f61492a
ST
300BUILTIN("keys")
301{
302 CHECK_ARGS_IS(1);
303 ARG(malHash, hash);
304 return hash->keys();
305}
306
307BUILTIN("keyword")
308{
309 CHECK_ARGS_IS(1);
310 ARG(malString, token);
311 return mal::keyword(":" + token->value());
312}
313
946ea85c
NB
314BUILTIN("list")
315{
316 return mal::list(argsBegin, argsEnd);
317}
318
d53749f2
ST
319BUILTIN("macro?")
320{
321 CHECK_ARGS_IS(1);
322
323 // Macros are implemented as lambdas, with a special flag.
324 const malLambda* lambda = DYNAMIC_CAST(malLambda, *argsBegin);
325 return mal::boolean((lambda != NULL) && lambda->isMacro());
326}
327
ef1de022
NB
328BUILTIN("map")
329{
330 CHECK_ARGS_IS(2);
331 malValuePtr op = *argsBegin++; // this gets checked in APPLY
332 ARG(malSequence, source);
333
334 const int length = source->count();
335 malValueVec* items = new malValueVec(length);
336 auto it = source->begin();
337 for (int i = 0; i < length; i++) {
338 items->at(i) = APPLY(op, it+i, it+i+1);
339 }
340
341 return mal::list(items);
342}
343
cb252845
ST
344BUILTIN("meta")
345{
346 CHECK_ARGS_IS(1);
347 malValuePtr obj = *argsBegin++;
348
349 return obj->meta();
350}
351
3a8e11b8
ST
352BUILTIN("nth")
353{
354 CHECK_ARGS_IS(2);
355 ARG(malSequence, seq);
356 ARG(malInteger, index);
357
358 int i = index->value();
0997015d 359 MAL_CHECK(i >= 0 && i < seq->count(), "Index out of range");
3a8e11b8
ST
360
361 return seq->item(i);
362}
363
ad0fc149
ST
364BUILTIN("pr-str")
365{
366 return mal::string(printValues(argsBegin, argsEnd, " ", true));
367}
368
369BUILTIN("println")
370{
371 std::cout << printValues(argsBegin, argsEnd, " ", false) << "\n";
372 return mal::nilValue();
373}
374
375BUILTIN("prn")
376{
377 std::cout << printValues(argsBegin, argsEnd, " ", true) << "\n";
378 return mal::nilValue();
379}
380
ad50bab2
ST
381BUILTIN("read-string")
382{
383 CHECK_ARGS_IS(1);
384 ARG(malString, str);
385
386 return readStr(str->value());
387}
388
cb252845
ST
389BUILTIN("readline")
390{
391 CHECK_ARGS_IS(1);
392 ARG(malString, str);
393
394 return readline(str->value());
395}
396
397BUILTIN("reset!")
398{
399 CHECK_ARGS_IS(2);
400 ARG(malAtom, atom);
401 return atom->reset(*argsBegin);
402}
403
3a8e11b8
ST
404BUILTIN("rest")
405{
406 CHECK_ARGS_IS(1);
86604244
DM
407 if (*argsBegin == mal::nilValue()) {
408 return mal::list(new malValueVec(0));
409 }
3a8e11b8
ST
410 ARG(malSequence, seq);
411 return seq->rest();
412}
413
b43cf8d6
ST
414BUILTIN("seq")
415{
416 CHECK_ARGS_IS(1);
417 malValuePtr arg = *argsBegin++;
418 if (arg == mal::nilValue()) {
419 return mal::nilValue();
420 }
421 if (const malSequence* seq = DYNAMIC_CAST(malSequence, arg)) {
422 return seq->isEmpty() ? mal::nilValue()
423 : mal::list(seq->begin(), seq->end());
424 }
425 if (const malString* strVal = DYNAMIC_CAST(malString, arg)) {
426 const String str = strVal->value();
427 int length = str.length();
428 if (length == 0)
429 return mal::nilValue();
430
431 malValueVec* items = new malValueVec(length);
432 for (int i = 0; i < length; i++) {
433 (*items)[i] = mal::string(str.substr(i, 1));
434 }
435 return mal::list(items);
436 }
437 MAL_FAIL("%s is not a string or sequence", arg->print(true).c_str());
438}
439
440
ad50bab2
ST
441BUILTIN("slurp")
442{
443 CHECK_ARGS_IS(1);
444 ARG(malString, filename);
445
446 std::ios_base::openmode openmode =
447 std::ios::ate | std::ios::in | std::ios::binary;
448 std::ifstream file(filename->value().c_str(), openmode);
0997015d 449 MAL_CHECK(!file.fail(), "Cannot open %s", filename->value().c_str());
ad50bab2
ST
450
451 String data;
452 data.reserve(file.tellg());
453 file.seekg(0, std::ios::beg);
454 data.append(std::istreambuf_iterator<char>(file.rdbuf()),
455 std::istreambuf_iterator<char>());
456
457 return mal::string(data);
458}
459
ad0fc149
ST
460BUILTIN("str")
461{
462 return mal::string(printValues(argsBegin, argsEnd, "", false));
463}
464
19a6c206
ST
465BUILTIN("swap!")
466{
467 CHECK_ARGS_AT_LEAST(2);
468 ARG(malAtom, atom);
469
470 malValuePtr op = *argsBegin++; // this gets checked in APPLY
471
472 malValueVec args(1 + argsEnd - argsBegin);
473 args[0] = atom->deref();
474 std::copy(argsBegin, argsEnd, args.begin() + 1);
475
494c1608 476 malValuePtr value = APPLY(op, args.begin(), args.end());
19a6c206
ST
477 return atom->reset(value);
478}
479
2f61492a
ST
480BUILTIN("symbol")
481{
482 CHECK_ARGS_IS(1);
483 ARG(malString, token);
484 return mal::symbol(token->value());
485}
486
487BUILTIN("throw")
488{
489 CHECK_ARGS_IS(1);
490 throw *argsBegin;
491}
492
cb252845
ST
493BUILTIN("time-ms")
494{
495 CHECK_ARGS_IS(0);
496
497 using namespace std::chrono;
498 milliseconds ms = duration_cast<milliseconds>(
499 high_resolution_clock::now().time_since_epoch()
500 );
501
502 return mal::integer(ms.count());
503}
504
2f61492a
ST
505BUILTIN("vals")
506{
507 CHECK_ARGS_IS(1);
508 ARG(malHash, hash);
509 return hash->values();
510}
511
512BUILTIN("vector")
513{
514 return mal::vector(argsBegin, argsEnd);
515}
516
cb252845
ST
517BUILTIN("with-meta")
518{
519 CHECK_ARGS_IS(2);
520 malValuePtr obj = *argsBegin++;
521 malValuePtr meta = *argsBegin++;
522 return obj->withMeta(meta);
523}
524
dc9b184b
ST
525void installCore(malEnvPtr env) {
526 for (auto it = handlers.begin(), end = handlers.end(); it != end; ++it) {
527 malBuiltIn* handler = *it;
528 env->set(handler->name(), handler);
529 }
530}
ad0fc149
ST
531
532static String printValues(malValueIter begin, malValueIter end,
533 const String& sep, bool readably)
534{
535 String out;
536
537 if (begin != end) {
538 out += (*begin)->print(readably);
539 ++begin;
540 }
541
542 for ( ; begin != end; ++begin) {
543 out += sep;
544 out += (*begin)->print(readably);
545 }
546
547 return out;
548}