DISABLE FDs (REMOVE ME).
[jackhill/mal.git] / c / core.c
CommitLineData
ea81a808
JM
1#include <stdarg.h>
2#include <stdio.h>
3#include <stdlib.h>
4#include <string.h>
8cb5cda4 5#include <sys/stat.h>
b81b2a7e 6#include <sys/time.h>
8cb5cda4 7#include <fcntl.h>
b81b2a7e 8#include <unistd.h>
ea81a808
JM
9
10#include "types.h"
11#include "core.h"
8cb5cda4 12#include "reader.h"
ea81a808
JM
13#include "printer.h"
14
15// Errors/Exceptions
16void throw(MalVal *obj) {
17 mal_error = obj;
18}
19
20
21// General functions
22
23MalVal *equal_Q(MalVal *a, MalVal *b) {
24 if (_equal_Q(a, b)) { return &mal_true; }
25 else { return &mal_false; }
26}
27
28
2df92e06 29// Misc predicates
ea81a808
JM
30
31MalVal *nil_Q(MalVal *seq) { return seq->type & MAL_NIL ? &mal_true : &mal_false; }
32MalVal *true_Q(MalVal *seq) { return seq->type & MAL_TRUE ? &mal_true : &mal_false; }
33MalVal *false_Q(MalVal *seq) { return seq->type & MAL_FALSE ? &mal_true : &mal_false; }
4c14a8b8
JM
34MalVal *string_Q(MalVal *seq) {
35 if ((seq->type & MAL_STRING) && (seq->val.string[0] != '\x7f')) {
36 return &mal_true;
37 } else {
38 return &mal_false;
39 }
40}
2df92e06
JM
41MalVal *number_Q(MalVal *obj) {
42 return obj->type & MAL_INTEGER || obj->type & MAL_FLOAT
43 ? &mal_true
44 : &mal_false;
45}
46MalVal *fn_Q(MalVal *obj) {
47 return (obj->type & MAL_FUNCTION_C || obj->type & MAL_FUNCTION_MAL) &&
48 !obj->ismacro
49 ? &mal_true
50 : &mal_false;
51}
52MalVal *macro_Q(MalVal *obj) { return obj->ismacro ? &mal_true : &mal_false; }
53
ea81a808
JM
54
55
56// Symbol functions
57
58MalVal *symbol(MalVal *args) {
59 assert_type(args, MAL_STRING,
60 "symbol called with non-string value");
61 args->type = MAL_SYMBOL; // change string to symbol
62 return args;
63}
64
b8ee29b2
JM
65MalVal *symbol_Q(MalVal *seq) {
66 return seq->type & MAL_SYMBOL ? &mal_true : &mal_false; }
67
68
69// Keyword functions
70
71MalVal *keyword(MalVal *args) {
72 assert_type(args, MAL_STRING,
73 "keyword called with non-string value");
dbac60df
JM
74 if (args->val.string[0] == '\x7f') {
75 return args;
76 } else {
77 return malval_new_keyword(args->val.string);
78 }
b8ee29b2
JM
79}
80
81MalVal *keyword_Q(MalVal *seq) {
82 return seq->type & MAL_STRING && seq->val.string[0] == '\x7f'
83 ? &mal_true
84 : &mal_false;
85}
ea81a808
JM
86
87
88// String functions
89
90// Return a string representation of a MalVal sequence (in a format that can
91// be read by the reader). Returned string must be freed by caller.
92MalVal *pr_str(MalVal *args) {
93 assert_type(args, MAL_LIST|MAL_VECTOR,
94 "pr_str called with non-sequential args");
95 return malval_new_string(_pr_str_args(args, " ", 1));
96}
97
98// Return a string representation of a MalVal sequence with every item
99// concatenated together. Returned string must be freed by caller.
100MalVal *str(MalVal *args) {
101 assert_type(args, MAL_LIST|MAL_VECTOR,
102 "str called with non-sequential args");
103 return malval_new_string(_pr_str_args(args, "", 0));
104}
105
106// Print a string representation of a MalVal sequence (in a format that can
107// be read by the reader) followed by a newline. Returns nil.
108MalVal *prn(MalVal *args) {
109 assert_type(args, MAL_LIST|MAL_VECTOR,
110 "prn called with non-sequential args");
111 char *repr = _pr_str_args(args, " ", 1);
6b3ecaa7
DM
112 puts(repr);
113 MAL_GC_FREE(repr);
ea81a808
JM
114 return &mal_nil;
115}
116
117// Print a string representation of a MalVal sequence (for human consumption)
118// followed by a newline. Returns nil.
119MalVal *println(MalVal *args) {
120 assert_type(args, MAL_LIST|MAL_VECTOR,
121 "println called with non-sequential args");
122 char *repr = _pr_str_args(args, " ", 0);
6b3ecaa7
DM
123 puts(repr);
124 MAL_GC_FREE(repr);
ea81a808
JM
125 return &mal_nil;
126}
127
8cb5cda4
JM
128MalVal *mal_readline(MalVal *str) {
129 assert_type(str, MAL_STRING, "readline of non-string");
130 char * line = _readline(str->val.string);
131 if (line) { return malval_new_string(line); }
132 else { return &mal_nil; }
133}
ea81a808 134
8cb5cda4
JM
135MalVal *read_string(MalVal *str) {
136 assert_type(str, MAL_STRING, "read_string of non-string");
137 return read_str(str->val.string);
138}
ea81a808 139
8cb5cda4
JM
140char *slurp_raw(char *path) {
141 char *data;
142 struct stat fst;
143 int fd = open(path, O_RDONLY),
144 sz;
145 if (fd < 0) {
146 abort("slurp failed to open '%s'", path);
147 }
148 if (fstat(fd, &fst) < 0) {
149 abort("slurp failed to stat '%s'", path);
ea81a808 150 }
6b3ecaa7 151 data = MAL_GC_MALLOC(fst.st_size+1);
8cb5cda4
JM
152 sz = read(fd, data, fst.st_size);
153 if (sz < fst.st_size) {
154 abort("slurp failed to read '%s'", path);
ea81a808 155 }
8cb5cda4
JM
156 data[sz] = '\0';
157 return data;
158}
159MalVal *slurp(MalVal *path) {
160 assert_type(path, MAL_STRING, "slurp of non-string");
161 char *data = slurp_raw(path->val.string);
162 if (!data || mal_error) { return NULL; }
163 return malval_new_string(data);
164}
165
166
167
168
169// Number functions
170
ea81a808
JM
171WRAP_INTEGER_OP(plus,+)
172WRAP_INTEGER_OP(minus,-)
173WRAP_INTEGER_OP(multiply,*)
174WRAP_INTEGER_OP(divide,/)
175WRAP_INTEGER_CMP_OP(gt,>)
176WRAP_INTEGER_CMP_OP(gte,>=)
177WRAP_INTEGER_CMP_OP(lt,<)
178WRAP_INTEGER_CMP_OP(lte,<=)
179
db4c329a
JM
180MalVal *time_ms(MalVal *_) {
181 struct timeval tv;
182 long msecs;
183 gettimeofday(&tv, NULL);
184 msecs = tv.tv_sec * 1000 + tv.tv_usec/1000.0 + 0.5;
185
186 return malval_new_integer(msecs);
187}
188
ea81a808
JM
189
190// List functions
191
192MalVal *list(MalVal *args) { return _list(args); }
193MalVal *list_Q(MalVal *seq) { return _list_Q(seq) ? &mal_true : &mal_false; }
194
195
196// Vector functions
197
198MalVal *vector(MalVal *args) { return _vector(args); }
199MalVal *vector_Q(MalVal *seq) { return _vector_Q(seq) ? &mal_true : &mal_false; }
200
201
202// Hash map functions
203
ea81a808
JM
204MalVal *hash_map_Q(MalVal *seq) { return _hash_map_Q(seq) ? &mal_true : &mal_false; }
205
206MalVal *assoc(MalVal *args) {
207 assert_type(args, MAL_LIST|MAL_VECTOR,
208 "assoc called with non-sequential arguments");
209 assert(_count(args) >= 2,
210 "assoc needs at least 2 arguments");
8cb5cda4 211 GHashTable *htable = g_hash_table_copy(_first(args)->val.hash_table);
ea81a808 212 MalVal *hm = malval_new_hash_map(htable);
8cb5cda4 213 return _assoc_BANG(hm, _rest(args));
ea81a808
JM
214}
215
216MalVal *dissoc(MalVal* args) {
8cb5cda4 217 GHashTable *htable = g_hash_table_copy(_first(args)->val.hash_table);
ea81a808 218 MalVal *hm = malval_new_hash_map(htable);
8cb5cda4 219 return _dissoc_BANG(hm, _rest(args));
ea81a808
JM
220}
221
222MalVal *keys(MalVal *obj) {
223 assert_type(obj, MAL_HASH_MAP,
224 "keys called on non-hash-map");
225
226 GHashTableIter iter;
227 gpointer key, value;
228 MalVal *seq = malval_new_list(MAL_LIST,
229 g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),
230 _count(obj)));
231 g_hash_table_iter_init (&iter, obj->val.hash_table);
232 while (g_hash_table_iter_next (&iter, &key, &value)) {
233 MalVal *kname = malval_new_string((char *)key);
234 g_array_append_val(seq->val.array, kname);
235 }
236 return seq;
237}
238
239MalVal *vals(MalVal *obj) {
240 assert_type(obj, MAL_HASH_MAP,
241 "vals called on non-hash-map");
242
243 GHashTableIter iter;
244 gpointer key, value;
245 MalVal *seq = malval_new_list(MAL_LIST,
246 g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),
247 _count(obj)));
248 g_hash_table_iter_init (&iter, obj->val.hash_table);
249 while (g_hash_table_iter_next (&iter, &key, &value)) {
250 g_array_append_val(seq->val.array, value);
251 }
252 return seq;
253}
254
255
256// hash map and vector functions
257MalVal *get(MalVal *obj, MalVal *key) {
258 MalVal *val;
259 switch (obj->type) {
260 case MAL_VECTOR:
261 return _nth(obj, key->val.intnum);
262 case MAL_HASH_MAP:
263 if (g_hash_table_lookup_extended(obj->val.hash_table,
264 key->val.string,
265 NULL, (gpointer*)&val)) {
266 return val;
267 } else {
268 return &mal_nil;
269 }
7e9a2883
JM
270 case MAL_NIL:
271 return &mal_nil;
ea81a808
JM
272 default:
273 abort("get called on unsupported type %d", obj->type);
274 }
275}
276
277MalVal *contains_Q(MalVal *obj, MalVal *key) {
278 switch (obj->type) {
279 case MAL_VECTOR:
280 if (key->val.intnum < obj->val.array->len) {
281 return &mal_true;
282 } else {
283 return &mal_false;
284 }
285 case MAL_HASH_MAP:
286 if (g_hash_table_contains(obj->val.hash_table, key->val.string)) {
287 return &mal_true;
288 } else {
289 return &mal_false;
290 }
291 default:
292 abort("contains? called on unsupported type %d", obj->type);
293 }
294}
295
296
297// Sequence functions
298
299MalVal *sequential_Q(MalVal *seq) {
300 return _sequential_Q(seq) ? &mal_true : &mal_false;
301}
302
303MalVal *cons(MalVal *x, MalVal *seq) {
304 assert_type(seq, MAL_LIST|MAL_VECTOR,
305 "second argument to cons is non-sequential");
306 int i, len = _count(seq);
307 GArray *new_arr = g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),
308 len+1);
309 g_array_append_val(new_arr, x);
310 for (i=0; i<len; i++) {
311 g_array_append_val(new_arr, g_array_index(seq->val.array, MalVal*, i));
312 }
313 return malval_new_list(MAL_LIST, new_arr);
314}
315
316MalVal *concat(MalVal *args) {
317 MalVal *arg, *e, *lst;
318 int i, j, arg_cnt = _count(args);
319 lst = malval_new_list(MAL_LIST,
320 g_array_sized_new(TRUE, TRUE, sizeof(MalVal*), arg_cnt));
321 for (i=0; i<arg_cnt; i++) {
322 arg = g_array_index(args->val.array, MalVal*, i);
323 assert_type(arg, MAL_LIST|MAL_VECTOR,
324 "concat called with non-sequential");
325 for (j=0; j<_count(arg); j++) {
326 e = g_array_index(arg->val.array, MalVal*, j);
327 g_array_append_val(lst->val.array, e);
328 }
329 }
330 return lst;
331}
332
333MalVal *nth(MalVal *seq, MalVal *idx) {
334 return _nth(seq, idx->val.intnum);
335}
336
ea81a808
JM
337MalVal *empty_Q(MalVal *seq) {
338 assert_type(seq, MAL_LIST|MAL_VECTOR,
339 "empty? called with non-sequential");
340 return (seq->val.array->len == 0) ? &mal_true : &mal_false;
341}
342
343MalVal *count(MalVal *seq) {
344 return malval_new_integer(_count(seq));
345}
346
ea81a808
JM
347MalVal *apply(MalVal *args) {
348 assert_type(args, MAL_LIST|MAL_VECTOR,
349 "apply called with non-sequential");
350 MalVal *f = _nth(args, 0);
8cb5cda4 351 MalVal *last_arg = _last(args);
ea81a808
JM
352 assert_type(last_arg, MAL_LIST|MAL_VECTOR,
353 "last argument to apply is non-sequential");
354 int i, len = _count(args) - 2 + _count(last_arg);
355 GArray *new_arr = g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),
356 len);
357 // Initial arguments
358 for (i=1; i<_count(args)-1; i++) {
359 g_array_append_val(new_arr, g_array_index(args->val.array, MalVal*, i));
360 }
361 // Add arguments from last_arg
362 for (i=0; i<_count(last_arg); i++) {
363 g_array_append_val(new_arr, g_array_index(last_arg->val.array, MalVal*, i));
364 }
365 return _apply(f, malval_new_list(MAL_LIST, new_arr));
366}
367
368MalVal *map(MalVal *mvf, MalVal *lst) {
369 MalVal *res, *el;
370 assert_type(mvf, MAL_FUNCTION_C|MAL_FUNCTION_MAL,
371 "map called with non-function");
372 assert_type(lst, MAL_LIST|MAL_VECTOR,
373 "map called with non-sequential");
374 int i, len = _count(lst);
375 el = malval_new_list(MAL_LIST,
376 g_array_sized_new(TRUE, TRUE, sizeof(MalVal*), len));
377 for (i=0; i<len; i++) {
378 // TODO: this is replicating some of apply functionality
379 if (mvf->type & MAL_FUNCTION_MAL) {
380 Env *fn_env = new_env(mvf->val.func.env,
381 mvf->val.func.args,
382 _slice(lst, i, i+1));
383 res = mvf->val.func.evaluator(mvf->val.func.body, fn_env);
384 } else {
385 res = mvf->val.f1(g_array_index(lst->val.array, MalVal*, i));
386 }
387 if (!res || mal_error) return NULL;
388 g_array_append_val(el->val.array, res);
389 }
390 return el;
391}
392
4c14a8b8
JM
393MalVal *sconj(MalVal *args) {
394 assert_type(args, MAL_LIST|MAL_VECTOR,
395 "conj called with non-sequential");
396 MalVal *src_lst = _nth(args, 0);
397 assert_type(args, MAL_LIST|MAL_VECTOR,
398 "first argument to conj is non-sequential");
399 int i, len = _count(src_lst) + _count(args) - 1;
400 GArray *new_arr = g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),
401 len);
402 // Copy in src_lst
403 for (i=0; i<_count(src_lst); i++) {
404 g_array_append_val(new_arr, g_array_index(src_lst->val.array, MalVal*, i));
405 }
406 // Conj extra args
407 for (i=1; i<_count(args); i++) {
408 if (src_lst->type & MAL_LIST) {
409 g_array_prepend_val(new_arr, g_array_index(args->val.array, MalVal*, i));
410 } else {
411 g_array_append_val(new_arr, g_array_index(args->val.array, MalVal*, i));
412 }
413 }
414 return malval_new_list(src_lst->type, new_arr);
415}
416
417MalVal *seq(MalVal *obj) {
418 assert_type(obj, MAL_LIST|MAL_VECTOR|MAL_STRING|MAL_NIL,
419 "seq: called with non-sequential");
420 int cnt, i;
421 MalVal *lst, *mstr;
422 switch (obj->type) {
423 case MAL_LIST:
424 cnt = _count(obj);
425 if (cnt == 0) { return &mal_nil; }
426 return obj;
427 case MAL_VECTOR:
428 cnt = _count(obj);
429 if (cnt == 0) { return &mal_nil; }
430 lst = malval_new_list(MAL_LIST,
431 g_array_sized_new(TRUE, TRUE, sizeof(MalVal*), cnt));
432 lst->val.array = obj->val.array;
433 return lst;
434 case MAL_STRING:
435 cnt = strlen(obj->val.string);
436 if (cnt == 0) { return &mal_nil; }
437 lst = malval_new_list(MAL_LIST,
438 g_array_sized_new(TRUE, TRUE, sizeof(MalVal*), cnt));
439 for (i=0; i<cnt; i++) {
440 mstr = malval_new_string(g_strdup_printf("%c", obj->val.string[i]));
441 g_array_append_val(lst->val.array, mstr);
442 }
443 return lst;
444 case MAL_NIL:
445 return &mal_nil;
446 }
447}
448
ea81a808
JM
449
450// Metadata functions
451
452MalVal *with_meta(MalVal *obj, MalVal *meta) {
453 MalVal *new_obj = malval_new(obj->type, meta);
454 new_obj->val = obj->val;
455 return new_obj;
456}
457
458MalVal *meta(MalVal *obj) {
b079f510
JM
459 assert_type(obj, MAL_LIST|MAL_VECTOR|MAL_HASH_MAP|
460 MAL_FUNCTION_C|MAL_FUNCTION_MAL|MAL_ATOM,
ea81a808
JM
461 "attempt to get metadata from non-collection type");
462 if (obj->metadata == NULL) {
463 return &mal_nil;
464 } else {
465 return obj->metadata;
466 }
467}
468
469
470// Atoms
471
472MalVal *atom(MalVal *val) {
473 return malval_new_atom(val);
474}
475
476MalVal *atom_Q(MalVal *exp) { return _atom_Q(exp) ? &mal_true : &mal_false; }
477
478MalVal *deref(MalVal *atm) {
479 assert_type(atm, MAL_ATOM,
480 "deref called on non-atom");
481 return atm->val.atom_val;
482}
483
484MalVal *reset_BANG(MalVal *atm, MalVal *val) {
485 assert_type(atm, MAL_ATOM,
486 "reset! called with non-atom");
487 atm->val.atom_val = val;
488 return val;
489}
490
491MalVal *swap_BANG(MalVal *args) {
492 assert_type(args, MAL_LIST|MAL_VECTOR,
493 "swap! called with invalid arguments");
494 assert(_count(args) >= 2,
495 "swap! called with %d args, needs at least 2", _count(args));
496 MalVal *atm = _nth(args, 0),
497 *f = _nth(args, 1),
498 *sargs = _slice(args, 2, _count(args)),
499 *fargs = cons(atm->val.atom_val, sargs),
500 *new_val = _apply(f, fargs);
501 if (mal_error) { return NULL; }
502 atm->val.atom_val = new_val;
503 return new_val;
504}
505
506
507
2df92e06 508core_ns_entry core_ns[61] = {
ea81a808
JM
509 {"=", (void*(*)(void*))equal_Q, 2},
510 {"throw", (void*(*)(void*))throw, 1},
511 {"nil?", (void*(*)(void*))nil_Q, 1},
512 {"true?", (void*(*)(void*))true_Q, 1},
513 {"false?", (void*(*)(void*))false_Q, 1},
4c14a8b8 514 {"string?", (void*(*)(void*))string_Q, 1},
ea81a808
JM
515 {"symbol", (void*(*)(void*))symbol, 1},
516 {"symbol?", (void*(*)(void*))symbol_Q, 1},
b8ee29b2
JM
517 {"keyword", (void*(*)(void*))keyword, 1},
518 {"keyword?", (void*(*)(void*))keyword_Q, 1},
2df92e06
JM
519 {"number?", (void*(*)(void*))number_Q, 1},
520 {"fn?", (void*(*)(void*))fn_Q, 1},
521 {"macro?", (void*(*)(void*))macro_Q, 1},
8cb5cda4 522
ea81a808
JM
523 {"pr-str", (void*(*)(void*))pr_str, -1},
524 {"str", (void*(*)(void*))str, -1},
525 {"prn", (void*(*)(void*))prn, -1},
526 {"println", (void*(*)(void*))println, -1},
8cb5cda4
JM
527 {"readline", (void*(*)(void*))mal_readline, 1},
528 {"read-string", (void*(*)(void*))read_string, 1},
529 {"slurp", (void*(*)(void*))slurp, 1},
ea81a808
JM
530 {"<", (void*(*)(void*))int_lt, 2},
531 {"<=", (void*(*)(void*))int_lte, 2},
532 {">", (void*(*)(void*))int_gt, 2},
533 {">=", (void*(*)(void*))int_gte, 2},
534 {"+", (void*(*)(void*))int_plus, 2},
535 {"-", (void*(*)(void*))int_minus, 2},
536 {"*", (void*(*)(void*))int_multiply, 2},
537 {"/", (void*(*)(void*))int_divide, 2},
db4c329a 538 {"time-ms", (void*(*)(void*))time_ms, 0},
ea81a808
JM
539
540 {"list", (void*(*)(void*))list, -1},
541 {"list?", (void*(*)(void*))list_Q, 1},
542 {"vector", (void*(*)(void*))vector, -1},
543 {"vector?", (void*(*)(void*))vector_Q, 1},
8cb5cda4 544 {"hash-map", (void*(*)(void*))_hash_map, -1},
ea81a808
JM
545 {"map?", (void*(*)(void*))hash_map_Q, 1},
546 {"assoc", (void*(*)(void*))assoc, -1},
547 {"dissoc", (void*(*)(void*))dissoc, -1},
548 {"get", (void*(*)(void*))get, 2},
549 {"contains?", (void*(*)(void*))contains_Q, 2},
550 {"keys", (void*(*)(void*))keys, 1},
551 {"vals", (void*(*)(void*))vals, 1},
552
553 {"sequential?", (void*(*)(void*))sequential_Q, 1},
554 {"cons", (void*(*)(void*))cons, 2},
555 {"concat", (void*(*)(void*))concat, -1},
556 {"nth", (void*(*)(void*))nth, 2},
8cb5cda4
JM
557 {"first", (void*(*)(void*))_first, 1},
558 {"rest", (void*(*)(void*))_rest, 1},
559 {"last", (void*(*)(void*))_last, 1},
ea81a808
JM
560 {"empty?", (void*(*)(void*))empty_Q, 1},
561 {"count", (void*(*)(void*))count, 1},
ea81a808
JM
562 {"apply", (void*(*)(void*))apply, -1},
563 {"map", (void*(*)(void*))map, 2},
564
4c14a8b8
JM
565 {"conj", (void*(*)(void*))sconj, -1},
566 {"seq", (void*(*)(void*))seq, 1},
567
ea81a808
JM
568 {"with-meta", (void*(*)(void*))with_meta, 2},
569 {"meta", (void*(*)(void*))meta, 1},
570 {"atom", (void*(*)(void*))atom, 1},
571 {"atom?", (void*(*)(void*))atom_Q, 1},
572 {"deref", (void*(*)(void*))deref, 1},
573 {"reset!", (void*(*)(void*))reset_BANG, 2},
574 {"swap!", (void*(*)(void*))swap_BANG, -1},
575 };