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