63fb69fda2ba44071cc1c3ffda34a54ecc2709b7
[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 void
18 ao_lisp_builtin_print(ao_poly b)
19 {
20         (void) b;
21         printf("[builtin]");
22 }
23
24 static int check_argc(struct ao_lisp_cons *cons, int min, int max)
25 {
26         int     argc = 0;
27
28         while (cons && argc <= max) {
29                 argc++;
30                 cons = ao_lisp_poly_cons(cons->cdr);
31         }
32         if (argc < min || argc > max) {
33                 ao_lisp_exception |= AO_LISP_INVALID;
34                 return 0;
35         }
36         return 1;
37 }
38
39 static int check_argt(struct ao_lisp_cons *cons, int argc, int type, int nil_ok)
40 {
41         ao_poly car;
42
43         /* find the desired arg */
44         while (argc--)
45                 cons = ao_lisp_poly_cons(cons->cdr);
46         car = cons->car;
47         if ((!car && !nil_ok) ||
48             ao_lisp_poly_type(car) != type)
49         {
50                 ao_lisp_exception |= AO_LISP_INVALID;
51                 return 0;
52         }
53         return 1;
54 }
55
56 enum math_op { math_plus, math_minus, math_times, math_divide, math_mod };
57
58 ao_poly
59 ao_lisp_car(struct ao_lisp_cons *cons)
60 {
61         if (!check_argc(cons, 1, 1))
62                 return AO_LISP_NIL;
63         if (!check_argt(cons, 0, AO_LISP_CONS, 0)) {
64                 ao_lisp_exception |= AO_LISP_INVALID;
65                 return AO_LISP_NIL;
66         }
67         return ao_lisp_poly_cons(cons->car)->car;
68 }
69
70 ao_poly
71 ao_lisp_cdr(struct ao_lisp_cons *cons)
72 {
73         if (!cons) {
74                 ao_lisp_exception |= AO_LISP_INVALID;
75                 return AO_LISP_NIL;
76         }
77         if (!cons->car) {
78                 ao_lisp_exception |= AO_LISP_INVALID;
79                 return AO_LISP_NIL;
80         }
81         if (ao_lisp_poly_type(cons->car) != AO_LISP_CONS) {
82                 ao_lisp_exception |= AO_LISP_INVALID;
83                 return AO_LISP_NIL;
84         }
85         return ao_lisp_poly_cons(cons->car)->cdr;
86 }
87
88 ao_poly
89 ao_lisp_cons(struct ao_lisp_cons *cons)
90 {
91         ao_poly car, cdr;
92         if (!cons) {
93                 ao_lisp_exception |= AO_LISP_INVALID;
94                 return AO_LISP_NIL;
95         }
96         car = cons->car;
97         cdr = cons->cdr;
98         if (!car || !cdr) {
99                 ao_lisp_exception |= AO_LISP_INVALID;
100                 return AO_LISP_NIL;
101         }
102         cdr = ao_lisp_poly_cons(cdr)->car;
103         if (ao_lisp_poly_type(cdr) != AO_LISP_CONS) {
104                 ao_lisp_exception |= AO_LISP_INVALID;
105                 return AO_LISP_NIL;
106         }
107         return ao_lisp_cons_poly(ao_lisp_cons_cons(car, ao_lisp_poly_cons(cdr)));
108 }
109
110 ao_poly
111 ao_lisp_quote(struct ao_lisp_cons *cons)
112 {
113         if (!cons) {
114                 ao_lisp_exception |= AO_LISP_INVALID;
115                 return AO_LISP_NIL;
116         }
117         return cons->car;
118 }
119
120 ao_poly
121 ao_lisp_set(struct ao_lisp_cons *cons)
122 {
123         ao_poly atom, val;
124         if (!check_argc(cons, 2, 2))
125                 return AO_LISP_NIL;
126         if (!check_argt(cons, 0, AO_LISP_ATOM, 0))
127                 return AO_LISP_NIL;
128
129         atom = cons->car;
130         val = ao_lisp_poly_cons(cons->cdr)->car;
131         if (ao_lisp_is_const(atom)) {
132                 ao_lisp_exception |= AO_LISP_INVALID;
133                 return AO_LISP_NIL;
134         }
135         ao_lisp_poly_atom(atom)->val = val;
136         return val;
137 }
138
139 ao_poly
140 ao_lisp_setq(struct ao_lisp_cons *cons)
141 {
142         struct ao_lisp_cons     *expand = 0;
143         if (!check_argc(cons, 2, 2))
144                 return AO_LISP_NIL;
145         expand = ao_lisp_cons_cons(_ao_lisp_atom_set,
146                                    ao_lisp_cons_cons(ao_lisp_cons_poly(ao_lisp_cons_cons(_ao_lisp_atom_quote,
147                                                                        ao_lisp_cons_cons(cons->car, NULL))),
148                                                      ao_lisp_poly_cons(cons->cdr)));
149         return ao_lisp_cons_poly(expand);
150 }
151
152 ao_poly
153 ao_lisp_print(struct ao_lisp_cons *cons)
154 {
155         ao_poly val = AO_LISP_NIL;
156         while (cons) {
157                 val = cons->car;
158                 ao_lisp_poly_print(val);
159                 cons = ao_lisp_poly_cons(cons->cdr);
160         }
161         return val;
162 }
163
164 ao_poly
165 ao_lisp_math(struct ao_lisp_cons *cons, enum math_op op)
166 {
167         ao_poly ret = AO_LISP_NIL;
168
169         while (cons) {
170                 ao_poly         car = cons->car;
171                 uint8_t         rt = ao_lisp_poly_type(ret);
172                 uint8_t         ct = ao_lisp_poly_type(car);
173
174                 cons = ao_lisp_poly_cons(cons->cdr);
175
176                 if (rt == AO_LISP_NIL)
177                         ret = car;
178
179                 else if (rt == AO_LISP_INT && ct == AO_LISP_INT) {
180                         int     r = ao_lisp_poly_int(ret);
181                         int     c = ao_lisp_poly_int(car);
182
183                         switch(op) {
184                         case math_plus:
185                                 r += c;
186                                 break;
187                         case math_minus:
188                                 r -= c;
189                                 break;
190                         case math_times:
191                                 r *= c;
192                                 break;
193                         case math_divide:
194                                 if (c == 0) {
195                                         ao_lisp_exception |= AO_LISP_DIVIDE_BY_ZERO;
196                                         return AO_LISP_NIL;
197                                 }
198                                 r /= c;
199                                 break;
200                         case math_mod:
201                                 if (c == 0) {
202                                         ao_lisp_exception |= AO_LISP_DIVIDE_BY_ZERO;
203                                         return AO_LISP_NIL;
204                                 }
205                                 r %= c;
206                                 break;
207                         }
208                         ret = ao_lisp_int_poly(r);
209                 }
210
211                 else if (rt == AO_LISP_STRING && ct == AO_LISP_STRING && op == math_plus)
212                         ret = ao_lisp_string_poly(ao_lisp_string_cat(ao_lisp_poly_string(ret),
213                                                                      ao_lisp_poly_string(car)));
214                 else {
215                         ao_lisp_exception |= AO_LISP_INVALID;
216                         return AO_LISP_NIL;
217                 }
218         }
219         return ret;
220 }
221
222 ao_poly
223 ao_lisp_plus(struct ao_lisp_cons *cons)
224 {
225         return ao_lisp_math(cons, math_plus);
226 }
227
228 ao_poly
229 ao_lisp_minus(struct ao_lisp_cons *cons)
230 {
231         return ao_lisp_math(cons, math_minus);
232 }
233
234 ao_poly
235 ao_lisp_times(struct ao_lisp_cons *cons)
236 {
237         return ao_lisp_math(cons, math_times);
238 }
239
240 ao_poly
241 ao_lisp_divide(struct ao_lisp_cons *cons)
242 {
243         return ao_lisp_math(cons, math_divide);
244 }
245
246 ao_poly
247 ao_lisp_mod(struct ao_lisp_cons *cons)
248 {
249         return ao_lisp_math(cons, math_mod);
250 }
251
252 ao_lisp_func_t ao_lisp_builtins[] = {
253         [builtin_car] = ao_lisp_car,
254         [builtin_cdr] = ao_lisp_cdr,
255         [builtin_cons] = ao_lisp_cons,
256         [builtin_quote] = ao_lisp_quote,
257         [builtin_set] = ao_lisp_set,
258         [builtin_setq] = ao_lisp_setq,
259         [builtin_print] = ao_lisp_print,
260         [builtin_plus] = ao_lisp_plus,
261         [builtin_minus] = ao_lisp_minus,
262         [builtin_times] = ao_lisp_times,
263         [builtin_divide] = ao_lisp_divide,
264         [builtin_mod] = ao_lisp_mod
265 };
266