altos/lisp: Add non-cons cdr support
[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 char *ao_lisp_builtin_name(enum ao_lisp_builtin_id b) {
44         (void) b;
45         return "???";
46 }
47 char *ao_lisp_args_name(uint8_t args) {
48         (void) args;
49         return "???";
50 }
51 #else
52 static const ao_poly builtin_names[] = {
53         [builtin_eval] = _ao_lisp_atom_eval,
54         [builtin_read] = _ao_lisp_atom_read,
55         [builtin_lambda] = _ao_lisp_atom_lambda,
56         [builtin_lexpr] = _ao_lisp_atom_lexpr,
57         [builtin_nlambda] = _ao_lisp_atom_nlambda,
58         [builtin_macro] = _ao_lisp_atom_macro,
59         [builtin_car] = _ao_lisp_atom_car,
60         [builtin_cdr] = _ao_lisp_atom_cdr,
61         [builtin_cons] = _ao_lisp_atom_cons,
62         [builtin_last] = _ao_lisp_atom_last,
63         [builtin_length] = _ao_lisp_atom_length,
64         [builtin_quote] = _ao_lisp_atom_quote,
65         [builtin_set] = _ao_lisp_atom_set,
66         [builtin_setq] = _ao_lisp_atom_setq,
67         [builtin_cond] = _ao_lisp_atom_cond,
68         [builtin_progn] = _ao_lisp_atom_progn,
69         [builtin_while] = _ao_lisp_atom_while,
70         [builtin_print] = _ao_lisp_atom_print,
71         [builtin_patom] = _ao_lisp_atom_patom,
72         [builtin_plus] = _ao_lisp_atom_2b,
73         [builtin_minus] = _ao_lisp_atom_2d,
74         [builtin_times] = _ao_lisp_atom_2a,
75         [builtin_divide] = _ao_lisp_atom_2f,
76         [builtin_mod] = _ao_lisp_atom_25,
77         [builtin_equal] = _ao_lisp_atom_3d,
78         [builtin_less] = _ao_lisp_atom_3c,
79         [builtin_greater] = _ao_lisp_atom_3e,
80         [builtin_less_equal] = _ao_lisp_atom_3c3d,
81         [builtin_greater_equal] = _ao_lisp_atom_3e3d,
82         [builtin_pack] = _ao_lisp_atom_pack,
83         [builtin_unpack] = _ao_lisp_atom_unpack,
84         [builtin_flush] = _ao_lisp_atom_flush,
85         [builtin_delay] = _ao_lisp_atom_delay,
86         [builtin_led] = _ao_lisp_atom_led,
87         [builtin_save] = _ao_lisp_atom_save,
88         [builtin_restore] = _ao_lisp_atom_restore,
89         [builtin_call_cc] = _ao_lisp_atom_call2fcc,
90         [builtin_collect] = _ao_lisp_atom_collect,
91 #if 0
92         [builtin_symbolp] = _ao_lisp_atom_symbolp,
93         [builtin_listp] = _ao_lisp_atom_listp,
94         [builtin_stringp] = _ao_lisp_atom_stringp,
95         [builtin_numberp] = _ao_lisp_atom_numberp,
96 #endif
97 };
98
99 static char *
100 ao_lisp_builtin_name(enum ao_lisp_builtin_id b) {
101         if (b < _builtin_last)
102                 return ao_lisp_poly_atom(builtin_names[b])->name;
103         return "???";
104 }
105
106 static const ao_poly ao_lisp_args_atoms[] = {
107         [AO_LISP_FUNC_LAMBDA] = _ao_lisp_atom_lambda,
108         [AO_LISP_FUNC_LEXPR] = _ao_lisp_atom_lexpr,
109         [AO_LISP_FUNC_NLAMBDA] = _ao_lisp_atom_nlambda,
110         [AO_LISP_FUNC_MACRO] = _ao_lisp_atom_macro,
111 };
112
113 char *
114 ao_lisp_args_name(uint8_t args)
115 {
116         args &= AO_LISP_FUNC_MASK;
117         if (args < sizeof ao_lisp_args_atoms / sizeof ao_lisp_args_atoms[0])
118                 return ao_lisp_poly_atom(ao_lisp_args_atoms[args])->name;
119         return "(unknown)";
120 }
121 #endif
122
123 void
124 ao_lisp_builtin_print(ao_poly b)
125 {
126         struct ao_lisp_builtin *builtin = ao_lisp_poly_builtin(b);
127         printf("%s", ao_lisp_builtin_name(builtin->func));
128 }
129
130 ao_poly
131 ao_lisp_check_argc(ao_poly name, struct ao_lisp_cons *cons, int min, int max)
132 {
133         int     argc = 0;
134
135         while (cons && argc <= max) {
136                 argc++;
137                 cons = ao_lisp_poly_cons(cons->cdr);
138         }
139         if (argc < min || argc > max)
140                 return ao_lisp_error(AO_LISP_INVALID, "%s: invalid arg count", ao_lisp_poly_atom(name)->name);
141         return _ao_lisp_atom_t;
142 }
143
144 ao_poly
145 ao_lisp_arg(struct ao_lisp_cons *cons, int argc)
146 {
147         if (!cons)
148                 return AO_LISP_NIL;
149         while (argc--) {
150                 if (!cons)
151                         return AO_LISP_NIL;
152                 cons = ao_lisp_poly_cons(cons->cdr);
153         }
154         return cons->car;
155 }
156
157 ao_poly
158 ao_lisp_check_argt(ao_poly name, struct ao_lisp_cons *cons, int argc, int type, int nil_ok)
159 {
160         ao_poly car = ao_lisp_arg(cons, argc);
161
162         if ((!car && !nil_ok) || ao_lisp_poly_type(car) != type)
163                 return ao_lisp_error(AO_LISP_INVALID, "%s: invalid type for arg %d", ao_lisp_poly_atom(name)->name, argc);
164         return _ao_lisp_atom_t;
165 }
166
167 ao_poly
168 ao_lisp_car(struct ao_lisp_cons *cons)
169 {
170         if (!ao_lisp_check_argc(_ao_lisp_atom_car, cons, 1, 1))
171                 return AO_LISP_NIL;
172         if (!ao_lisp_check_argt(_ao_lisp_atom_car, cons, 0, AO_LISP_CONS, 0))
173                 return AO_LISP_NIL;
174         return ao_lisp_poly_cons(cons->car)->car;
175 }
176
177 ao_poly
178 ao_lisp_cdr(struct ao_lisp_cons *cons)
179 {
180         if (!ao_lisp_check_argc(_ao_lisp_atom_cdr, cons, 1, 1))
181                 return AO_LISP_NIL;
182         if (!ao_lisp_check_argt(_ao_lisp_atom_cdr, cons, 0, AO_LISP_CONS, 0))
183                 return AO_LISP_NIL;
184         return ao_lisp_poly_cons(cons->car)->cdr;
185 }
186
187 ao_poly
188 ao_lisp_cons(struct ao_lisp_cons *cons)
189 {
190         ao_poly car, cdr;
191         if(!ao_lisp_check_argc(_ao_lisp_atom_cons, cons, 2, 2))
192                 return AO_LISP_NIL;
193         car = ao_lisp_arg(cons, 0);
194         cdr = ao_lisp_arg(cons, 1);
195         return ao_lisp__cons(car, cdr);
196 }
197
198 ao_poly
199 ao_lisp_last(struct ao_lisp_cons *cons)
200 {
201         ao_poly l;
202         if (!ao_lisp_check_argc(_ao_lisp_atom_last, cons, 1, 1))
203                 return AO_LISP_NIL;
204         if (!ao_lisp_check_argt(_ao_lisp_atom_last, cons, 0, AO_LISP_CONS, 1))
205                 return AO_LISP_NIL;
206         l = ao_lisp_arg(cons, 0);
207         while (l) {
208                 struct ao_lisp_cons *list = ao_lisp_poly_cons(l);
209                 if (!list->cdr)
210                         return list->car;
211                 l = list->cdr;
212         }
213         return AO_LISP_NIL;
214 }
215
216 ao_poly
217 ao_lisp_length(struct ao_lisp_cons *cons)
218 {
219         if (!ao_lisp_check_argc(_ao_lisp_atom_length, cons, 1, 1))
220                 return AO_LISP_NIL;
221         if (!ao_lisp_check_argt(_ao_lisp_atom_length, cons, 0, AO_LISP_CONS, 1))
222                 return AO_LISP_NIL;
223         return ao_lisp_int_poly(ao_lisp_cons_length(ao_lisp_poly_cons(ao_lisp_arg(cons, 0))));
224 }
225
226 ao_poly
227 ao_lisp_quote(struct ao_lisp_cons *cons)
228 {
229         if (!ao_lisp_check_argc(_ao_lisp_atom_quote, cons, 1, 1))
230                 return AO_LISP_NIL;
231         return ao_lisp_arg(cons, 0);
232 }
233
234 ao_poly
235 ao_lisp_set(struct ao_lisp_cons *cons)
236 {
237         if (!ao_lisp_check_argc(_ao_lisp_atom_set, cons, 2, 2))
238                 return AO_LISP_NIL;
239         if (!ao_lisp_check_argt(_ao_lisp_atom_set, cons, 0, AO_LISP_ATOM, 0))
240                 return AO_LISP_NIL;
241
242         return ao_lisp_atom_set(ao_lisp_arg(cons, 0), ao_lisp_arg(cons, 1));
243 }
244
245 ao_poly
246 ao_lisp_setq(struct ao_lisp_cons *cons)
247 {
248         if (!ao_lisp_check_argc(_ao_lisp_atom_setq, cons, 2, 2))
249                 return AO_LISP_NIL;
250         return ao_lisp__cons(_ao_lisp_atom_set,
251                              ao_lisp__cons(ao_lisp__cons(_ao_lisp_atom_quote,
252                                                          ao_lisp__cons(cons->car, AO_LISP_NIL)),
253                                            cons->cdr));
254 }
255
256 ao_poly
257 ao_lisp_cond(struct ao_lisp_cons *cons)
258 {
259         ao_lisp_set_cond(cons);
260         return AO_LISP_NIL;
261 }
262
263 ao_poly
264 ao_lisp_progn(struct ao_lisp_cons *cons)
265 {
266         ao_lisp_stack->state = eval_progn;
267         ao_lisp_stack->sexprs = ao_lisp_cons_poly(cons);
268         return AO_LISP_NIL;
269 }
270
271 ao_poly
272 ao_lisp_while(struct ao_lisp_cons *cons)
273 {
274         ao_lisp_stack->state = eval_while;
275         ao_lisp_stack->sexprs = ao_lisp_cons_poly(cons);
276         return AO_LISP_NIL;
277 }
278
279 ao_poly
280 ao_lisp_print(struct ao_lisp_cons *cons)
281 {
282         ao_poly val = AO_LISP_NIL;
283         while (cons) {
284                 val = cons->car;
285                 ao_lisp_poly_print(val);
286                 cons = ao_lisp_poly_cons(cons->cdr);
287                 if (cons)
288                         printf(" ");
289         }
290         printf("\n");
291         return val;
292 }
293
294 ao_poly
295 ao_lisp_patom(struct ao_lisp_cons *cons)
296 {
297         ao_poly val = AO_LISP_NIL;
298         while (cons) {
299                 val = cons->car;
300                 ao_lisp_poly_patom(val);
301                 cons = ao_lisp_poly_cons(cons->cdr);
302         }
303         return val;
304 }
305
306 ao_poly
307 ao_lisp_math(struct ao_lisp_cons *cons, enum ao_lisp_builtin_id op)
308 {
309         ao_poly ret = AO_LISP_NIL;
310
311         while (cons) {
312                 ao_poly         car = cons->car;
313                 uint8_t         rt = ao_lisp_poly_type(ret);
314                 uint8_t         ct = ao_lisp_poly_type(car);
315
316                 cons = ao_lisp_poly_cons(cons->cdr);
317
318                 if (rt == AO_LISP_NIL)
319                         ret = car;
320
321                 else if (rt == AO_LISP_INT && ct == AO_LISP_INT) {
322                         int     r = ao_lisp_poly_int(ret);
323                         int     c = ao_lisp_poly_int(car);
324
325                         switch(op) {
326                         case builtin_plus:
327                                 r += c;
328                                 break;
329                         case builtin_minus:
330                                 r -= c;
331                                 break;
332                         case builtin_times:
333                                 r *= c;
334                                 break;
335                         case builtin_divide:
336                                 if (c == 0)
337                                         return ao_lisp_error(AO_LISP_DIVIDE_BY_ZERO, "divide by zero");
338                                 r /= c;
339                                 break;
340                         case builtin_mod:
341                                 if (c == 0)
342                                         return ao_lisp_error(AO_LISP_DIVIDE_BY_ZERO, "mod by zero");
343                                 r %= c;
344                                 break;
345                         default:
346                                 break;
347                         }
348                         ret = ao_lisp_int_poly(r);
349                 }
350
351                 else if (rt == AO_LISP_STRING && ct == AO_LISP_STRING && op == builtin_plus)
352                         ret = ao_lisp_string_poly(ao_lisp_string_cat(ao_lisp_poly_string(ret),
353                                                                      ao_lisp_poly_string(car)));
354                 else
355                         return ao_lisp_error(AO_LISP_INVALID, "invalid args");
356         }
357         return ret;
358 }
359
360 ao_poly
361 ao_lisp_plus(struct ao_lisp_cons *cons)
362 {
363         return ao_lisp_math(cons, builtin_plus);
364 }
365
366 ao_poly
367 ao_lisp_minus(struct ao_lisp_cons *cons)
368 {
369         return ao_lisp_math(cons, builtin_minus);
370 }
371
372 ao_poly
373 ao_lisp_times(struct ao_lisp_cons *cons)
374 {
375         return ao_lisp_math(cons, builtin_times);
376 }
377
378 ao_poly
379 ao_lisp_divide(struct ao_lisp_cons *cons)
380 {
381         return ao_lisp_math(cons, builtin_divide);
382 }
383
384 ao_poly
385 ao_lisp_mod(struct ao_lisp_cons *cons)
386 {
387         return ao_lisp_math(cons, builtin_mod);
388 }
389
390 ao_poly
391 ao_lisp_compare(struct ao_lisp_cons *cons, enum ao_lisp_builtin_id op)
392 {
393         ao_poly left;
394
395         if (!cons)
396                 return _ao_lisp_atom_t;
397
398         left = cons->car;
399         cons = ao_lisp_poly_cons(cons->cdr);
400         while (cons) {
401                 ao_poly right = cons->car;
402
403                 if (op == builtin_equal) {
404                         if (left != right)
405                                 return AO_LISP_NIL;
406                 } else {
407                         uint8_t lt = ao_lisp_poly_type(left);
408                         uint8_t rt = ao_lisp_poly_type(right);
409                         if (lt == AO_LISP_INT && rt == AO_LISP_INT) {
410                                 int l = ao_lisp_poly_int(left);
411                                 int r = ao_lisp_poly_int(right);
412
413                                 switch (op) {
414                                 case builtin_less:
415                                         if (!(l < r))
416                                                 return AO_LISP_NIL;
417                                         break;
418                                 case builtin_greater:
419                                         if (!(l > r))
420                                                 return AO_LISP_NIL;
421                                         break;
422                                 case builtin_less_equal:
423                                         if (!(l <= r))
424                                                 return AO_LISP_NIL;
425                                         break;
426                                 case builtin_greater_equal:
427                                         if (!(l >= r))
428                                                 return AO_LISP_NIL;
429                                         break;
430                                 default:
431                                         break;
432                                 }
433                         } else if (lt == AO_LISP_STRING && rt == AO_LISP_STRING) {
434                                 int c = strcmp(ao_lisp_poly_string(left),
435                                                ao_lisp_poly_string(right));
436                                 switch (op) {
437                                 case builtin_less:
438                                         if (!(c < 0))
439                                                 return AO_LISP_NIL;
440                                         break;
441                                 case builtin_greater:
442                                         if (!(c > 0))
443                                                 return AO_LISP_NIL;
444                                         break;
445                                 case builtin_less_equal:
446                                         if (!(c <= 0))
447                                                 return AO_LISP_NIL;
448                                         break;
449                                 case builtin_greater_equal:
450                                         if (!(c >= 0))
451                                                 return AO_LISP_NIL;
452                                         break;
453                                 default:
454                                         break;
455                                 }
456                         }
457                 }
458                 left = right;
459                 cons = ao_lisp_poly_cons(cons->cdr);
460         }
461         return _ao_lisp_atom_t;
462 }
463
464 ao_poly
465 ao_lisp_equal(struct ao_lisp_cons *cons)
466 {
467         return ao_lisp_compare(cons, builtin_equal);
468 }
469
470 ao_poly
471 ao_lisp_less(struct ao_lisp_cons *cons)
472 {
473         return ao_lisp_compare(cons, builtin_less);
474 }
475
476 ao_poly
477 ao_lisp_greater(struct ao_lisp_cons *cons)
478 {
479         return ao_lisp_compare(cons, builtin_greater);
480 }
481
482 ao_poly
483 ao_lisp_less_equal(struct ao_lisp_cons *cons)
484 {
485         return ao_lisp_compare(cons, builtin_less_equal);
486 }
487
488 ao_poly
489 ao_lisp_greater_equal(struct ao_lisp_cons *cons)
490 {
491         return ao_lisp_compare(cons, builtin_greater_equal);
492 }
493
494 ao_poly
495 ao_lisp_pack(struct ao_lisp_cons *cons)
496 {
497         if (!ao_lisp_check_argc(_ao_lisp_atom_pack, cons, 1, 1))
498                 return AO_LISP_NIL;
499         if (!ao_lisp_check_argt(_ao_lisp_atom_pack, cons, 0, AO_LISP_CONS, 1))
500                 return AO_LISP_NIL;
501         return ao_lisp_string_pack(ao_lisp_poly_cons(ao_lisp_arg(cons, 0)));
502 }
503
504 ao_poly
505 ao_lisp_unpack(struct ao_lisp_cons *cons)
506 {
507         if (!ao_lisp_check_argc(_ao_lisp_atom_unpack, cons, 1, 1))
508                 return AO_LISP_NIL;
509         if (!ao_lisp_check_argt(_ao_lisp_atom_unpack, cons, 0, AO_LISP_STRING, 0))
510                 return AO_LISP_NIL;
511         return ao_lisp_string_unpack(ao_lisp_poly_string(ao_lisp_arg(cons, 0)));
512 }
513
514 ao_poly
515 ao_lisp_flush(struct ao_lisp_cons *cons)
516 {
517         if (!ao_lisp_check_argc(_ao_lisp_atom_flush, cons, 0, 0))
518                 return AO_LISP_NIL;
519         ao_lisp_os_flush();
520         return _ao_lisp_atom_t;
521 }
522
523 ao_poly
524 ao_lisp_led(struct ao_lisp_cons *cons)
525 {
526         ao_poly led;
527         if (!ao_lisp_check_argc(_ao_lisp_atom_led, cons, 1, 1))
528                 return AO_LISP_NIL;
529         if (!ao_lisp_check_argt(_ao_lisp_atom_led, cons, 0, AO_LISP_INT, 0))
530                 return AO_LISP_NIL;
531         led = ao_lisp_arg(cons, 0);
532         ao_lisp_os_led(ao_lisp_poly_int(led));
533         return led;
534 }
535
536 ao_poly
537 ao_lisp_delay(struct ao_lisp_cons *cons)
538 {
539         ao_poly delay;
540         if (!ao_lisp_check_argc(_ao_lisp_atom_led, cons, 1, 1))
541                 return AO_LISP_NIL;
542         if (!ao_lisp_check_argt(_ao_lisp_atom_led, cons, 0, AO_LISP_INT, 0))
543                 return AO_LISP_NIL;
544         delay = ao_lisp_arg(cons, 0);
545         ao_lisp_os_delay(ao_lisp_poly_int(delay));
546         return delay;
547 }
548
549 ao_poly
550 ao_lisp_do_eval(struct ao_lisp_cons *cons)
551 {
552         if (!ao_lisp_check_argc(_ao_lisp_atom_eval, cons, 1, 1))
553                 return AO_LISP_NIL;
554         ao_lisp_stack->state = eval_sexpr;
555         return cons->car;
556 }
557
558 ao_poly
559 ao_lisp_do_read(struct ao_lisp_cons *cons)
560 {
561         if (!ao_lisp_check_argc(_ao_lisp_atom_read, cons, 0, 0))
562                 return AO_LISP_NIL;
563         return ao_lisp_read();
564 }
565
566 ao_poly
567 ao_lisp_do_collect(struct ao_lisp_cons *cons)
568 {
569         int     free;
570         (void) cons;
571         free = ao_lisp_collect(AO_LISP_COLLECT_FULL);
572         return ao_lisp_int_poly(free);
573 }
574
575 const ao_lisp_func_t ao_lisp_builtins[] = {
576         [builtin_eval] = ao_lisp_do_eval,
577         [builtin_read] = ao_lisp_do_read,
578         [builtin_lambda] = ao_lisp_lambda,
579         [builtin_lexpr] = ao_lisp_lexpr,
580         [builtin_nlambda] = ao_lisp_nlambda,
581         [builtin_macro] = ao_lisp_macro,
582         [builtin_car] = ao_lisp_car,
583         [builtin_cdr] = ao_lisp_cdr,
584         [builtin_cons] = ao_lisp_cons,
585         [builtin_last] = ao_lisp_last,
586         [builtin_length] = ao_lisp_length,
587         [builtin_quote] = ao_lisp_quote,
588         [builtin_set] = ao_lisp_set,
589         [builtin_setq] = ao_lisp_setq,
590         [builtin_cond] = ao_lisp_cond,
591         [builtin_progn] = ao_lisp_progn,
592         [builtin_while] = ao_lisp_while,
593         [builtin_print] = ao_lisp_print,
594         [builtin_patom] = ao_lisp_patom,
595         [builtin_plus] = ao_lisp_plus,
596         [builtin_minus] = ao_lisp_minus,
597         [builtin_times] = ao_lisp_times,
598         [builtin_divide] = ao_lisp_divide,
599         [builtin_mod] = ao_lisp_mod,
600         [builtin_equal] = ao_lisp_equal,
601         [builtin_less] = ao_lisp_less,
602         [builtin_greater] = ao_lisp_greater,
603         [builtin_less_equal] = ao_lisp_less_equal,
604         [builtin_greater_equal] = ao_lisp_greater_equal,
605         [builtin_pack] = ao_lisp_pack,
606         [builtin_unpack] = ao_lisp_unpack,
607         [builtin_flush] = ao_lisp_flush,
608         [builtin_led] = ao_lisp_led,
609         [builtin_delay] = ao_lisp_delay,
610         [builtin_save] = ao_lisp_save,
611         [builtin_restore] = ao_lisp_restore,
612         [builtin_call_cc] = ao_lisp_call_cc,
613         [builtin_collect] = ao_lisp_do_collect,
614 };
615