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