altos/lisp: More schemisms
[fw/altos] / src / lisp / ao_lisp_builtin.c
1 /*
2  * Copyright © 2016 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  */
14
15 #include "ao_lisp.h"
16
17 static int
18 builtin_size(void *addr)
19 {
20         (void) addr;
21         return sizeof (struct ao_lisp_builtin);
22 }
23
24 static void
25 builtin_mark(void *addr)
26 {
27         (void) addr;
28 }
29
30 static void
31 builtin_move(void *addr)
32 {
33         (void) addr;
34 }
35
36 const struct ao_lisp_type ao_lisp_builtin_type = {
37         .size = builtin_size,
38         .mark = builtin_mark,
39         .move = builtin_move
40 };
41
42 #ifdef AO_LISP_MAKE_CONST
43
44 #define AO_LISP_BUILTIN_CASENAME
45 #include "ao_lisp_builtin.h"
46
47 #define _atomn(n)       ao_lisp_poly_atom(_atom(n))
48
49 char *ao_lisp_args_name(uint8_t args) {
50         args &= AO_LISP_FUNC_MASK;
51         switch (args) {
52         case AO_LISP_FUNC_LAMBDA: return _atomn(lambda)->name;
53         case AO_LISP_FUNC_LEXPR: return _atomn(lexpr)->name;
54         case AO_LISP_FUNC_NLAMBDA: return _atomn(nlambda)->name;
55         case AO_LISP_FUNC_MACRO: return _atomn(macro)->name;
56         default: return "???";
57         }
58 }
59 #else
60
61 #define AO_LISP_BUILTIN_ARRAYNAME
62 #include "ao_lisp_builtin.h"
63
64 static char *
65 ao_lisp_builtin_name(enum ao_lisp_builtin_id b) {
66         if (b < _builtin_last)
67                 return ao_lisp_poly_atom(builtin_names[b])->name;
68         return "???";
69 }
70
71 static const ao_poly ao_lisp_args_atoms[] = {
72         [AO_LISP_FUNC_LAMBDA] = _ao_lisp_atom_lambda,
73         [AO_LISP_FUNC_LEXPR] = _ao_lisp_atom_lexpr,
74         [AO_LISP_FUNC_NLAMBDA] = _ao_lisp_atom_nlambda,
75         [AO_LISP_FUNC_MACRO] = _ao_lisp_atom_macro,
76 };
77
78 char *
79 ao_lisp_args_name(uint8_t args)
80 {
81         args &= AO_LISP_FUNC_MASK;
82         if (args < sizeof ao_lisp_args_atoms / sizeof ao_lisp_args_atoms[0])
83                 return ao_lisp_poly_atom(ao_lisp_args_atoms[args])->name;
84         return "(unknown)";
85 }
86 #endif
87
88 void
89 ao_lisp_builtin_print(ao_poly b)
90 {
91         struct ao_lisp_builtin *builtin = ao_lisp_poly_builtin(b);
92         printf("%s", ao_lisp_builtin_name(builtin->func));
93 }
94
95 ao_poly
96 ao_lisp_check_argc(ao_poly name, struct ao_lisp_cons *cons, int min, int max)
97 {
98         int     argc = 0;
99
100         while (cons && argc <= max) {
101                 argc++;
102                 cons = ao_lisp_poly_cons(cons->cdr);
103         }
104         if (argc < min || argc > max)
105                 return ao_lisp_error(AO_LISP_INVALID, "%s: invalid arg count", ao_lisp_poly_atom(name)->name);
106         return _ao_lisp_bool_true;
107 }
108
109 ao_poly
110 ao_lisp_arg(struct ao_lisp_cons *cons, int argc)
111 {
112         if (!cons)
113                 return AO_LISP_NIL;
114         while (argc--) {
115                 if (!cons)
116                         return AO_LISP_NIL;
117                 cons = ao_lisp_poly_cons(cons->cdr);
118         }
119         return cons->car;
120 }
121
122 ao_poly
123 ao_lisp_check_argt(ao_poly name, struct ao_lisp_cons *cons, int argc, int type, int nil_ok)
124 {
125         ao_poly car = ao_lisp_arg(cons, argc);
126
127         if ((!car && !nil_ok) || ao_lisp_poly_type(car) != type)
128                 return ao_lisp_error(AO_LISP_INVALID, "%s: invalid type for arg %d", ao_lisp_poly_atom(name)->name, argc);
129         return _ao_lisp_bool_true;
130 }
131
132 ao_poly
133 ao_lisp_do_car(struct ao_lisp_cons *cons)
134 {
135         if (!ao_lisp_check_argc(_ao_lisp_atom_car, cons, 1, 1))
136                 return AO_LISP_NIL;
137         if (!ao_lisp_check_argt(_ao_lisp_atom_car, cons, 0, AO_LISP_CONS, 0))
138                 return AO_LISP_NIL;
139         return ao_lisp_poly_cons(cons->car)->car;
140 }
141
142 ao_poly
143 ao_lisp_do_cdr(struct ao_lisp_cons *cons)
144 {
145         if (!ao_lisp_check_argc(_ao_lisp_atom_cdr, cons, 1, 1))
146                 return AO_LISP_NIL;
147         if (!ao_lisp_check_argt(_ao_lisp_atom_cdr, cons, 0, AO_LISP_CONS, 0))
148                 return AO_LISP_NIL;
149         return ao_lisp_poly_cons(cons->car)->cdr;
150 }
151
152 ao_poly
153 ao_lisp_do_cons(struct ao_lisp_cons *cons)
154 {
155         ao_poly car, cdr;
156         if(!ao_lisp_check_argc(_ao_lisp_atom_cons, cons, 2, 2))
157                 return AO_LISP_NIL;
158         car = ao_lisp_arg(cons, 0);
159         cdr = ao_lisp_arg(cons, 1);
160         return ao_lisp__cons(car, cdr);
161 }
162
163 ao_poly
164 ao_lisp_do_last(struct ao_lisp_cons *cons)
165 {
166         ao_poly l;
167         if (!ao_lisp_check_argc(_ao_lisp_atom_last, cons, 1, 1))
168                 return AO_LISP_NIL;
169         if (!ao_lisp_check_argt(_ao_lisp_atom_last, cons, 0, AO_LISP_CONS, 1))
170                 return AO_LISP_NIL;
171         l = ao_lisp_arg(cons, 0);
172         while (l) {
173                 struct ao_lisp_cons *list = ao_lisp_poly_cons(l);
174                 if (!list->cdr)
175                         return list->car;
176                 l = list->cdr;
177         }
178         return AO_LISP_NIL;
179 }
180
181 ao_poly
182 ao_lisp_do_length(struct ao_lisp_cons *cons)
183 {
184         if (!ao_lisp_check_argc(_ao_lisp_atom_length, cons, 1, 1))
185                 return AO_LISP_NIL;
186         if (!ao_lisp_check_argt(_ao_lisp_atom_length, cons, 0, AO_LISP_CONS, 1))
187                 return AO_LISP_NIL;
188         return ao_lisp_int_poly(ao_lisp_cons_length(ao_lisp_poly_cons(ao_lisp_arg(cons, 0))));
189 }
190
191 ao_poly
192 ao_lisp_do_quote(struct ao_lisp_cons *cons)
193 {
194         if (!ao_lisp_check_argc(_ao_lisp_atom_quote, cons, 1, 1))
195                 return AO_LISP_NIL;
196         return ao_lisp_arg(cons, 0);
197 }
198
199 ao_poly
200 ao_lisp_do_set(struct ao_lisp_cons *cons)
201 {
202         if (!ao_lisp_check_argc(_ao_lisp_atom_set, cons, 2, 2))
203                 return AO_LISP_NIL;
204         if (!ao_lisp_check_argt(_ao_lisp_atom_set, cons, 0, AO_LISP_ATOM, 0))
205                 return AO_LISP_NIL;
206
207         return ao_lisp_atom_set(ao_lisp_arg(cons, 0), ao_lisp_arg(cons, 1));
208 }
209
210 ao_poly
211 ao_lisp_do_setq(struct ao_lisp_cons *cons)
212 {
213         ao_poly name;
214         if (!ao_lisp_check_argc(_ao_lisp_atom_setq, cons, 2, 2))
215                 return AO_LISP_NIL;
216         name = cons->car;
217         if (ao_lisp_poly_type(name) != AO_LISP_ATOM)
218                 return ao_lisp_error(AO_LISP_INVALID, "set! of non-atom");
219         if (!ao_lisp_atom_ref(ao_lisp_frame_current, name))
220                 return ao_lisp_error(AO_LISP_INVALID, "atom not defined");
221         return ao_lisp__cons(_ao_lisp_atom_set,
222                              ao_lisp__cons(ao_lisp__cons(_ao_lisp_atom_quote,
223                                                          ao_lisp__cons(name, AO_LISP_NIL)),
224                                            cons->cdr));
225 }
226
227 ao_poly
228 ao_lisp_do_cond(struct ao_lisp_cons *cons)
229 {
230         ao_lisp_set_cond(cons);
231         return AO_LISP_NIL;
232 }
233
234 ao_poly
235 ao_lisp_do_progn(struct ao_lisp_cons *cons)
236 {
237         ao_lisp_stack->state = eval_progn;
238         ao_lisp_stack->sexprs = ao_lisp_cons_poly(cons);
239         return AO_LISP_NIL;
240 }
241
242 ao_poly
243 ao_lisp_do_while(struct ao_lisp_cons *cons)
244 {
245         ao_lisp_stack->state = eval_while;
246         ao_lisp_stack->sexprs = ao_lisp_cons_poly(cons);
247         return AO_LISP_NIL;
248 }
249
250 ao_poly
251 ao_lisp_do_print(struct ao_lisp_cons *cons)
252 {
253         ao_poly val = AO_LISP_NIL;
254         while (cons) {
255                 val = cons->car;
256                 ao_lisp_poly_print(val);
257                 cons = ao_lisp_poly_cons(cons->cdr);
258                 if (cons)
259                         printf(" ");
260         }
261         printf("\n");
262         return val;
263 }
264
265 ao_poly
266 ao_lisp_do_patom(struct ao_lisp_cons *cons)
267 {
268         ao_poly val = AO_LISP_NIL;
269         while (cons) {
270                 val = cons->car;
271                 ao_lisp_poly_patom(val);
272                 cons = ao_lisp_poly_cons(cons->cdr);
273         }
274         return val;
275 }
276
277 ao_poly
278 ao_lisp_math(struct ao_lisp_cons *cons, enum ao_lisp_builtin_id op)
279 {
280         ao_poly ret = AO_LISP_NIL;
281
282         while (cons) {
283                 ao_poly         car = cons->car;
284                 uint8_t         rt = ao_lisp_poly_type(ret);
285                 uint8_t         ct = ao_lisp_poly_type(car);
286
287                 cons = ao_lisp_poly_cons(cons->cdr);
288
289                 if (rt == AO_LISP_NIL)
290                         ret = car;
291
292                 else if (rt == AO_LISP_INT && ct == AO_LISP_INT) {
293                         int     r = ao_lisp_poly_int(ret);
294                         int     c = ao_lisp_poly_int(car);
295
296                         switch(op) {
297                         case builtin_plus:
298                                 r += c;
299                                 break;
300                         case builtin_minus:
301                                 r -= c;
302                                 break;
303                         case builtin_times:
304                                 r *= c;
305                                 break;
306                         case builtin_divide:
307                                 if (c == 0)
308                                         return ao_lisp_error(AO_LISP_DIVIDE_BY_ZERO, "divide by zero");
309                                 r /= c;
310                                 break;
311                         case builtin_mod:
312                                 if (c == 0)
313                                         return ao_lisp_error(AO_LISP_DIVIDE_BY_ZERO, "mod by zero");
314                                 r %= c;
315                                 break;
316                         default:
317                                 break;
318                         }
319                         ret = ao_lisp_int_poly(r);
320                 }
321
322                 else if (rt == AO_LISP_STRING && ct == AO_LISP_STRING && op == builtin_plus)
323                         ret = ao_lisp_string_poly(ao_lisp_string_cat(ao_lisp_poly_string(ret),
324                                                                      ao_lisp_poly_string(car)));
325                 else
326                         return ao_lisp_error(AO_LISP_INVALID, "invalid args");
327         }
328         return ret;
329 }
330
331 ao_poly
332 ao_lisp_do_plus(struct ao_lisp_cons *cons)
333 {
334         return ao_lisp_math(cons, builtin_plus);
335 }
336
337 ao_poly
338 ao_lisp_do_minus(struct ao_lisp_cons *cons)
339 {
340         return ao_lisp_math(cons, builtin_minus);
341 }
342
343 ao_poly
344 ao_lisp_do_times(struct ao_lisp_cons *cons)
345 {
346         return ao_lisp_math(cons, builtin_times);
347 }
348
349 ao_poly
350 ao_lisp_do_divide(struct ao_lisp_cons *cons)
351 {
352         return ao_lisp_math(cons, builtin_divide);
353 }
354
355 ao_poly
356 ao_lisp_do_mod(struct ao_lisp_cons *cons)
357 {
358         return ao_lisp_math(cons, builtin_mod);
359 }
360
361 ao_poly
362 ao_lisp_compare(struct ao_lisp_cons *cons, enum ao_lisp_builtin_id op)
363 {
364         ao_poly left;
365
366         if (!cons)
367                 return _ao_lisp_bool_true;
368
369         left = cons->car;
370         cons = ao_lisp_poly_cons(cons->cdr);
371         while (cons) {
372                 ao_poly right = cons->car;
373
374                 if (op == builtin_equal) {
375                         if (left != right)
376                                 return _ao_lisp_bool_false;
377                 } else {
378                         uint8_t lt = ao_lisp_poly_type(left);
379                         uint8_t rt = ao_lisp_poly_type(right);
380                         if (lt == AO_LISP_INT && rt == AO_LISP_INT) {
381                                 int l = ao_lisp_poly_int(left);
382                                 int r = ao_lisp_poly_int(right);
383
384                                 switch (op) {
385                                 case builtin_less:
386                                         if (!(l < r))
387                                                 return _ao_lisp_bool_false;
388                                         break;
389                                 case builtin_greater:
390                                         if (!(l > r))
391                                                 return _ao_lisp_bool_false;
392                                         break;
393                                 case builtin_less_equal:
394                                         if (!(l <= r))
395                                                 return _ao_lisp_bool_false;
396                                         break;
397                                 case builtin_greater_equal:
398                                         if (!(l >= r))
399                                                 return _ao_lisp_bool_false;
400                                         break;
401                                 default:
402                                         break;
403                                 }
404                         } else if (lt == AO_LISP_STRING && rt == AO_LISP_STRING) {
405                                 int c = strcmp(ao_lisp_poly_string(left),
406                                                ao_lisp_poly_string(right));
407                                 switch (op) {
408                                 case builtin_less:
409                                         if (!(c < 0))
410                                                 return _ao_lisp_bool_false;
411                                         break;
412                                 case builtin_greater:
413                                         if (!(c > 0))
414                                                 return _ao_lisp_bool_false;
415                                         break;
416                                 case builtin_less_equal:
417                                         if (!(c <= 0))
418                                                 return _ao_lisp_bool_false;
419                                         break;
420                                 case builtin_greater_equal:
421                                         if (!(c >= 0))
422                                                 return _ao_lisp_bool_false;
423                                         break;
424                                 default:
425                                         break;
426                                 }
427                         }
428                 }
429                 left = right;
430                 cons = ao_lisp_poly_cons(cons->cdr);
431         }
432         return _ao_lisp_bool_true;
433 }
434
435 ao_poly
436 ao_lisp_do_equal(struct ao_lisp_cons *cons)
437 {
438         return ao_lisp_compare(cons, builtin_equal);
439 }
440
441 ao_poly
442 ao_lisp_do_less(struct ao_lisp_cons *cons)
443 {
444         return ao_lisp_compare(cons, builtin_less);
445 }
446
447 ao_poly
448 ao_lisp_do_greater(struct ao_lisp_cons *cons)
449 {
450         return ao_lisp_compare(cons, builtin_greater);
451 }
452
453 ao_poly
454 ao_lisp_do_less_equal(struct ao_lisp_cons *cons)
455 {
456         return ao_lisp_compare(cons, builtin_less_equal);
457 }
458
459 ao_poly
460 ao_lisp_do_greater_equal(struct ao_lisp_cons *cons)
461 {
462         return ao_lisp_compare(cons, builtin_greater_equal);
463 }
464
465 ao_poly
466 ao_lisp_do_pack(struct ao_lisp_cons *cons)
467 {
468         if (!ao_lisp_check_argc(_ao_lisp_atom_pack, cons, 1, 1))
469                 return AO_LISP_NIL;
470         if (!ao_lisp_check_argt(_ao_lisp_atom_pack, cons, 0, AO_LISP_CONS, 1))
471                 return AO_LISP_NIL;
472         return ao_lisp_string_pack(ao_lisp_poly_cons(ao_lisp_arg(cons, 0)));
473 }
474
475 ao_poly
476 ao_lisp_do_unpack(struct ao_lisp_cons *cons)
477 {
478         if (!ao_lisp_check_argc(_ao_lisp_atom_unpack, cons, 1, 1))
479                 return AO_LISP_NIL;
480         if (!ao_lisp_check_argt(_ao_lisp_atom_unpack, cons, 0, AO_LISP_STRING, 0))
481                 return AO_LISP_NIL;
482         return ao_lisp_string_unpack(ao_lisp_poly_string(ao_lisp_arg(cons, 0)));
483 }
484
485 ao_poly
486 ao_lisp_do_flush(struct ao_lisp_cons *cons)
487 {
488         if (!ao_lisp_check_argc(_ao_lisp_atom_flush, cons, 0, 0))
489                 return AO_LISP_NIL;
490         ao_lisp_os_flush();
491         return _ao_lisp_bool_true;
492 }
493
494 ao_poly
495 ao_lisp_do_led(struct ao_lisp_cons *cons)
496 {
497         ao_poly led;
498         if (!ao_lisp_check_argc(_ao_lisp_atom_led, cons, 1, 1))
499                 return AO_LISP_NIL;
500         if (!ao_lisp_check_argt(_ao_lisp_atom_led, cons, 0, AO_LISP_INT, 0))
501                 return AO_LISP_NIL;
502         led = ao_lisp_arg(cons, 0);
503         ao_lisp_os_led(ao_lisp_poly_int(led));
504         return led;
505 }
506
507 ao_poly
508 ao_lisp_do_delay(struct ao_lisp_cons *cons)
509 {
510         ao_poly delay;
511         if (!ao_lisp_check_argc(_ao_lisp_atom_led, cons, 1, 1))
512                 return AO_LISP_NIL;
513         if (!ao_lisp_check_argt(_ao_lisp_atom_led, cons, 0, AO_LISP_INT, 0))
514                 return AO_LISP_NIL;
515         delay = ao_lisp_arg(cons, 0);
516         ao_lisp_os_delay(ao_lisp_poly_int(delay));
517         return delay;
518 }
519
520 ao_poly
521 ao_lisp_do_eval(struct ao_lisp_cons *cons)
522 {
523         if (!ao_lisp_check_argc(_ao_lisp_atom_eval, cons, 1, 1))
524                 return AO_LISP_NIL;
525         ao_lisp_stack->state = eval_sexpr;
526         return cons->car;
527 }
528
529 ao_poly
530 ao_lisp_do_read(struct ao_lisp_cons *cons)
531 {
532         if (!ao_lisp_check_argc(_ao_lisp_atom_read, cons, 0, 0))
533                 return AO_LISP_NIL;
534         return ao_lisp_read();
535 }
536
537 ao_poly
538 ao_lisp_do_collect(struct ao_lisp_cons *cons)
539 {
540         int     free;
541         (void) cons;
542         free = ao_lisp_collect(AO_LISP_COLLECT_FULL);
543         return ao_lisp_int_poly(free);
544 }
545
546 ao_poly
547 ao_lisp_do_nullp(struct ao_lisp_cons *cons)
548 {
549         if (!ao_lisp_check_argc(_ao_lisp_atom_led, cons, 1, 1))
550                 return AO_LISP_NIL;
551         if (ao_lisp_arg(cons, 0) == AO_LISP_NIL)
552                 return _ao_lisp_bool_true;
553         else
554                 return _ao_lisp_bool_false;
555 }
556
557 ao_poly
558 ao_lisp_do_not(struct ao_lisp_cons *cons)
559 {
560         if (!ao_lisp_check_argc(_ao_lisp_atom_led, cons, 1, 1))
561                 return AO_LISP_NIL;
562         if (ao_lisp_arg(cons, 0) == _ao_lisp_bool_false)
563                 return _ao_lisp_bool_true;
564         else
565                 return _ao_lisp_bool_false;
566 }
567
568 ao_poly
569 ao_lisp_do_listp(struct ao_lisp_cons *cons)
570 {
571         ao_poly v;
572         if (!ao_lisp_check_argc(_ao_lisp_atom_led, cons, 1, 1))
573                 return AO_LISP_NIL;
574         v = ao_lisp_arg(cons, 0);
575         for (;;) {
576                 if (v == AO_LISP_NIL)
577                         return _ao_lisp_bool_true;
578                 if (ao_lisp_poly_type(v) != AO_LISP_CONS)
579                         return _ao_lisp_bool_false;
580                 v = ao_lisp_poly_cons(v)->cdr;
581         }
582 }
583
584 ao_poly
585 ao_lisp_do_pairp(struct ao_lisp_cons *cons)
586 {
587         ao_poly v;
588         if (!ao_lisp_check_argc(_ao_lisp_atom_led, cons, 1, 1))
589                 return AO_LISP_NIL;
590         v = ao_lisp_arg(cons, 0);
591         if (ao_lisp_poly_type(v) == AO_LISP_CONS)
592                 return _ao_lisp_bool_true;
593         return _ao_lisp_bool_false;
594 }
595
596 #define AO_LISP_BUILTIN_FUNCS
597 #include "ao_lisp_builtin.h"