6fc28820d403f1bd3d44ad77609d00fafa6737d7
[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         if (!ao_lisp_check_argc(_ao_lisp_atom_setq, cons, 2, 2))
214                 return AO_LISP_NIL;
215         return ao_lisp__cons(_ao_lisp_atom_set,
216                              ao_lisp__cons(ao_lisp__cons(_ao_lisp_atom_quote,
217                                                          ao_lisp__cons(cons->car, AO_LISP_NIL)),
218                                            cons->cdr));
219 }
220
221 ao_poly
222 ao_lisp_do_cond(struct ao_lisp_cons *cons)
223 {
224         ao_lisp_set_cond(cons);
225         return AO_LISP_NIL;
226 }
227
228 ao_poly
229 ao_lisp_do_progn(struct ao_lisp_cons *cons)
230 {
231         ao_lisp_stack->state = eval_progn;
232         ao_lisp_stack->sexprs = ao_lisp_cons_poly(cons);
233         return AO_LISP_NIL;
234 }
235
236 ao_poly
237 ao_lisp_do_while(struct ao_lisp_cons *cons)
238 {
239         ao_lisp_stack->state = eval_while;
240         ao_lisp_stack->sexprs = ao_lisp_cons_poly(cons);
241         return AO_LISP_NIL;
242 }
243
244 ao_poly
245 ao_lisp_do_print(struct ao_lisp_cons *cons)
246 {
247         ao_poly val = AO_LISP_NIL;
248         while (cons) {
249                 val = cons->car;
250                 ao_lisp_poly_print(val);
251                 cons = ao_lisp_poly_cons(cons->cdr);
252                 if (cons)
253                         printf(" ");
254         }
255         printf("\n");
256         return val;
257 }
258
259 ao_poly
260 ao_lisp_do_patom(struct ao_lisp_cons *cons)
261 {
262         ao_poly val = AO_LISP_NIL;
263         while (cons) {
264                 val = cons->car;
265                 ao_lisp_poly_patom(val);
266                 cons = ao_lisp_poly_cons(cons->cdr);
267         }
268         return val;
269 }
270
271 ao_poly
272 ao_lisp_math(struct ao_lisp_cons *cons, enum ao_lisp_builtin_id op)
273 {
274         ao_poly ret = AO_LISP_NIL;
275
276         while (cons) {
277                 ao_poly         car = cons->car;
278                 uint8_t         rt = ao_lisp_poly_type(ret);
279                 uint8_t         ct = ao_lisp_poly_type(car);
280
281                 cons = ao_lisp_poly_cons(cons->cdr);
282
283                 if (rt == AO_LISP_NIL)
284                         ret = car;
285
286                 else if (rt == AO_LISP_INT && ct == AO_LISP_INT) {
287                         int     r = ao_lisp_poly_int(ret);
288                         int     c = ao_lisp_poly_int(car);
289
290                         switch(op) {
291                         case builtin_plus:
292                                 r += c;
293                                 break;
294                         case builtin_minus:
295                                 r -= c;
296                                 break;
297                         case builtin_times:
298                                 r *= c;
299                                 break;
300                         case builtin_divide:
301                                 if (c == 0)
302                                         return ao_lisp_error(AO_LISP_DIVIDE_BY_ZERO, "divide by zero");
303                                 r /= c;
304                                 break;
305                         case builtin_mod:
306                                 if (c == 0)
307                                         return ao_lisp_error(AO_LISP_DIVIDE_BY_ZERO, "mod by zero");
308                                 r %= c;
309                                 break;
310                         default:
311                                 break;
312                         }
313                         ret = ao_lisp_int_poly(r);
314                 }
315
316                 else if (rt == AO_LISP_STRING && ct == AO_LISP_STRING && op == builtin_plus)
317                         ret = ao_lisp_string_poly(ao_lisp_string_cat(ao_lisp_poly_string(ret),
318                                                                      ao_lisp_poly_string(car)));
319                 else
320                         return ao_lisp_error(AO_LISP_INVALID, "invalid args");
321         }
322         return ret;
323 }
324
325 ao_poly
326 ao_lisp_do_plus(struct ao_lisp_cons *cons)
327 {
328         return ao_lisp_math(cons, builtin_plus);
329 }
330
331 ao_poly
332 ao_lisp_do_minus(struct ao_lisp_cons *cons)
333 {
334         return ao_lisp_math(cons, builtin_minus);
335 }
336
337 ao_poly
338 ao_lisp_do_times(struct ao_lisp_cons *cons)
339 {
340         return ao_lisp_math(cons, builtin_times);
341 }
342
343 ao_poly
344 ao_lisp_do_divide(struct ao_lisp_cons *cons)
345 {
346         return ao_lisp_math(cons, builtin_divide);
347 }
348
349 ao_poly
350 ao_lisp_do_mod(struct ao_lisp_cons *cons)
351 {
352         return ao_lisp_math(cons, builtin_mod);
353 }
354
355 ao_poly
356 ao_lisp_compare(struct ao_lisp_cons *cons, enum ao_lisp_builtin_id op)
357 {
358         ao_poly left;
359
360         if (!cons)
361                 return _ao_lisp_bool_true;
362
363         left = cons->car;
364         cons = ao_lisp_poly_cons(cons->cdr);
365         while (cons) {
366                 ao_poly right = cons->car;
367
368                 if (op == builtin_equal) {
369                         if (left != right)
370                                 return _ao_lisp_bool_false;
371                 } else {
372                         uint8_t lt = ao_lisp_poly_type(left);
373                         uint8_t rt = ao_lisp_poly_type(right);
374                         if (lt == AO_LISP_INT && rt == AO_LISP_INT) {
375                                 int l = ao_lisp_poly_int(left);
376                                 int r = ao_lisp_poly_int(right);
377
378                                 switch (op) {
379                                 case builtin_less:
380                                         if (!(l < r))
381                                                 return _ao_lisp_bool_false;
382                                         break;
383                                 case builtin_greater:
384                                         if (!(l > r))
385                                                 return _ao_lisp_bool_false;
386                                         break;
387                                 case builtin_less_equal:
388                                         if (!(l <= r))
389                                                 return _ao_lisp_bool_false;
390                                         break;
391                                 case builtin_greater_equal:
392                                         if (!(l >= r))
393                                                 return _ao_lisp_bool_false;
394                                         break;
395                                 default:
396                                         break;
397                                 }
398                         } else if (lt == AO_LISP_STRING && rt == AO_LISP_STRING) {
399                                 int c = strcmp(ao_lisp_poly_string(left),
400                                                ao_lisp_poly_string(right));
401                                 switch (op) {
402                                 case builtin_less:
403                                         if (!(c < 0))
404                                                 return _ao_lisp_bool_false;
405                                         break;
406                                 case builtin_greater:
407                                         if (!(c > 0))
408                                                 return _ao_lisp_bool_false;
409                                         break;
410                                 case builtin_less_equal:
411                                         if (!(c <= 0))
412                                                 return _ao_lisp_bool_false;
413                                         break;
414                                 case builtin_greater_equal:
415                                         if (!(c >= 0))
416                                                 return _ao_lisp_bool_false;
417                                         break;
418                                 default:
419                                         break;
420                                 }
421                         }
422                 }
423                 left = right;
424                 cons = ao_lisp_poly_cons(cons->cdr);
425         }
426         return _ao_lisp_bool_true;
427 }
428
429 ao_poly
430 ao_lisp_do_equal(struct ao_lisp_cons *cons)
431 {
432         return ao_lisp_compare(cons, builtin_equal);
433 }
434
435 ao_poly
436 ao_lisp_do_less(struct ao_lisp_cons *cons)
437 {
438         return ao_lisp_compare(cons, builtin_less);
439 }
440
441 ao_poly
442 ao_lisp_do_greater(struct ao_lisp_cons *cons)
443 {
444         return ao_lisp_compare(cons, builtin_greater);
445 }
446
447 ao_poly
448 ao_lisp_do_less_equal(struct ao_lisp_cons *cons)
449 {
450         return ao_lisp_compare(cons, builtin_less_equal);
451 }
452
453 ao_poly
454 ao_lisp_do_greater_equal(struct ao_lisp_cons *cons)
455 {
456         return ao_lisp_compare(cons, builtin_greater_equal);
457 }
458
459 ao_poly
460 ao_lisp_do_pack(struct ao_lisp_cons *cons)
461 {
462         if (!ao_lisp_check_argc(_ao_lisp_atom_pack, cons, 1, 1))
463                 return AO_LISP_NIL;
464         if (!ao_lisp_check_argt(_ao_lisp_atom_pack, cons, 0, AO_LISP_CONS, 1))
465                 return AO_LISP_NIL;
466         return ao_lisp_string_pack(ao_lisp_poly_cons(ao_lisp_arg(cons, 0)));
467 }
468
469 ao_poly
470 ao_lisp_do_unpack(struct ao_lisp_cons *cons)
471 {
472         if (!ao_lisp_check_argc(_ao_lisp_atom_unpack, cons, 1, 1))
473                 return AO_LISP_NIL;
474         if (!ao_lisp_check_argt(_ao_lisp_atom_unpack, cons, 0, AO_LISP_STRING, 0))
475                 return AO_LISP_NIL;
476         return ao_lisp_string_unpack(ao_lisp_poly_string(ao_lisp_arg(cons, 0)));
477 }
478
479 ao_poly
480 ao_lisp_do_flush(struct ao_lisp_cons *cons)
481 {
482         if (!ao_lisp_check_argc(_ao_lisp_atom_flush, cons, 0, 0))
483                 return AO_LISP_NIL;
484         ao_lisp_os_flush();
485         return _ao_lisp_bool_true;
486 }
487
488 ao_poly
489 ao_lisp_do_led(struct ao_lisp_cons *cons)
490 {
491         ao_poly led;
492         if (!ao_lisp_check_argc(_ao_lisp_atom_led, cons, 1, 1))
493                 return AO_LISP_NIL;
494         if (!ao_lisp_check_argt(_ao_lisp_atom_led, cons, 0, AO_LISP_INT, 0))
495                 return AO_LISP_NIL;
496         led = ao_lisp_arg(cons, 0);
497         ao_lisp_os_led(ao_lisp_poly_int(led));
498         return led;
499 }
500
501 ao_poly
502 ao_lisp_do_delay(struct ao_lisp_cons *cons)
503 {
504         ao_poly delay;
505         if (!ao_lisp_check_argc(_ao_lisp_atom_led, cons, 1, 1))
506                 return AO_LISP_NIL;
507         if (!ao_lisp_check_argt(_ao_lisp_atom_led, cons, 0, AO_LISP_INT, 0))
508                 return AO_LISP_NIL;
509         delay = ao_lisp_arg(cons, 0);
510         ao_lisp_os_delay(ao_lisp_poly_int(delay));
511         return delay;
512 }
513
514 ao_poly
515 ao_lisp_do_eval(struct ao_lisp_cons *cons)
516 {
517         if (!ao_lisp_check_argc(_ao_lisp_atom_eval, cons, 1, 1))
518                 return AO_LISP_NIL;
519         ao_lisp_stack->state = eval_sexpr;
520         return cons->car;
521 }
522
523 ao_poly
524 ao_lisp_do_read(struct ao_lisp_cons *cons)
525 {
526         if (!ao_lisp_check_argc(_ao_lisp_atom_read, cons, 0, 0))
527                 return AO_LISP_NIL;
528         return ao_lisp_read();
529 }
530
531 ao_poly
532 ao_lisp_do_collect(struct ao_lisp_cons *cons)
533 {
534         int     free;
535         (void) cons;
536         free = ao_lisp_collect(AO_LISP_COLLECT_FULL);
537         return ao_lisp_int_poly(free);
538 }
539
540 ao_poly
541 ao_lisp_do_nullp(struct ao_lisp_cons *cons)
542 {
543         if (!ao_lisp_check_argc(_ao_lisp_atom_led, cons, 1, 1))
544                 return AO_LISP_NIL;
545         if (ao_lisp_arg(cons, 0) == AO_LISP_NIL)
546                 return _ao_lisp_bool_true;
547         else
548                 return _ao_lisp_bool_false;
549 }
550
551 ao_poly
552 ao_lisp_do_not(struct ao_lisp_cons *cons)
553 {
554         if (!ao_lisp_check_argc(_ao_lisp_atom_led, cons, 1, 1))
555                 return AO_LISP_NIL;
556         if (ao_lisp_arg(cons, 0) == _ao_lisp_bool_false)
557                 return _ao_lisp_bool_true;
558         else
559                 return _ao_lisp_bool_false;
560 }
561
562 #define AO_LISP_BUILTIN_FUNCS
563 #include "ao_lisp_builtin.h"