5aa50f6b0661738ccf4fcc9f9b6c849c0f0ff85f
[fw/altos] / src / lisp / ao_lisp_frame.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 #if 0
18 #define DBG(...)        printf(__VA_ARGS__)
19 #else
20 #define DBG(...)
21 #endif
22
23 static inline int
24 frame_num_size(int num)
25 {
26         return sizeof (struct ao_lisp_frame) + num * sizeof (struct ao_lisp_val);
27 }
28
29 static int
30 frame_size(void *addr)
31 {
32         struct ao_lisp_frame    *frame = addr;
33         return frame_num_size(frame->num);
34 }
35
36 #define OFFSET(a)       ((uint8_t *) (ao_lisp_ref(a)) - ao_lisp_const)
37
38 static void
39 frame_mark(void *addr)
40 {
41         struct ao_lisp_frame    *frame = addr;
42         int                     f;
43
44         for (;;) {
45                 if (frame->readonly)
46                         break;
47                 for (f = 0; f < frame->num; f++) {
48                         struct ao_lisp_val      *v = &frame->vals[f];
49
50                         ao_lisp_poly_mark(v->atom);
51                         ao_lisp_poly_mark(v->val);
52                         DBG ("\tframe mark atom %s %d val %d at %d\n", ao_lisp_poly_atom(v->atom)->name, OFFSET(v->atom), OFFSET(v->val), f);
53                 }
54                 frame = ao_lisp_poly_frame(frame->next);
55                 if (!frame)
56                         break;
57                 if (ao_lisp_mark_memory(frame, frame_size(frame)))
58                         break;
59         }
60 }
61
62 static void
63 frame_move(void *addr)
64 {
65         struct ao_lisp_frame    *frame = addr;
66         int                     f;
67
68         for (;;) {
69                 struct ao_lisp_frame    *next;
70                 if (frame->readonly)
71                         break;
72                 for (f = 0; f < frame->num; f++) {
73                         struct ao_lisp_val      *v = &frame->vals[f];
74                         ao_poly                 t;
75
76                         t = ao_lisp_poly_move(v->atom);
77                         DBG("\t\tatom %s %d -> %d\n", ao_lisp_poly_atom(t)->name, OFFSET(v->atom), OFFSET(t));
78                         v->atom = t;
79                         t = ao_lisp_poly_move(v->val);
80                         DBG("\t\tval %d -> %d\n", OFFSET(v->val), OFFSET(t));
81                         v->val = t;
82                 }
83                 next = ao_lisp_poly_frame(frame->next);
84                 if (!next)
85                         break;
86                 next = ao_lisp_move_memory(next, frame_size(next));
87                 frame->next = ao_lisp_frame_poly(next);
88                 frame = next;
89         }
90 }
91
92 const struct ao_lisp_type ao_lisp_frame_type = {
93         .mark = frame_mark,
94         .size = frame_size,
95         .move = frame_move
96 };
97
98 static ao_poly *
99 ao_lisp_frame_ref(struct ao_lisp_frame *frame, ao_poly atom)
100 {
101         int f;
102         for (f = 0; f < frame->num; f++)
103                 if (frame->vals[f].atom == atom)
104                         return &frame->vals[f].val;
105         return NULL;
106 }
107
108 int
109 ao_lisp_frame_set(struct ao_lisp_frame *frame, ao_poly atom, ao_poly val)
110 {
111         while (frame) {
112                 if (!frame->readonly) {
113                         ao_poly *ref = ao_lisp_frame_ref(frame, atom);
114                         if (ref) {
115                                 *ref = val;
116                                 return 1;
117                         }
118                 }
119                 frame = ao_lisp_poly_frame(frame->next);
120         }
121         return 0;
122 }
123
124 ao_poly
125 ao_lisp_frame_get(struct ao_lisp_frame *frame, ao_poly atom)
126 {
127         while (frame) {
128                 ao_poly *ref = ao_lisp_frame_ref(frame, atom);
129                 if (ref)
130                         return *ref;
131                 frame = ao_lisp_poly_frame(frame->next);
132         }
133         return AO_LISP_NIL;
134 }
135
136 struct ao_lisp_frame *
137 ao_lisp_frame_new(int num, int readonly)
138 {
139         struct ao_lisp_frame *frame = ao_lisp_alloc(frame_num_size(num));
140
141         if (!frame)
142                 return NULL;
143         frame->num = num;
144         frame->readonly = readonly;
145         frame->next = AO_LISP_NIL;
146         memset(frame->vals, '\0', num * sizeof (struct ao_lisp_val));
147         return frame;
148 }
149
150 static struct ao_lisp_frame *
151 ao_lisp_frame_realloc(struct ao_lisp_frame *frame, int new_num, int readonly)
152 {
153         struct ao_lisp_frame    *new;
154         int                     copy;
155
156         if (new_num == frame->num)
157                 return frame;
158         new = ao_lisp_frame_new(new_num, readonly);
159         if (!new)
160                 return NULL;
161         copy = new_num;
162         if (copy > frame->num)
163                 copy = frame->num;
164         memcpy(new->vals, frame->vals, copy * sizeof (struct ao_lisp_val));
165         if (frame)
166                 new->next = frame->next;
167         return new;
168 }
169
170 struct ao_lisp_frame *
171 ao_lisp_frame_add(struct ao_lisp_frame *frame, ao_poly atom, ao_poly val)
172 {
173         ao_poly *ref = frame ? ao_lisp_frame_ref(frame, atom) : NULL;
174         if (!ref) {
175                 int f;
176                 if (frame) {
177                         f = frame->num;
178                         frame = ao_lisp_frame_realloc(frame, f + 1, frame->readonly);
179                 } else {
180                         f = 0;
181                         frame = ao_lisp_frame_new(1, 0);
182                 }
183                 if (!frame)
184                         return NULL;
185                 DBG ("add atom %s %d, val %d at %d\n", ao_lisp_poly_atom(atom)->name, OFFSET(atom), OFFSET(val), f);
186                 frame->vals[f].atom = atom;
187                 ref = &frame->vals[f].val;
188         }
189         *ref = val;
190         return frame;
191 }