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