Merge pull request #167 from dubek/issue_166_ocaml
[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
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
494c1608 124 return APPLY(op, args.begin(), args.end());
2f61492a
ST
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);
494c1608 230 return EVAL(*argsBegin, NULL);
ad50bab2
ST
231}
232
3a8e11b8
ST
233BUILTIN("first")
234{
235 CHECK_ARGS_IS(1);
86604244
DM
236 if (*argsBegin == mal::nilValue()) {
237 return mal::nilValue();
238 }
3a8e11b8
ST
239 ARG(malSequence, seq);
240 return seq->first();
241}
242
2f61492a
ST
243BUILTIN("get")
244{
245 CHECK_ARGS_IS(2);
246 if (*argsBegin == mal::nilValue()) {
247 return *argsBegin;
248 }
249 ARG(malHash, hash);
250 return hash->get(*argsBegin);
251}
252
dc9b184b
ST
253BUILTIN("hash-map")
254{
86eae5ec 255 return mal::hash(argsBegin, argsEnd, true);
dc9b184b
ST
256}
257
2f61492a
ST
258BUILTIN("keys")
259{
260 CHECK_ARGS_IS(1);
261 ARG(malHash, hash);
262 return hash->keys();
263}
264
265BUILTIN("keyword")
266{
267 CHECK_ARGS_IS(1);
268 ARG(malString, token);
269 return mal::keyword(":" + token->value());
270}
271
cb252845
ST
272BUILTIN("meta")
273{
274 CHECK_ARGS_IS(1);
275 malValuePtr obj = *argsBegin++;
276
277 return obj->meta();
278}
279
3a8e11b8
ST
280BUILTIN("nth")
281{
282 CHECK_ARGS_IS(2);
283 ARG(malSequence, seq);
284 ARG(malInteger, index);
285
286 int i = index->value();
0997015d 287 MAL_CHECK(i >= 0 && i < seq->count(), "Index out of range");
3a8e11b8
ST
288
289 return seq->item(i);
290}
291
ad0fc149
ST
292BUILTIN("pr-str")
293{
294 return mal::string(printValues(argsBegin, argsEnd, " ", true));
295}
296
297BUILTIN("println")
298{
299 std::cout << printValues(argsBegin, argsEnd, " ", false) << "\n";
300 return mal::nilValue();
301}
302
303BUILTIN("prn")
304{
305 std::cout << printValues(argsBegin, argsEnd, " ", true) << "\n";
306 return mal::nilValue();
307}
308
ad50bab2
ST
309BUILTIN("read-string")
310{
311 CHECK_ARGS_IS(1);
312 ARG(malString, str);
313
314 return readStr(str->value());
315}
316
cb252845
ST
317BUILTIN("readline")
318{
319 CHECK_ARGS_IS(1);
320 ARG(malString, str);
321
322 return readline(str->value());
323}
324
325BUILTIN("reset!")
326{
327 CHECK_ARGS_IS(2);
328 ARG(malAtom, atom);
329 return atom->reset(*argsBegin);
330}
331
3a8e11b8
ST
332BUILTIN("rest")
333{
334 CHECK_ARGS_IS(1);
86604244
DM
335 if (*argsBegin == mal::nilValue()) {
336 return mal::list(new malValueVec(0));
337 }
3a8e11b8
ST
338 ARG(malSequence, seq);
339 return seq->rest();
340}
341
ad50bab2
ST
342BUILTIN("slurp")
343{
344 CHECK_ARGS_IS(1);
345 ARG(malString, filename);
346
347 std::ios_base::openmode openmode =
348 std::ios::ate | std::ios::in | std::ios::binary;
349 std::ifstream file(filename->value().c_str(), openmode);
0997015d 350 MAL_CHECK(!file.fail(), "Cannot open %s", filename->value().c_str());
ad50bab2
ST
351
352 String data;
353 data.reserve(file.tellg());
354 file.seekg(0, std::ios::beg);
355 data.append(std::istreambuf_iterator<char>(file.rdbuf()),
356 std::istreambuf_iterator<char>());
357
358 return mal::string(data);
359}
360
ad0fc149
ST
361BUILTIN("str")
362{
363 return mal::string(printValues(argsBegin, argsEnd, "", false));
364}
365
19a6c206
ST
366BUILTIN("swap!")
367{
368 CHECK_ARGS_AT_LEAST(2);
369 ARG(malAtom, atom);
370
371 malValuePtr op = *argsBegin++; // this gets checked in APPLY
372
373 malValueVec args(1 + argsEnd - argsBegin);
374 args[0] = atom->deref();
375 std::copy(argsBegin, argsEnd, args.begin() + 1);
376
494c1608 377 malValuePtr value = APPLY(op, args.begin(), args.end());
19a6c206
ST
378 return atom->reset(value);
379}
380
2f61492a
ST
381BUILTIN("symbol")
382{
383 CHECK_ARGS_IS(1);
384 ARG(malString, token);
385 return mal::symbol(token->value());
386}
387
388BUILTIN("throw")
389{
390 CHECK_ARGS_IS(1);
391 throw *argsBegin;
392}
393
cb252845
ST
394BUILTIN("time-ms")
395{
396 CHECK_ARGS_IS(0);
397
398 using namespace std::chrono;
399 milliseconds ms = duration_cast<milliseconds>(
400 high_resolution_clock::now().time_since_epoch()
401 );
402
403 return mal::integer(ms.count());
404}
405
2f61492a
ST
406BUILTIN("vals")
407{
408 CHECK_ARGS_IS(1);
409 ARG(malHash, hash);
410 return hash->values();
411}
412
413BUILTIN("vector")
414{
415 return mal::vector(argsBegin, argsEnd);
416}
417
cb252845
ST
418BUILTIN("with-meta")
419{
420 CHECK_ARGS_IS(2);
421 malValuePtr obj = *argsBegin++;
422 malValuePtr meta = *argsBegin++;
423 return obj->withMeta(meta);
424}
425
dc9b184b
ST
426void installCore(malEnvPtr env) {
427 for (auto it = handlers.begin(), end = handlers.end(); it != end; ++it) {
428 malBuiltIn* handler = *it;
429 env->set(handler->name(), handler);
430 }
431}
ad0fc149
ST
432
433static String printValues(malValueIter begin, malValueIter end,
434 const String& sep, bool readably)
435{
436 String out;
437
438 if (begin != end) {
439 out += (*begin)->print(readably);
440 ++begin;
441 }
442
443 for ( ; begin != end; ++begin) {
444 out += sep;
445 out += (*begin)->print(readably);
446 }
447
448 return out;
449}