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