altos/lisp: Change lisp objects to use ao_poly everywhere. Add const
[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 enum math_op { math_plus, math_minus, math_times, math_divide, math_mod };
25
26 ao_poly
27 ao_lisp_car(struct ao_lisp_cons *cons)
28 {
29         if (!cons) {
30                 ao_lisp_exception |= AO_LISP_INVALID;
31                 return AO_LISP_NIL;
32         }
33         if (!cons->car) {
34                 ao_lisp_exception |= AO_LISP_INVALID;
35                 return AO_LISP_NIL;
36         }
37         if (ao_lisp_poly_type(cons->car) != AO_LISP_CONS) {
38                 ao_lisp_exception |= AO_LISP_INVALID;
39                 return AO_LISP_NIL;
40         }
41         return ao_lisp_poly_cons(cons->car)->car;
42 }
43
44 ao_poly
45 ao_lisp_cdr(struct ao_lisp_cons *cons)
46 {
47         if (!cons) {
48                 ao_lisp_exception |= AO_LISP_INVALID;
49                 return AO_LISP_NIL;
50         }
51         if (!cons->car) {
52                 ao_lisp_exception |= AO_LISP_INVALID;
53                 return AO_LISP_NIL;
54         }
55         if (ao_lisp_poly_type(cons->car) != AO_LISP_CONS) {
56                 ao_lisp_exception |= AO_LISP_INVALID;
57                 return AO_LISP_NIL;
58         }
59         return ao_lisp_poly_cons(cons->car)->cdr;
60 }
61
62 ao_poly
63 ao_lisp_cons(struct ao_lisp_cons *cons)
64 {
65         ao_poly car, cdr;
66         if (!cons) {
67                 ao_lisp_exception |= AO_LISP_INVALID;
68                 return AO_LISP_NIL;
69         }
70         car = cons->car;
71         cdr = cons->cdr;
72         if (!car || !cdr) {
73                 ao_lisp_exception |= AO_LISP_INVALID;
74                 return AO_LISP_NIL;
75         }
76         cdr = ao_lisp_poly_cons(cdr)->car;
77         if (ao_lisp_poly_type(cdr) != AO_LISP_CONS) {
78                 ao_lisp_exception |= AO_LISP_INVALID;
79                 return AO_LISP_NIL;
80         }
81         return ao_lisp_cons_poly(ao_lisp_cons_cons(car, ao_lisp_poly_cons(cdr)));
82 }
83
84 ao_poly
85 ao_lisp_quote(struct ao_lisp_cons *cons)
86 {
87         if (!cons) {
88                 ao_lisp_exception |= AO_LISP_INVALID;
89                 return AO_LISP_NIL;
90         }
91         return cons->car;
92 }
93
94 ao_poly
95 ao_lisp_print(struct ao_lisp_cons *cons)
96 {
97         ao_poly val = AO_LISP_NIL;
98         while (cons) {
99                 val = cons->car;
100                 ao_lisp_poly_print(val);
101                 cons = ao_lisp_poly_cons(cons->cdr);
102         }
103         return val;
104 }
105
106 ao_poly
107 ao_lisp_math(struct ao_lisp_cons *cons, enum math_op op)
108 {
109         ao_poly ret = AO_LISP_NIL;
110
111         while (cons) {
112                 ao_poly         car = cons->car;
113                 uint8_t         rt = ao_lisp_poly_type(ret);
114                 uint8_t         ct = ao_lisp_poly_type(car);
115
116                 cons = ao_lisp_poly_cons(cons->cdr);
117
118                 if (rt == AO_LISP_NIL)
119                         ret = car;
120
121                 else if (rt == AO_LISP_INT && ct == AO_LISP_INT) {
122                         int     r = ao_lisp_poly_int(ret);
123                         int     c = ao_lisp_poly_int(car);
124
125                         switch(op) {
126                         case math_plus:
127                                 r += c;
128                                 break;
129                         case math_minus:
130                                 r -= c;
131                                 break;
132                         case math_times:
133                                 r *= c;
134                                 break;
135                         case math_divide:
136                                 if (c == 0) {
137                                         ao_lisp_exception |= AO_LISP_DIVIDE_BY_ZERO;
138                                         return AO_LISP_NIL;
139                                 }
140                                 r /= c;
141                                 break;
142                         case math_mod:
143                                 if (c == 0) {
144                                         ao_lisp_exception |= AO_LISP_DIVIDE_BY_ZERO;
145                                         return AO_LISP_NIL;
146                                 }
147                                 r %= c;
148                                 break;
149                         }
150                         ret = ao_lisp_int_poly(r);
151                 }
152
153                 else if (rt == AO_LISP_STRING && ct == AO_LISP_STRING && op == math_plus)
154                         ret = ao_lisp_string_poly(ao_lisp_string_cat(ao_lisp_poly_string(ret),
155                                                                      ao_lisp_poly_string(car)));
156                 else {
157                         ao_lisp_exception |= AO_LISP_INVALID;
158                         return AO_LISP_NIL;
159                 }
160         }
161         return ret;
162 }
163
164 ao_poly
165 ao_lisp_plus(struct ao_lisp_cons *cons)
166 {
167         return ao_lisp_math(cons, math_plus);
168 }
169
170 ao_poly
171 ao_lisp_minus(struct ao_lisp_cons *cons)
172 {
173         return ao_lisp_math(cons, math_minus);
174 }
175
176 ao_poly
177 ao_lisp_times(struct ao_lisp_cons *cons)
178 {
179         return ao_lisp_math(cons, math_times);
180 }
181
182 ao_poly
183 ao_lisp_divide(struct ao_lisp_cons *cons)
184 {
185         return ao_lisp_math(cons, math_divide);
186 }
187
188 ao_poly
189 ao_lisp_mod(struct ao_lisp_cons *cons)
190 {
191         return ao_lisp_math(cons, math_mod);
192 }
193
194 ao_lisp_func_t ao_lisp_builtins[] = {
195         [builtin_car] = ao_lisp_car,
196         [builtin_cdr] = ao_lisp_cdr,
197         [builtin_cons] = ao_lisp_cons,
198         [builtin_quote] = ao_lisp_quote,
199         [builtin_print] = ao_lisp_print,
200         [builtin_plus] = ao_lisp_plus,
201         [builtin_minus] = ao_lisp_minus,
202         [builtin_times] = ao_lisp_times,
203         [builtin_divide] = ao_lisp_divide,
204         [builtin_mod] = ao_lisp_mod
205 };
206