GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* |
||
2 |
Copyright 2018, 2020 - 2025 Joel Svensson svenssonjoel@yahoo.se |
||
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 3 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, |
||
10 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
11 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
12 |
GNU General Public License for more details. |
||
13 |
|||
14 |
You should have received a copy of the GNU General Public License |
||
15 |
along with this program. If not, see <http://www.gnu.org/licenses/>. |
||
16 |
*/ |
||
17 |
|||
18 |
#include <lbm_memory.h> |
||
19 |
#include <lbm_types.h> |
||
20 |
#include "symrepr.h" |
||
21 |
#include "heap.h" |
||
22 |
#include "env.h" |
||
23 |
#include "eval_cps.h" |
||
24 |
#include "stack.h" |
||
25 |
#include "fundamental.h" |
||
26 |
#include "extensions.h" |
||
27 |
#include "tokpar.h" |
||
28 |
#include "lbm_channel.h" |
||
29 |
#include "print.h" |
||
30 |
#include "platform_mutex.h" |
||
31 |
#include "lbm_flat_value.h" |
||
32 |
#include "lbm_flags.h" |
||
33 |
|||
34 |
#ifdef VISUALIZE_HEAP |
||
35 |
#include "heap_vis.h" |
||
36 |
#endif |
||
37 |
|||
38 |
#include <setjmp.h> |
||
39 |
#include <stdarg.h> |
||
40 |
|||
41 |
static jmp_buf error_jmp_buf; |
||
42 |
static jmp_buf critical_error_jmp_buf; |
||
43 |
|||
44 |
#define S_TO_US(X) (lbm_uint)((X) * 1000000) |
||
45 |
|||
46 |
#define DEC_CONTINUATION(x) (((x) & ~LBM_CONTINUATION_INTERNAL) >> LBM_ADDRESS_SHIFT) |
||
47 |
#define IS_CONTINUATION(x) (((x) & LBM_CONTINUATION_INTERNAL) == LBM_CONTINUATION_INTERNAL) |
||
48 |
#define CONTINUATION(x) (((x) << LBM_ADDRESS_SHIFT) | LBM_CONTINUATION_INTERNAL) |
||
49 |
|||
50 |
#define DONE CONTINUATION(0) |
||
51 |
#define SET_GLOBAL_ENV CONTINUATION(1) |
||
52 |
#define BIND_TO_KEY_REST CONTINUATION(2) |
||
53 |
#define IF CONTINUATION(3) |
||
54 |
#define PROGN_REST CONTINUATION(4) |
||
55 |
#define APPLICATION_ARGS CONTINUATION(5) |
||
56 |
#define AND CONTINUATION(6) |
||
57 |
#define OR CONTINUATION(7) |
||
58 |
#define WAIT CONTINUATION(8) |
||
59 |
#define MATCH CONTINUATION(9) |
||
60 |
#define APPLICATION_START CONTINUATION(10) |
||
61 |
#define EVAL_R CONTINUATION(11) |
||
62 |
#define RESUME CONTINUATION(12) |
||
63 |
#define CLOSURE_ARGS CONTINUATION(13) |
||
64 |
#define EXIT_ATOMIC CONTINUATION(14) |
||
65 |
#define READ_NEXT_TOKEN CONTINUATION(15) |
||
66 |
#define READ_APPEND_CONTINUE CONTINUATION(16) |
||
67 |
#define READ_EVAL_CONTINUE CONTINUATION(17) |
||
68 |
#define READ_EXPECT_CLOSEPAR CONTINUATION(18) |
||
69 |
#define READ_DOT_TERMINATE CONTINUATION(19) |
||
70 |
#define READ_DONE CONTINUATION(20) |
||
71 |
#define READ_START_ARRAY CONTINUATION(21) |
||
72 |
#define READ_APPEND_ARRAY CONTINUATION(22) |
||
73 |
#define MAP CONTINUATION(23) |
||
74 |
#define MATCH_GUARD CONTINUATION(24) |
||
75 |
#define TERMINATE CONTINUATION(25) |
||
76 |
#define PROGN_VAR CONTINUATION(26) |
||
77 |
#define SETQ CONTINUATION(27) |
||
78 |
#define MOVE_TO_FLASH CONTINUATION(28) |
||
79 |
#define MOVE_VAL_TO_FLASH_DISPATCH CONTINUATION(29) |
||
80 |
#define MOVE_LIST_TO_FLASH CONTINUATION(30) |
||
81 |
#define CLOSE_LIST_IN_FLASH CONTINUATION(31) |
||
82 |
#define QQ_EXPAND_START CONTINUATION(32) |
||
83 |
#define QQ_EXPAND CONTINUATION(33) |
||
84 |
#define QQ_APPEND CONTINUATION(34) |
||
85 |
#define QQ_EXPAND_LIST CONTINUATION(35) |
||
86 |
#define QQ_LIST CONTINUATION(36) |
||
87 |
#define KILL CONTINUATION(37) |
||
88 |
#define LOOP CONTINUATION(38) |
||
89 |
#define LOOP_CONDITION CONTINUATION(39) |
||
90 |
#define MERGE_REST CONTINUATION(40) |
||
91 |
#define MERGE_LAYER CONTINUATION(41) |
||
92 |
#define CLOSURE_ARGS_REST CONTINUATION(42) |
||
93 |
#define MOVE_ARRAY_ELTS_TO_FLASH CONTINUATION(43) |
||
94 |
#define POP_READER_FLAGS CONTINUATION(44) |
||
95 |
#define EXCEPTION_HANDLER CONTINUATION(45) |
||
96 |
#define RECV_TO CONTINUATION(46) |
||
97 |
#define WRAP_RESULT CONTINUATION(47) |
||
98 |
#define RECV_TO_RETRY CONTINUATION(48) |
||
99 |
#define NUM_CONTINUATIONS 49 |
||
100 |
|||
101 |
#define FM_NEED_GC -1 |
||
102 |
#define FM_NO_MATCH -2 |
||
103 |
#define FM_PATTERN_ERROR -3 |
||
104 |
|||
105 |
typedef enum { |
||
106 |
BL_OK = 0, |
||
107 |
BL_NO_MEMORY, |
||
108 |
BL_INCORRECT_KEY |
||
109 |
} binding_location_status; |
||
110 |
|||
111 |
#define FB_OK 0 |
||
112 |
#define FB_TYPE_ERROR -1 |
||
113 |
|||
114 |
// Infer canarie |
||
115 |
// |
||
116 |
// In some cases Infer incorrectly complains about null pointer |
||
117 |
// derefences that cannot happen. In these cases the longjmp |
||
118 |
// error system aborts execution before the potential null |
||
119 |
// pointer dereference can occur. |
||
120 |
// |
||
121 |
// Functions such as stack_reserve does not return NULL, |
||
122 |
// instead it executes a longjmp and does not return at all. |
||
123 |
// Infer does not seem to understand this abrubt code flow. |
||
124 |
#ifdef LBM64 |
||
125 |
#define INFER_CANARY_BITS (lbm_uint)0xAAAAAAAAAAAAAAAA |
||
126 |
#else |
||
127 |
#define INFER_CANARY_BITS 0xAAAAAAAAu |
||
128 |
#endif |
||
129 |
lbm_uint INFER_CANARY[1]; |
||
130 |
|||
131 |
8156 |
bool check_infer_canary(void) { |
|
132 |
8156 |
return INFER_CANARY[0] == INFER_CANARY_BITS; |
|
133 |
} |
||
134 |
|||
135 |
21588 |
void reset_infer_canary(void) { |
|
136 |
21588 |
INFER_CANARY[0] = INFER_CANARY_BITS; |
|
137 |
21588 |
} |
|
138 |
|||
139 |
const char* lbm_error_str_parse_eof = "End of parse stream."; |
||
140 |
const char* lbm_error_str_parse_dot = "Incorrect usage of '.'."; |
||
141 |
const char* lbm_error_str_parse_close = "Expected closing parenthesis."; |
||
142 |
const char* lbm_error_str_num_args = "Incorrect number of arguments."; |
||
143 |
const char* lbm_error_str_forbidden_in_atomic = "Operation is forbidden in an atomic block."; |
||
144 |
const char* lbm_error_str_no_number = "Argument(s) must be a number."; |
||
145 |
const char* lbm_error_str_not_a_boolean = "Argument must be t or nil (true or false)."; |
||
146 |
const char* lbm_error_str_incorrect_arg = "Incorrect argument."; |
||
147 |
const char* lbm_error_str_var_outside_progn = "Usage of var outside of progn."; |
||
148 |
const char* lbm_error_str_flash_not_possible = "Value cannot be written to flash."; |
||
149 |
const char* lbm_error_str_flash_error = "Error writing to flash."; |
||
150 |
const char* lbm_error_str_flash_full = "Flash memory is full."; |
||
151 |
const char* lbm_error_str_variable_not_bound = "Variable not bound."; |
||
152 |
const char* lbm_error_str_read_no_mem = "Out of memory while reading."; |
||
153 |
const char* lbm_error_str_qq_expand = "Quasiquotation expansion error."; |
||
154 |
|||
155 |
static lbm_value lbm_error_suspect; |
||
156 |
static bool lbm_error_has_suspect = false; |
||
157 |
#ifdef LBM_ALWAYS_GC |
||
158 |
|||
159 |
#define WITH_GC(y, x) \ |
||
160 |
gc(); \ |
||
161 |
(y) = (x); \ |
||
162 |
if (lbm_is_symbol_merror((y))) { \ |
||
163 |
error_ctx(ENC_SYM_MERROR); \ |
||
164 |
} |
||
165 |
|||
166 |
#define WITH_GC_RMBR_1(y, x, r) \ |
||
167 |
lbm_gc_mark_phase(r); \ |
||
168 |
gc(); \ |
||
169 |
(y) = (x); \ |
||
170 |
if (lbm_is_symbol_merror((y))) { \ |
||
171 |
error_ctx(ENC_SYM_MERROR); \ |
||
172 |
} |
||
173 |
|||
174 |
#else |
||
175 |
|||
176 |
#define WITH_GC(y, x) \ |
||
177 |
(y) = (x); \ |
||
178 |
if (lbm_is_symbol_merror((y))) { \ |
||
179 |
gc(); \ |
||
180 |
(y) = (x); \ |
||
181 |
if (lbm_is_symbol_merror((y))) { \ |
||
182 |
error_ctx(ENC_SYM_MERROR); \ |
||
183 |
} \ |
||
184 |
/* continue executing statements below */ \ |
||
185 |
} |
||
186 |
#define WITH_GC_RMBR_1(y, x, r) \ |
||
187 |
(y) = (x); \ |
||
188 |
if (lbm_is_symbol_merror((y))) { \ |
||
189 |
lbm_gc_mark_phase(r); \ |
||
190 |
gc(); \ |
||
191 |
(y) = (x); \ |
||
192 |
if (lbm_is_symbol_merror((y))) { \ |
||
193 |
error_ctx(ENC_SYM_MERROR); \ |
||
194 |
} \ |
||
195 |
/* continue executing statements below */ \ |
||
196 |
} |
||
197 |
|||
198 |
#endif |
||
199 |
|||
200 |
/**************************************************************/ |
||
201 |
/* */ |
||
202 |
typedef struct { |
||
203 |
eval_context_t *first; |
||
204 |
eval_context_t *last; |
||
205 |
} eval_context_queue_t; |
||
206 |
|||
207 |
#ifdef CLEAN_UP_CLOSURES |
||
208 |
static lbm_value clean_cl_env_symbol = ENC_SYM_NIL; |
||
209 |
#endif |
||
210 |
|||
211 |
static int gc(void); |
||
212 |
static void error_ctx(lbm_value); |
||
213 |
static void error_at_ctx(lbm_value err_val, lbm_value at); |
||
214 |
static void enqueue_ctx(eval_context_queue_t *q, eval_context_t *ctx); |
||
215 |
static bool mailbox_add_mail(eval_context_t *ctx, lbm_value mail); |
||
216 |
|||
217 |
// The currently executing context. |
||
218 |
eval_context_t *ctx_running = NULL; |
||
219 |
volatile bool lbm_system_sleeping = false; |
||
220 |
|||
221 |
static volatile bool gc_requested = false; |
||
222 |
4368 |
void lbm_request_gc(void) { |
|
223 |
4368 |
gc_requested = true; |
|
224 |
4368 |
} |
|
225 |
|||
226 |
/* |
||
227 |
On ChibiOs the CH_CFG_ST_FREQUENCY setting in chconf.h sets the |
||
228 |
resolution of the timer used for sleep operations. If this is set |
||
229 |
to 10KHz the resolution is 100us. |
||
230 |
|||
231 |
The CH_CFG_ST_TIMEDELTA specifies the minimum number of ticks that |
||
232 |
can be safely specified in a timeout directive (wonder if that |
||
233 |
means sleep-period). The timedelta is set to 2. |
||
234 |
|||
235 |
If I have understood these correctly it means that the minimum |
||
236 |
sleep duration possible is 2 * 100us = 200us. |
||
237 |
*/ |
||
238 |
|||
239 |
#define EVAL_CPS_DEFAULT_STACK_SIZE 256 |
||
240 |
#define EVAL_CPS_MIN_SLEEP 200 |
||
241 |
#define EVAL_STEPS_QUOTA 10 |
||
242 |
|||
243 |
static volatile uint32_t eval_steps_refill = EVAL_STEPS_QUOTA; |
||
244 |
static uint32_t eval_steps_quota = EVAL_STEPS_QUOTA; |
||
245 |
|||
246 |
28 |
void lbm_set_eval_step_quota(uint32_t quota) { |
|
247 |
28 |
eval_steps_refill = quota; |
|
248 |
28 |
} |
|
249 |
|||
250 |
static uint32_t eval_cps_run_state = EVAL_CPS_STATE_DEAD; |
||
251 |
static volatile uint32_t eval_cps_next_state = EVAL_CPS_STATE_NONE; |
||
252 |
static volatile uint32_t eval_cps_next_state_arg = 0; |
||
253 |
static volatile bool eval_cps_state_changed = false; |
||
254 |
|||
255 |
static void usleep_nonsense(uint32_t us) { |
||
256 |
(void) us; |
||
257 |
} |
||
258 |
|||
259 |
static bool dynamic_load_nonsense(const char *sym, const char **code) { |
||
260 |
(void) sym; |
||
261 |
(void) code; |
||
262 |
return false; |
||
263 |
} |
||
264 |
|||
265 |
static uint32_t timestamp_nonsense(void) { |
||
266 |
return 0; |
||
267 |
} |
||
268 |
|||
269 |
static int printf_nonsense(const char *fmt, ...) { |
||
270 |
(void) fmt; |
||
271 |
return 0; |
||
272 |
} |
||
273 |
|||
274 |
static void ctx_done_nonsense(eval_context_t *ctx) { |
||
275 |
(void) ctx; |
||
276 |
} |
||
277 |
|||
278 |
static void critical_nonsense(void) { |
||
279 |
return; |
||
280 |
} |
||
281 |
|||
282 |
static void user_callback_nonsense(void *arg) { |
||
283 |
(void) arg; |
||
284 |
return; |
||
285 |
} |
||
286 |
|||
287 |
static void (*critical_error_callback)(void) = critical_nonsense; |
||
288 |
static void (*usleep_callback)(uint32_t) = usleep_nonsense; |
||
289 |
static uint32_t (*timestamp_us_callback)(void) = timestamp_nonsense; |
||
290 |
static void (*ctx_done_callback)(eval_context_t *) = ctx_done_nonsense; |
||
291 |
static int (*printf_callback)(const char *, ...) = printf_nonsense; |
||
292 |
static bool (*dynamic_load_callback)(const char *, const char **) = dynamic_load_nonsense; |
||
293 |
static void (*user_callback)(void *) = user_callback_nonsense; |
||
294 |
|||
295 |
void lbm_set_user_callback(void (*fptr)(void *)) { |
||
296 |
if (fptr == NULL) user_callback = user_callback_nonsense; |
||
297 |
else user_callback = fptr; |
||
298 |
} |
||
299 |
|||
300 |
21588 |
void lbm_set_critical_error_callback(void (*fptr)(void)) { |
|
301 |
✗✓ | 21588 |
if (fptr == NULL) critical_error_callback = critical_nonsense; |
302 |
21588 |
else critical_error_callback = fptr; |
|
303 |
21588 |
} |
|
304 |
|||
305 |
21588 |
void lbm_set_usleep_callback(void (*fptr)(uint32_t)) { |
|
306 |
✗✓ | 21588 |
if (fptr == NULL) usleep_callback = usleep_nonsense; |
307 |
21588 |
else usleep_callback = fptr; |
|
308 |
21588 |
} |
|
309 |
|||
310 |
21588 |
void lbm_set_timestamp_us_callback(uint32_t (*fptr)(void)) { |
|
311 |
✗✓ | 21588 |
if (fptr == NULL) timestamp_us_callback = timestamp_nonsense; |
312 |
21588 |
else timestamp_us_callback = fptr; |
|
313 |
21588 |
} |
|
314 |
|||
315 |
21588 |
void lbm_set_ctx_done_callback(void (*fptr)(eval_context_t *)) { |
|
316 |
✗✓ | 21588 |
if (fptr == NULL) ctx_done_callback = ctx_done_nonsense; |
317 |
21588 |
else ctx_done_callback = fptr; |
|
318 |
21588 |
} |
|
319 |
|||
320 |
21588 |
void lbm_set_printf_callback(int (*fptr)(const char*, ...)){ |
|
321 |
✗✓ | 21588 |
if (fptr == NULL) printf_callback = printf_nonsense; |
322 |
21588 |
else printf_callback = fptr; |
|
323 |
21588 |
} |
|
324 |
|||
325 |
21588 |
void lbm_set_dynamic_load_callback(bool (*fptr)(const char *, const char **)) { |
|
326 |
✗✓ | 21588 |
if (fptr == NULL) dynamic_load_callback = dynamic_load_nonsense; |
327 |
21588 |
else dynamic_load_callback = fptr; |
|
328 |
21588 |
} |
|
329 |
|||
330 |
static volatile lbm_event_t *lbm_events = NULL; |
||
331 |
static unsigned int lbm_events_head = 0; |
||
332 |
static unsigned int lbm_events_tail = 0; |
||
333 |
static unsigned int lbm_events_max = 0; |
||
334 |
static bool lbm_events_full = false; |
||
335 |
static mutex_t lbm_events_mutex; |
||
336 |
static bool lbm_events_mutex_initialized = false; |
||
337 |
static volatile lbm_cid lbm_event_handler_pid = -1; |
||
338 |
|||
339 |
lbm_cid lbm_get_event_handler_pid(void) { |
||
340 |
return lbm_event_handler_pid; |
||
341 |
} |
||
342 |
|||
343 |
224 |
void lbm_set_event_handler_pid(lbm_cid pid) { |
|
344 |
224 |
lbm_event_handler_pid = pid; |
|
345 |
224 |
} |
|
346 |
|||
347 |
bool lbm_event_handler_exists(void) { |
||
348 |
return(lbm_event_handler_pid > 0); |
||
349 |
} |
||
350 |
|||
351 |
|||
352 |
7024 |
static bool event_internal(lbm_event_type_t event_type, lbm_uint parameter, lbm_uint buf_ptr, lbm_uint buf_len) { |
|
353 |
7024 |
bool r = false; |
|
354 |
✓✗ | 7024 |
if (lbm_events) { |
355 |
7024 |
mutex_lock(&lbm_events_mutex); |
|
356 |
✓✗ | 7024 |
if (!lbm_events_full) { |
357 |
lbm_event_t event; |
||
358 |
7024 |
event.type = event_type; |
|
359 |
7024 |
event.parameter = parameter; |
|
360 |
7024 |
event.buf_ptr = buf_ptr; |
|
361 |
7024 |
event.buf_len = buf_len; |
|
362 |
7024 |
lbm_events[lbm_events_head] = event; |
|
363 |
7024 |
lbm_events_head = (lbm_events_head + 1) % lbm_events_max; |
|
364 |
7024 |
lbm_events_full = lbm_events_head == lbm_events_tail; |
|
365 |
7024 |
r = true; |
|
366 |
} |
||
367 |
7024 |
mutex_unlock(&lbm_events_mutex); |
|
368 |
} |
||
369 |
7024 |
return r; |
|
370 |
} |
||
371 |
|||
372 |
bool lbm_event_define(lbm_value key, lbm_flat_value_t *fv) { |
||
373 |
return event_internal(LBM_EVENT_DEFINE, key, (lbm_uint)fv->buf, fv->buf_size); |
||
374 |
} |
||
375 |
|||
376 |
bool lbm_event_run_user_callback(void *arg) { |
||
377 |
return event_internal(LBM_EVENT_RUN_USER_CALLBACK, (lbm_uint)arg, 0, 0); |
||
378 |
} |
||
379 |
|||
380 |
bool lbm_event_unboxed(lbm_value unboxed) { |
||
381 |
lbm_uint t = lbm_type_of(unboxed); |
||
382 |
if (t == LBM_TYPE_SYMBOL || |
||
383 |
t == LBM_TYPE_I || |
||
384 |
t == LBM_TYPE_U || |
||
385 |
t == LBM_TYPE_CHAR) { |
||
386 |
if (lbm_event_handler_pid > 0) { |
||
387 |
return event_internal(LBM_EVENT_FOR_HANDLER, 0, (lbm_uint)unboxed, 0); |
||
388 |
} |
||
389 |
} |
||
390 |
return false; |
||
391 |
} |
||
392 |
|||
393 |
6940 |
bool lbm_event(lbm_flat_value_t *fv) { |
|
394 |
✓✗ | 6940 |
if (lbm_event_handler_pid > 0) { |
395 |
6940 |
return event_internal(LBM_EVENT_FOR_HANDLER, 0, (lbm_uint)fv->buf, fv->buf_size); |
|
396 |
} |
||
397 |
return false; |
||
398 |
} |
||
399 |
|||
400 |
93335603 |
static bool lbm_event_pop(lbm_event_t *event) { |
|
401 |
93335603 |
mutex_lock(&lbm_events_mutex); |
|
402 |
✓✓✓✗ |
93335603 |
if (lbm_events_head == lbm_events_tail && !lbm_events_full) { |
403 |
93328586 |
mutex_unlock(&lbm_events_mutex); |
|
404 |
93328586 |
return false; |
|
405 |
} |
||
406 |
7017 |
*event = lbm_events[lbm_events_tail]; |
|
407 |
7017 |
lbm_events_tail = (lbm_events_tail + 1) % lbm_events_max; |
|
408 |
7017 |
lbm_events_full = false; |
|
409 |
7017 |
mutex_unlock(&lbm_events_mutex); |
|
410 |
7017 |
return true; |
|
411 |
} |
||
412 |
|||
413 |
bool lbm_event_queue_is_empty(void) { |
||
414 |
mutex_lock(&lbm_events_mutex); |
||
415 |
bool empty = false; |
||
416 |
if (lbm_events_head == lbm_events_tail && !lbm_events_full) { |
||
417 |
empty = true; |
||
418 |
} |
||
419 |
mutex_unlock(&lbm_events_mutex); |
||
420 |
return empty; |
||
421 |
} |
||
422 |
|||
423 |
static bool eval_running = false; |
||
424 |
static volatile bool blocking_extension = false; |
||
425 |
static mutex_t blocking_extension_mutex; |
||
426 |
static bool blocking_extension_mutex_initialized = false; |
||
427 |
static lbm_uint blocking_extension_timeout_us = 0; |
||
428 |
static bool blocking_extension_timeout = false; |
||
429 |
|||
430 |
static bool is_atomic = false; |
||
431 |
|||
432 |
/* Process queues */ |
||
433 |
static eval_context_queue_t blocked = {NULL, NULL}; |
||
434 |
static eval_context_queue_t queue = {NULL, NULL}; |
||
435 |
|||
436 |
/* one mutex for all queue operations */ |
||
437 |
mutex_t qmutex; |
||
438 |
bool qmutex_initialized = false; |
||
439 |
|||
440 |
|||
441 |
// MODES |
||
442 |
static volatile bool lbm_verbose = false; |
||
443 |
static volatile bool lbm_hide_trapped_error = false; |
||
444 |
|||
445 |
void lbm_toggle_verbose(void) { |
||
446 |
lbm_verbose = !lbm_verbose; |
||
447 |
} |
||
448 |
|||
449 |
21588 |
void lbm_set_verbose(bool verbose) { |
|
450 |
21588 |
lbm_verbose = verbose; |
|
451 |
21588 |
} |
|
452 |
|||
453 |
void lbm_set_hide_trapped_error(bool hide) { |
||
454 |
lbm_hide_trapped_error = hide; |
||
455 |
} |
||
456 |
|||
457 |
1120 |
lbm_cid lbm_get_current_cid(void) { |
|
458 |
✓✗ | 1120 |
if (ctx_running) |
459 |
1120 |
return ctx_running->id; |
|
460 |
else |
||
461 |
return -1; |
||
462 |
} |
||
463 |
|||
464 |
eval_context_t *lbm_get_current_context(void) { |
||
465 |
return ctx_running; |
||
466 |
} |
||
467 |
|||
468 |
void lbm_surrender_quota(void) { |
||
469 |
eval_steps_quota = 0; |
||
470 |
} |
||
471 |
|||
472 |
|||
473 |
/****************************************************/ |
||
474 |
/* Utilities used locally in this file */ |
||
475 |
|||
476 |
380968 |
static inline lbm_array_header_t *assume_array(lbm_value a){ |
|
477 |
380968 |
return (lbm_array_header_t*)lbm_ref_cell(a)->car; |
|
478 |
} |
||
479 |
|||
480 |
4388468 |
static lbm_value cons_with_gc(lbm_value head, lbm_value tail, lbm_value remember) { |
|
481 |
#ifdef LBM_ALWAYS_GC |
||
482 |
lbm_value always_gc_roots[3] = {head, tail, remember}; |
||
483 |
lbm_gc_mark_roots(always_gc_roots,3); |
||
484 |
gc(); |
||
485 |
#endif |
||
486 |
4388468 |
lbm_value res = lbm_heap_state.freelist; |
|
487 |
✓✓ | 4388468 |
if (lbm_is_symbol_nil(res)) { |
488 |
1136 |
lbm_value roots[3] = {head, tail, remember}; |
|
489 |
1136 |
lbm_gc_mark_roots(roots,3); |
|
490 |
1136 |
gc(); |
|
491 |
1136 |
res = lbm_heap_state.freelist; |
|
492 |
✗✓ | 1136 |
if (lbm_is_symbol_nil(res)) { |
493 |
error_ctx(ENC_SYM_MERROR); |
||
494 |
} |
||
495 |
} |
||
496 |
4388468 |
lbm_uint heap_ix = lbm_dec_ptr(res); |
|
497 |
4388468 |
lbm_heap_state.freelist = lbm_heap_state.heap[heap_ix].cdr; |
|
498 |
4388468 |
lbm_heap_state.num_alloc++; |
|
499 |
4388468 |
lbm_heap_state.heap[heap_ix].car = head; |
|
500 |
4388468 |
lbm_heap_state.heap[heap_ix].cdr = tail; |
|
501 |
4388468 |
res = lbm_set_ptr_type(res, LBM_TYPE_CONS); |
|
502 |
4388468 |
return res; |
|
503 |
} |
||
504 |
|||
505 |
469496525 |
static lbm_uint *get_stack_ptr(eval_context_t *ctx, unsigned int n) { |
|
506 |
✓✗ | 469496525 |
if (n <= ctx->K.sp) { |
507 |
469496525 |
lbm_uint index = ctx->K.sp - n; |
|
508 |
469496525 |
return &ctx->K.data[index]; |
|
509 |
} |
||
510 |
error_ctx(ENC_SYM_STACK_ERROR); |
||
511 |
return (lbm_uint*)INFER_CANARY; // dead code cannot be reached, but C compiler doesn't realise. |
||
512 |
} |
||
513 |
|||
514 |
// pop_stack_ptr is safe when no GC is performed and |
||
515 |
// the values of the stack will be dropped. |
||
516 |
21771007 |
static lbm_uint *pop_stack_ptr(eval_context_t *ctx, unsigned int n) { |
|
517 |
✓✗ | 21771007 |
if (n <= ctx->K.sp) { |
518 |
21771007 |
ctx->K.sp -= n; |
|
519 |
21771007 |
return &ctx->K.data[ctx->K.sp]; |
|
520 |
} |
||
521 |
error_ctx(ENC_SYM_STACK_ERROR); |
||
522 |
return (lbm_uint*)INFER_CANARY; // dead code cannot be reached, but C compiler doesn't realise. |
||
523 |
} |
||
524 |
|||
525 |
493959640 |
static inline lbm_uint *stack_reserve(eval_context_t *ctx, unsigned int n) { |
|
526 |
✓✗ | 493959640 |
if (ctx->K.sp + n < ctx->K.size) { |
527 |
493959640 |
lbm_uint *ptr = &ctx->K.data[ctx->K.sp]; |
|
528 |
493959640 |
ctx->K.sp += n; |
|
529 |
493959640 |
return ptr; |
|
530 |
} |
||
531 |
error_ctx(ENC_SYM_STACK_ERROR); |
||
532 |
return (lbm_uint*)INFER_CANARY; // dead code cannot be reached, but C compiler doesn't realise. |
||
533 |
} |
||
534 |
|||
535 |
7196 |
static void handle_flash_status(lbm_flash_status s) { |
|
536 |
✗✓ | 7196 |
if ( s == LBM_FLASH_FULL) { |
537 |
lbm_set_error_reason((char*)lbm_error_str_flash_full); |
||
538 |
error_ctx(ENC_SYM_EERROR); |
||
539 |
} |
||
540 |
✗✓ | 7196 |
if (s == LBM_FLASH_WRITE_ERROR) { |
541 |
lbm_set_error_reason((char*)lbm_error_str_flash_error); |
||
542 |
error_ctx(ENC_SYM_FATAL_ERROR); |
||
543 |
} |
||
544 |
7196 |
} |
|
545 |
|||
546 |
84 |
static void lift_array_flash(lbm_value flash_cell, bool bytearray, char *data, lbm_uint num_elt) { |
|
547 |
|||
548 |
lbm_array_header_t flash_array_header; |
||
549 |
84 |
flash_array_header.size = num_elt; |
|
550 |
84 |
flash_array_header.data = (lbm_uint*)data; |
|
551 |
84 |
lbm_uint flash_array_header_ptr = 0; |
|
552 |
84 |
handle_flash_status(lbm_write_const_raw((lbm_uint*)&flash_array_header, |
|
553 |
sizeof(lbm_array_header_t) / sizeof(lbm_uint), |
||
554 |
&flash_array_header_ptr)); |
||
555 |
84 |
handle_flash_status(write_const_car(flash_cell, flash_array_header_ptr)); |
|
556 |
✓✓ | 84 |
lbm_uint t = bytearray ? ENC_SYM_ARRAY_TYPE : ENC_SYM_LISPARRAY_TYPE; |
557 |
84 |
handle_flash_status(write_const_cdr(flash_cell, t)); |
|
558 |
84 |
} |
|
559 |
|||
560 |
119716056 |
static void get_car_and_cdr(lbm_value a, lbm_value *a_car, lbm_value *a_cdr) { |
|
561 |
✓✓ | 119716056 |
if (lbm_is_ptr(a)) { |
562 |
119398396 |
lbm_cons_t *cell = lbm_ref_cell(a); |
|
563 |
119398396 |
*a_car = cell->car; |
|
564 |
119398396 |
*a_cdr = cell->cdr; |
|
565 |
✓✗ | 317660 |
} else if (lbm_is_symbol_nil(a)) { |
566 |
317660 |
*a_car = *a_cdr = ENC_SYM_NIL; |
|
567 |
} else { |
||
568 |
*a_car = *a_cdr = ENC_SYM_NIL; |
||
569 |
error_ctx(ENC_SYM_TERROR); |
||
570 |
} |
||
571 |
119716056 |
} |
|
572 |
|||
573 |
/* car cdr caar cadr replacements that are evaluator safe. */ |
||
574 |
114759837 |
static lbm_value get_car(lbm_value a) { |
|
575 |
✓✗ | 114759837 |
if (lbm_is_ptr(a)) { |
576 |
114759837 |
lbm_cons_t *cell = lbm_ref_cell(a); |
|
577 |
114759837 |
return cell->car; |
|
578 |
} else if (lbm_is_symbol_nil(a)) { |
||
579 |
return a; |
||
580 |
} |
||
581 |
error_ctx(ENC_SYM_TERROR); |
||
582 |
return(ENC_SYM_TERROR); |
||
583 |
} |
||
584 |
|||
585 |
139610947 |
static lbm_value get_cdr(lbm_value a) { |
|
586 |
✓✓ | 139610947 |
if (lbm_is_ptr(a)) { |
587 |
139610919 |
lbm_cons_t *cell = lbm_ref_cell(a); |
|
588 |
139610919 |
return cell->cdr; |
|
589 |
✓✗ | 28 |
} else if (lbm_is_symbol_nil(a)) { |
590 |
28 |
return a; |
|
591 |
} |
||
592 |
error_ctx(ENC_SYM_TERROR); |
||
593 |
return(ENC_SYM_TERROR); |
||
594 |
} |
||
595 |
|||
596 |
27381821 |
static lbm_value get_cadr(lbm_value a) { |
|
597 |
✓✗ | 27381821 |
if (lbm_is_ptr(a)) { |
598 |
27381821 |
lbm_cons_t *cell = lbm_ref_cell(a); |
|
599 |
27381821 |
lbm_value tmp = cell->cdr; |
|
600 |
✓✓ | 27381821 |
if (lbm_is_ptr(tmp)) { |
601 |
27371937 |
return lbm_ref_cell(tmp)->car; |
|
602 |
✓✗ | 9884 |
} else if (lbm_is_symbol_nil(tmp)) { |
603 |
9884 |
return tmp; |
|
604 |
} |
||
605 |
} else if (lbm_is_symbol_nil(a)) { |
||
606 |
return a; |
||
607 |
} |
||
608 |
error_ctx(ENC_SYM_TERROR); |
||
609 |
return(ENC_SYM_TERROR); |
||
610 |
} |
||
611 |
|||
612 |
// Allocate a binding and attach it to a list (if so desired) |
||
613 |
60007154 |
static lbm_value allocate_binding(lbm_value key, lbm_value val, lbm_value the_cdr) { |
|
614 |
#ifdef LBM_ALWAYS_GC |
||
615 |
lbm_gc_mark_phase(key); |
||
616 |
lbm_gc_mark_phase(val); |
||
617 |
lbm_gc_mark_phase(the_cdr); |
||
618 |
gc(); |
||
619 |
if (lbm_heap_num_free() < 2) { |
||
620 |
error_ctx(ENC_SYM_MERROR); |
||
621 |
} |
||
622 |
#else |
||
623 |
✓✓ | 60007154 |
if (lbm_heap_num_free() < 2) { |
624 |
83306 |
lbm_gc_mark_phase(key); |
|
625 |
83306 |
lbm_gc_mark_phase(val); |
|
626 |
83306 |
lbm_gc_mark_phase(the_cdr); |
|
627 |
83306 |
gc(); |
|
628 |
✓✓ | 83306 |
if (lbm_heap_num_free() < 2) { |
629 |
28 |
error_ctx(ENC_SYM_MERROR); |
|
630 |
} |
||
631 |
} |
||
632 |
#endif |
||
633 |
// If num_free is calculated correctly, freelist is definitely a cons-cell. |
||
634 |
60007126 |
lbm_cons_t* heap = lbm_heap_state.heap; |
|
635 |
60007126 |
lbm_value binding_cell = lbm_heap_state.freelist; |
|
636 |
60007126 |
lbm_uint binding_cell_ix = lbm_dec_ptr(binding_cell); |
|
637 |
60007126 |
lbm_value list_cell = heap[binding_cell_ix].cdr; |
|
638 |
60007126 |
lbm_uint list_cell_ix = lbm_dec_ptr(list_cell); |
|
639 |
60007126 |
lbm_heap_state.freelist = heap[list_cell_ix].cdr; |
|
640 |
60007126 |
lbm_heap_state.num_alloc += 2; |
|
641 |
60007126 |
heap[binding_cell_ix].car = key; |
|
642 |
60007126 |
heap[binding_cell_ix].cdr = val; |
|
643 |
60007126 |
heap[list_cell_ix].car = binding_cell; |
|
644 |
60007126 |
heap[list_cell_ix].cdr = the_cdr; |
|
645 |
60007126 |
return list_cell; |
|
646 |
} |
||
647 |
|||
648 |
#define CLO_PARAMS 0 |
||
649 |
#define CLO_BODY 1 |
||
650 |
#define CLO_ENV 2 |
||
651 |
#define LOOP_BINDS 0 |
||
652 |
#define LOOP_COND 1 |
||
653 |
#define LOOP_BODY 2 |
||
654 |
|||
655 |
// TODO: extract_n could be a good place to do some error checking. |
||
656 |
// extract_n is often used to extract components of a list that |
||
657 |
// makes up a special form application. If there are not n items |
||
658 |
// present that could be an indication of a syntax error in the |
||
659 |
// special form application. |
||
660 |
// (a b c) -> [a b c] |
||
661 |
57802816 |
static lbm_value extract_n(lbm_value curr, lbm_value *res, unsigned int n) { |
|
662 |
✓✓ | 218965912 |
for (unsigned int i = 0; i < n; i ++) { |
663 |
✓✓ | 161163096 |
if (lbm_is_ptr(curr)) { |
664 |
161163068 |
lbm_cons_t *cell = lbm_ref_cell(curr); |
|
665 |
161163068 |
res[i] = cell->car; |
|
666 |
161163068 |
curr = cell->cdr; |
|
667 |
} else { |
||
668 |
28 |
res[i] = ENC_SYM_NIL; |
|
669 |
} |
||
670 |
} |
||
671 |
57802816 |
return curr; // Rest of list is returned here. |
|
672 |
} |
||
673 |
|||
674 |
73276474 |
static void call_fundamental(lbm_uint fundamental, lbm_value *args, lbm_uint arg_count, eval_context_t *ctx) { |
|
675 |
lbm_value res; |
||
676 |
#ifdef LBM_ALWAYS_GC |
||
677 |
gc(); |
||
678 |
#endif |
||
679 |
73276474 |
res = fundamental_table[fundamental](args, arg_count, ctx); |
|
680 |
✓✓ | 73276474 |
if (lbm_is_error(res)) { |
681 |
✓✓ | 216067 |
if (lbm_is_symbol_merror(res)) { |
682 |
211475 |
gc(); |
|
683 |
211475 |
res = fundamental_table[fundamental](args, arg_count, ctx); |
|
684 |
} |
||
685 |
✓✓ | 216067 |
if (lbm_is_error(res)) { |
686 |
4656 |
error_at_ctx(res, lbm_enc_sym(FUNDAMENTAL_SYMBOLS_START | fundamental)); |
|
687 |
} |
||
688 |
} |
||
689 |
73271818 |
lbm_stack_drop(&ctx->K, arg_count+1); |
|
690 |
73271818 |
ctx->app_cont = true; |
|
691 |
73271818 |
ctx->r = res; |
|
692 |
73271818 |
} |
|
693 |
|||
694 |
28 |
static void atomic_error(void) { |
|
695 |
28 |
is_atomic = false; |
|
696 |
28 |
lbm_set_error_reason((char*)lbm_error_str_forbidden_in_atomic); |
|
697 |
28 |
error_ctx(ENC_SYM_EERROR); |
|
698 |
} |
||
699 |
|||
700 |
// block_current_ctx blocks a context until it is |
||
701 |
// woken up externally or a timeout period of time passes. |
||
702 |
// Blocking while in an atomic block would have bad consequences. |
||
703 |
3323 |
static void block_current_ctx(uint32_t state, lbm_uint sleep_us, bool do_cont) { |
|
704 |
✗✓ | 3323 |
if (is_atomic) atomic_error(); |
705 |
3323 |
ctx_running->timestamp = timestamp_us_callback(); |
|
706 |
3323 |
ctx_running->sleep_us = sleep_us; |
|
707 |
3323 |
ctx_running->state = state; |
|
708 |
3323 |
ctx_running->app_cont = do_cont; |
|
709 |
3323 |
enqueue_ctx(&blocked, ctx_running); |
|
710 |
3323 |
ctx_running = NULL; |
|
711 |
3323 |
} |
|
712 |
|||
713 |
// reblock an essentially already blocked context. |
||
714 |
// Same as block but sets no new timestamp or sleep_us. |
||
715 |
static void reblock_current_ctx(uint32_t state, bool do_cont) { |
||
716 |
if (is_atomic) atomic_error(); |
||
717 |
ctx_running->state = state; |
||
718 |
ctx_running->app_cont = do_cont; |
||
719 |
enqueue_ctx(&blocked, ctx_running); |
||
720 |
ctx_running = NULL; |
||
721 |
} |
||
722 |
|||
723 |
|||
724 |
126 |
lbm_flash_status lbm_write_const_array_padded(uint8_t *data, lbm_uint n, lbm_uint *res) { |
|
725 |
126 |
lbm_uint full_words = n / sizeof(lbm_uint); |
|
726 |
126 |
lbm_uint n_mod = n % sizeof(lbm_uint); |
|
727 |
|||
728 |
✓✓ | 126 |
if (n_mod == 0) { // perfect fit. |
729 |
56 |
return lbm_write_const_raw((lbm_uint*)data, full_words, res); |
|
730 |
} else { |
||
731 |
70 |
lbm_uint last_word = 0; |
|
732 |
70 |
memcpy(&last_word, &data[full_words * sizeof(lbm_uint)], n_mod); |
|
733 |
✓✓ | 70 |
if (full_words >= 1) { |
734 |
14 |
lbm_flash_status s = lbm_write_const_raw((lbm_uint*)data, full_words, res); |
|
735 |
✓✗ | 14 |
if ( s == LBM_FLASH_WRITE_OK) { |
736 |
lbm_uint dummy; |
||
737 |
14 |
s = lbm_write_const_raw(&last_word, 1, &dummy); |
|
738 |
} |
||
739 |
14 |
return s; |
|
740 |
} else { |
||
741 |
56 |
return lbm_write_const_raw(&last_word, 1, res); |
|
742 |
} |
||
743 |
} |
||
744 |
} |
||
745 |
|||
746 |
/****************************************************/ |
||
747 |
/* Error message creation */ |
||
748 |
|||
749 |
#define ERROR_MESSAGE_BUFFER_SIZE_BYTES 256 |
||
750 |
|||
751 |
8156 |
void print_environments(char *buf, unsigned int size) { |
|
752 |
|||
753 |
8156 |
lbm_value curr_l = ctx_running->curr_env; |
|
754 |
8156 |
printf_callback("\tCurrent local environment:\n"); |
|
755 |
✓✓ | 8384 |
while (lbm_type_of(curr_l) == LBM_TYPE_CONS) { |
756 |
228 |
lbm_print_value(buf, (size/2) - 1, lbm_caar(curr_l)); |
|
757 |
228 |
lbm_print_value(buf + (size/2),size/2, lbm_cdr(lbm_car(curr_l))); |
|
758 |
228 |
printf_callback("\t%s = %s\n", buf, buf+(size/2)); |
|
759 |
228 |
curr_l = lbm_cdr(curr_l); |
|
760 |
} |
||
761 |
8156 |
printf_callback("\n\n"); |
|
762 |
8156 |
printf_callback("\tCurrent global environment:\n"); |
|
763 |
8156 |
lbm_value *glob_env = lbm_get_global_env(); |
|
764 |
|||
765 |
✓✓ | 269148 |
for (int i = 0; i < GLOBAL_ENV_ROOTS; i ++) { |
766 |
260992 |
lbm_value curr_g = glob_env[i];; |
|
767 |
✓✓ | 306696 |
while (lbm_type_of(curr_g) == LBM_TYPE_CONS) { |
768 |
|||
769 |
45704 |
lbm_print_value(buf, (size/2) - 1, lbm_caar(curr_g)); |
|
770 |
45704 |
lbm_print_value(buf + (size/2),size/2, lbm_cdr(lbm_car(curr_g))); |
|
771 |
45704 |
printf_callback("\t%s = %s\n", buf, buf+(size/2)); |
|
772 |
45704 |
curr_g = lbm_cdr(curr_g); |
|
773 |
} |
||
774 |
} |
||
775 |
8156 |
} |
|
776 |
|||
777 |
24216 |
void print_error_value(char *buf, lbm_uint bufsize, char *pre, lbm_value v, bool lookup) { |
|
778 |
|||
779 |
24216 |
lbm_print_value(buf, bufsize, v); |
|
780 |
24216 |
printf_callback("%s %s\n",pre, buf); |
|
781 |
✓✓ | 24216 |
if (lookup) { |
782 |
✓✓ | 16060 |
if (lbm_is_symbol(v)) { |
783 |
✓✓ | 9196 |
if (lbm_dec_sym(v) >= RUNTIME_SYMBOLS_START) { |
784 |
1152 |
lbm_value res = ENC_SYM_NIL; |
|
785 |
✓✓✓✓ |
2244 |
if (lbm_env_lookup_b(&res, v, ctx_running->curr_env) || |
786 |
1092 |
lbm_global_env_lookup(&res, v)) { |
|
787 |
760 |
lbm_print_value(buf, bufsize, res); |
|
788 |
760 |
printf_callback(" bound to: %s\n", buf); |
|
789 |
} else { |
||
790 |
392 |
printf_callback(" UNDEFINED\n"); |
|
791 |
} |
||
792 |
} |
||
793 |
} |
||
794 |
} |
||
795 |
24216 |
} |
|
796 |
|||
797 |
8156 |
void print_error_message(lbm_value error, |
|
798 |
bool has_at, |
||
799 |
lbm_value at, |
||
800 |
unsigned int row, |
||
801 |
unsigned int col, |
||
802 |
lbm_int row0, |
||
803 |
lbm_int row1, |
||
804 |
lbm_int cid, |
||
805 |
char *name) { |
||
806 |
/* try to allocate a lbm_print_value buffer on the lbm_memory */ |
||
807 |
8156 |
char *buf = lbm_malloc_reserve(ERROR_MESSAGE_BUFFER_SIZE_BYTES); |
|
808 |
✗✓ | 8156 |
if (!buf) { |
809 |
printf_callback("Error: Not enough free memory to create a human readable error message\n"); |
||
810 |
return; |
||
811 |
} |
||
812 |
|||
813 |
8156 |
print_error_value(buf, ERROR_MESSAGE_BUFFER_SIZE_BYTES," Error:", error, false); |
|
814 |
✗✓ | 8156 |
if (name) { |
815 |
printf_callback( " CTX: %d \"%s\"\n", cid, name); |
||
816 |
} else { |
||
817 |
8156 |
printf_callback( " CTX: %d\n", cid); |
|
818 |
} |
||
819 |
8156 |
print_error_value(buf, ERROR_MESSAGE_BUFFER_SIZE_BYTES," Current:", ctx_running->curr_exp, true); |
|
820 |
✓✓ | 8156 |
if (lbm_error_has_suspect) { |
821 |
1232 |
print_error_value(buf, ERROR_MESSAGE_BUFFER_SIZE_BYTES," At:", lbm_error_suspect, true); |
|
822 |
1232 |
lbm_error_has_suspect = false; |
|
823 |
✓✓ | 6924 |
} else if (has_at) { |
824 |
6672 |
print_error_value(buf, ERROR_MESSAGE_BUFFER_SIZE_BYTES," In:", at, true); |
|
825 |
} |
||
826 |
|||
827 |
8156 |
printf_callback("\n"); |
|
828 |
|||
829 |
✓✓✗✓ |
8156 |
if (lbm_is_symbol(error) && |
830 |
error == ENC_SYM_RERROR) { |
||
831 |
printf_callback(" Line: %u\n", row); |
||
832 |
printf_callback(" Column: %u\n", col); |
||
833 |
✓✓ | 8156 |
} else if (row0 >= 0) { |
834 |
✗✓ | 3976 |
if (row1 < 0) printf_callback(" Starting at row: %d\n", row0); |
835 |
3976 |
else printf_callback(" Between row %d and %d\n", row0, row1); |
|
836 |
} |
||
837 |
|||
838 |
8156 |
printf_callback("\n"); |
|
839 |
|||
840 |
✓✓ | 8156 |
if (ctx_running->error_reason) { |
841 |
1736 |
printf_callback(" Reason: %s\n\n", ctx_running->error_reason); |
|
842 |
} |
||
843 |
✓✗ | 8156 |
if (lbm_verbose) { |
844 |
8156 |
lbm_print_value(buf, ERROR_MESSAGE_BUFFER_SIZE_BYTES, ctx_running->r); |
|
845 |
8156 |
printf_callback(" Current intermediate result: %s\n\n", buf); |
|
846 |
|||
847 |
8156 |
print_environments(buf, ERROR_MESSAGE_BUFFER_SIZE_BYTES); |
|
848 |
8156 |
printf_callback("\n\n"); |
|
849 |
|||
850 |
8156 |
printf_callback(" Stack:\n"); |
|
851 |
✓✓ | 166932 |
for (unsigned int i = 0; i < ctx_running->K.sp; i ++) { |
852 |
158776 |
lbm_print_value(buf, ERROR_MESSAGE_BUFFER_SIZE_BYTES, ctx_running->K.data[i]); |
|
853 |
158776 |
printf_callback(" %s\n", buf); |
|
854 |
} |
||
855 |
} |
||
856 |
8156 |
lbm_free(buf); |
|
857 |
} |
||
858 |
|||
859 |
/****************************************************/ |
||
860 |
/* Tokenizing and parsing */ |
||
861 |
|||
862 |
309988 |
bool create_string_channel(char *str, lbm_value *res, lbm_value dep) { |
|
863 |
|||
864 |
309988 |
lbm_char_channel_t *chan = NULL; |
|
865 |
309988 |
lbm_string_channel_state_t *st = NULL; |
|
866 |
|||
867 |
309988 |
st = (lbm_string_channel_state_t*)lbm_malloc(sizeof(lbm_string_channel_state_t)); |
|
868 |
✓✓ | 309988 |
if (st == NULL) { |
869 |
1018 |
return false; |
|
870 |
} |
||
871 |
308970 |
chan = (lbm_char_channel_t*)lbm_malloc(sizeof(lbm_char_channel_t)); |
|
872 |
✓✓ | 308970 |
if (chan == NULL) { |
873 |
270 |
lbm_free(st); |
|
874 |
270 |
return false; |
|
875 |
} |
||
876 |
|||
877 |
308700 |
lbm_create_string_char_channel(st, chan, str); |
|
878 |
308700 |
lbm_value cell = lbm_heap_allocate_cell(LBM_TYPE_CHANNEL, (lbm_uint) chan, ENC_SYM_CHANNEL_TYPE); |
|
879 |
✗✓ | 308700 |
if (cell == ENC_SYM_MERROR) { |
880 |
lbm_free(st); |
||
881 |
lbm_free(chan); |
||
882 |
return false; |
||
883 |
} |
||
884 |
|||
885 |
308700 |
lbm_char_channel_set_dependency(chan, dep); |
|
886 |
|||
887 |
308700 |
*res = cell; |
|
888 |
308700 |
return true; |
|
889 |
} |
||
890 |
|||
891 |
21588 |
bool lift_char_channel(lbm_char_channel_t *chan , lbm_value *res) { |
|
892 |
21588 |
lbm_value cell = lbm_heap_allocate_cell(LBM_TYPE_CHANNEL, (lbm_uint) chan, ENC_SYM_CHANNEL_TYPE); |
|
893 |
✗✓ | 21588 |
if (cell == ENC_SYM_MERROR) { |
894 |
return false; |
||
895 |
} |
||
896 |
21588 |
*res = cell; |
|
897 |
21588 |
return true; |
|
898 |
} |
||
899 |
|||
900 |
|||
901 |
/****************************************************/ |
||
902 |
/* Queue functions */ |
||
903 |
|||
904 |
695634 |
static void queue_iterator_nm(eval_context_queue_t *q, ctx_fun f, void *arg1, void *arg2) { |
|
905 |
eval_context_t *curr; |
||
906 |
695634 |
curr = q->first; |
|
907 |
|||
908 |
✓✓ | 708960 |
while (curr != NULL) { |
909 |
13326 |
f(curr, arg1, arg2); |
|
910 |
13326 |
curr = curr->next; |
|
911 |
} |
||
912 |
695634 |
} |
|
913 |
|||
914 |
void lbm_all_ctxs_iterator(ctx_fun f, void *arg1, void *arg2) { |
||
915 |
mutex_lock(&qmutex); |
||
916 |
queue_iterator_nm(&blocked, f, arg1, arg2); |
||
917 |
queue_iterator_nm(&queue, f, arg1, arg2); |
||
918 |
if (ctx_running) f(ctx_running, arg1, arg2); |
||
919 |
mutex_unlock(&qmutex); |
||
920 |
} |
||
921 |
|||
922 |
84 |
void lbm_running_iterator(ctx_fun f, void *arg1, void *arg2){ |
|
923 |
84 |
mutex_lock(&qmutex); |
|
924 |
84 |
queue_iterator_nm(&queue, f, arg1, arg2); |
|
925 |
84 |
mutex_unlock(&qmutex); |
|
926 |
84 |
} |
|
927 |
|||
928 |
84 |
void lbm_blocked_iterator(ctx_fun f, void *arg1, void *arg2){ |
|
929 |
84 |
mutex_lock(&qmutex); |
|
930 |
84 |
queue_iterator_nm(&blocked, f, arg1, arg2); |
|
931 |
84 |
mutex_unlock(&qmutex); |
|
932 |
84 |
} |
|
933 |
|||
934 |
91256870 |
static void enqueue_ctx_nm(eval_context_queue_t *q, eval_context_t *ctx) { |
|
935 |
✓✓ | 91256870 |
if (q->last == NULL) { |
936 |
88252634 |
ctx->prev = NULL; |
|
937 |
88252634 |
ctx->next = NULL; |
|
938 |
88252634 |
q->first = ctx; |
|
939 |
88252634 |
q->last = ctx; |
|
940 |
} else { |
||
941 |
3004236 |
ctx->prev = q->last; |
|
942 |
3004236 |
ctx->next = NULL; |
|
943 |
3004236 |
q->last->next = ctx; |
|
944 |
3004236 |
q->last = ctx; |
|
945 |
} |
||
946 |
91256870 |
} |
|
947 |
|||
948 |
57177 |
static void enqueue_ctx(eval_context_queue_t *q, eval_context_t *ctx) { |
|
949 |
57177 |
mutex_lock(&qmutex); |
|
950 |
57177 |
enqueue_ctx_nm(q,ctx); |
|
951 |
57177 |
mutex_unlock(&qmutex); |
|
952 |
57177 |
} |
|
953 |
|||
954 |
18481 |
static eval_context_t *lookup_ctx_nm(eval_context_queue_t *q, lbm_cid cid) { |
|
955 |
eval_context_t *curr; |
||
956 |
18481 |
curr = q->first; |
|
957 |
✓✓ | 18481 |
while (curr != NULL) { |
958 |
✓✗ | 4200 |
if (curr->id == cid) { |
959 |
4200 |
return curr; |
|
960 |
} |
||
961 |
curr = curr->next; |
||
962 |
} |
||
963 |
14281 |
return NULL; |
|
964 |
} |
||
965 |
|||
966 |
3183 |
static bool drop_ctx_nm(eval_context_queue_t *q, eval_context_t *ctx) { |
|
967 |
|||
968 |
3183 |
bool res = false; |
|
969 |
✓✗✗✓ |
3183 |
if (q->first == NULL || q->last == NULL) { |
970 |
if (!(q->last == NULL && q->first == NULL)) { |
||
971 |
/* error state that should not happen */ |
||
972 |
return res; |
||
973 |
} |
||
974 |
/* Queue is empty */ |
||
975 |
return res; |
||
976 |
} |
||
977 |
|||
978 |
3183 |
eval_context_t *curr = q->first; |
|
979 |
✓✗ | 3183 |
while (curr) { |
980 |
✓✗ | 3183 |
if (curr->id == ctx->id) { |
981 |
3183 |
res = true; |
|
982 |
3183 |
eval_context_t *tmp = curr->next; |
|
983 |
✓✗ | 3183 |
if (curr->prev == NULL) { |
984 |
✓✓ | 3183 |
if (curr->next == NULL) { |
985 |
3169 |
q->last = NULL; |
|
986 |
3169 |
q->first = NULL; |
|
987 |
} else { |
||
988 |
14 |
q->first = tmp; |
|
989 |
14 |
tmp->prev = NULL; |
|
990 |
} |
||
991 |
} else { /* curr->prev != NULL */ |
||
992 |
if (curr->next == NULL) { |
||
993 |
q->last = curr->prev; |
||
994 |
q->last->next = NULL; |
||
995 |
} else { |
||
996 |
curr->prev->next = tmp; |
||
997 |
tmp->prev = curr->prev; |
||
998 |
} |
||
999 |
} |
||
1000 |
3183 |
break; |
|
1001 |
} |
||
1002 |
curr = curr->next; |
||
1003 |
} |
||
1004 |
3183 |
return res; |
|
1005 |
} |
||
1006 |
|||
1007 |
/* End execution of the running context. */ |
||
1008 |
22399 |
static void finish_ctx(void) { |
|
1009 |
|||
1010 |
✗✓ | 22399 |
if (!ctx_running) { |
1011 |
return; |
||
1012 |
} |
||
1013 |
/* Drop the continuation stack immediately to free up lbm_memory */ |
||
1014 |
22399 |
lbm_stack_free(&ctx_running->K); |
|
1015 |
22399 |
ctx_done_callback(ctx_running); |
|
1016 |
|||
1017 |
22399 |
lbm_free(ctx_running->name); //free name if in LBM_MEM |
|
1018 |
22399 |
lbm_memory_free((lbm_uint*)ctx_running->error_reason); //free error_reason if in LBM_MEM |
|
1019 |
|||
1020 |
22399 |
lbm_memory_free((lbm_uint*)ctx_running->mailbox); |
|
1021 |
22399 |
lbm_memory_free((lbm_uint*)ctx_running); |
|
1022 |
22399 |
ctx_running = NULL; |
|
1023 |
} |
||
1024 |
|||
1025 |
140 |
static void context_exists(eval_context_t *ctx, void *cid, void *b) { |
|
1026 |
✓✓ | 140 |
if (ctx->id == *(lbm_cid*)cid) { |
1027 |
28 |
*(bool*)b = true; |
|
1028 |
} |
||
1029 |
140 |
} |
|
1030 |
|||
1031 |
1232 |
void lbm_set_error_suspect(lbm_value suspect) { |
|
1032 |
1232 |
lbm_error_suspect = suspect; |
|
1033 |
1232 |
lbm_error_has_suspect = true; |
|
1034 |
1232 |
} |
|
1035 |
|||
1036 |
1316 |
void lbm_set_error_reason(char *error_str) { |
|
1037 |
✓✗ | 1316 |
if (ctx_running != NULL) { |
1038 |
1316 |
ctx_running->error_reason = error_str; |
|
1039 |
} |
||
1040 |
1316 |
} |
|
1041 |
|||
1042 |
// Not possible to CONS_WITH_GC in error_ctx_base (potential loop) |
||
1043 |
8156 |
static void error_ctx_base(lbm_value err_val, bool has_at, lbm_value at, unsigned int row, unsigned int column) { |
|
1044 |
|||
1045 |
✗✓ | 8156 |
if (!check_infer_canary()) { |
1046 |
// If this happens the Runtime system is likely corrupt and |
||
1047 |
// a crash is imminent. |
||
1048 |
// A critical error is issues so that the crash can be handled. |
||
1049 |
// At a minimum the lbm runtime should be restarted. |
||
1050 |
lbm_critical_error(); |
||
1051 |
} |
||
1052 |
|||
1053 |
✗✓ | 8156 |
if (!(lbm_hide_trapped_error && |
1054 |
(ctx_running->flags & EVAL_CPS_CONTEXT_FLAG_TRAP_UNROLL_RETURN))) { |
||
1055 |
8156 |
print_error_message(err_val, |
|
1056 |
has_at, |
||
1057 |
at, |
||
1058 |
row, |
||
1059 |
column, |
||
1060 |
8156 |
ctx_running->row0, |
|
1061 |
8156 |
ctx_running->row1, |
|
1062 |
8156 |
ctx_running->id, |
|
1063 |
8156 |
ctx_running->name); |
|
1064 |
} |
||
1065 |
|||
1066 |
✓✓ | 8156 |
if (ctx_running->flags & EVAL_CPS_CONTEXT_FLAG_TRAP) { |
1067 |
✗✓ | 196 |
if (lbm_heap_num_free() < 3) { |
1068 |
gc(); |
||
1069 |
} |
||
1070 |
|||
1071 |
✓✗ | 196 |
if (lbm_heap_num_free() >= 3) { |
1072 |
196 |
lbm_value msg = lbm_cons(err_val, ENC_SYM_NIL); |
|
1073 |
196 |
msg = lbm_cons(lbm_enc_i(ctx_running->id), msg); |
|
1074 |
196 |
msg = lbm_cons(ENC_SYM_EXIT_ERROR, msg); |
|
1075 |
✓✗ | 196 |
if (!lbm_is_symbol_merror(msg)) { |
1076 |
196 |
lbm_find_receiver_and_send(ctx_running->parent, msg); |
|
1077 |
} |
||
1078 |
} |
||
1079 |
// context dies. |
||
1080 |
✓✓✓✗ |
7960 |
} else if ((ctx_running->flags & EVAL_CPS_CONTEXT_FLAG_TRAP_UNROLL_RETURN) && |
1081 |
(err_val != ENC_SYM_FATAL_ERROR)) { |
||
1082 |
lbm_uint v; |
||
1083 |
✓✗ | 28336 |
while (ctx_running->K.sp > 0) { |
1084 |
28336 |
lbm_pop(&ctx_running->K, &v); |
|
1085 |
✓✓ | 28336 |
if (v == EXCEPTION_HANDLER) { // context continues executing. |
1086 |
7952 |
lbm_value *sptr = get_stack_ptr(ctx_running, 2); |
|
1087 |
7952 |
lbm_set_car(sptr[0], ENC_SYM_EXIT_ERROR); |
|
1088 |
7952 |
stack_reserve(ctx_running, 1)[0] = EXCEPTION_HANDLER; |
|
1089 |
7952 |
ctx_running->app_cont = true; |
|
1090 |
7952 |
ctx_running->r = err_val; |
|
1091 |
7952 |
longjmp(error_jmp_buf, 1); |
|
1092 |
} |
||
1093 |
} |
||
1094 |
err_val = ENC_SYM_FATAL_ERROR; |
||
1095 |
} |
||
1096 |
204 |
ctx_running->r = err_val; |
|
1097 |
204 |
finish_ctx(); |
|
1098 |
204 |
longjmp(error_jmp_buf, 1); |
|
1099 |
} |
||
1100 |
|||
1101 |
7904 |
static void error_at_ctx(lbm_value err_val, lbm_value at) { |
|
1102 |
7904 |
error_ctx_base(err_val, true, at, 0, 0); |
|
1103 |
} |
||
1104 |
|||
1105 |
252 |
static void error_ctx(lbm_value err_val) { |
|
1106 |
252 |
error_ctx_base(err_val, false, 0, 0, 0); |
|
1107 |
} |
||
1108 |
|||
1109 |
static void read_error_ctx(unsigned int row, unsigned int column) { |
||
1110 |
error_ctx_base(ENC_SYM_RERROR, false, 0, row, column); |
||
1111 |
} |
||
1112 |
|||
1113 |
void lbm_critical_error(void) { |
||
1114 |
longjmp(critical_error_jmp_buf, 1); |
||
1115 |
} |
||
1116 |
|||
1117 |
// successfully finish a context |
||
1118 |
22195 |
static void ok_ctx(void) { |
|
1119 |
✓✓ | 22195 |
if (ctx_running->flags & EVAL_CPS_CONTEXT_FLAG_TRAP) { |
1120 |
lbm_value msg; |
||
1121 |
✗✓✗✗ |
140 |
WITH_GC(msg, lbm_heap_allocate_list_init(3, |
1122 |
ENC_SYM_EXIT_OK, |
||
1123 |
lbm_enc_i(ctx_running->id), |
||
1124 |
ctx_running->r)); |
||
1125 |
140 |
lbm_find_receiver_and_send(ctx_running->parent, msg); |
|
1126 |
} |
||
1127 |
22195 |
finish_ctx(); |
|
1128 |
22195 |
} |
|
1129 |
|||
1130 |
93328586 |
static eval_context_t *dequeue_ctx_nm(eval_context_queue_t *q) { |
|
1131 |
✓✓ | 93328586 |
if (q->last == NULL) { |
1132 |
2106353 |
return NULL; |
|
1133 |
} |
||
1134 |
// q->first should only be NULL if q->last is. |
||
1135 |
91222233 |
eval_context_t *res = q->first; |
|
1136 |
|||
1137 |
✓✓ | 91222233 |
if (q->first == q->last) { // One thing in queue |
1138 |
88220339 |
q->first = NULL; |
|
1139 |
88220339 |
q->last = NULL; |
|
1140 |
} else { |
||
1141 |
3001894 |
q->first = q->first->next; |
|
1142 |
3001894 |
q->first->prev = NULL; |
|
1143 |
} |
||
1144 |
91222233 |
res->prev = NULL; |
|
1145 |
91222233 |
res->next = NULL; |
|
1146 |
91222233 |
return res; |
|
1147 |
} |
||
1148 |
|||
1149 |
93328586 |
static void wake_up_ctxs_nm(void) { |
|
1150 |
lbm_uint t_now; |
||
1151 |
|||
1152 |
✓✗ | 93328586 |
if (timestamp_us_callback) { |
1153 |
93328586 |
t_now = timestamp_us_callback(); |
|
1154 |
} else { |
||
1155 |
t_now = 0; |
||
1156 |
} |
||
1157 |
|||
1158 |
93328586 |
eval_context_queue_t *q = &blocked; |
|
1159 |
93328586 |
eval_context_t *curr = q->first; |
|
1160 |
|||
1161 |
✓✓ | 96684327 |
while (curr != NULL) { |
1162 |
lbm_uint t_diff; |
||
1163 |
3355741 |
eval_context_t *next = curr->next; |
|
1164 |
✓✓ | 3355741 |
if (LBM_IS_STATE_WAKE_UP_WAKABLE(curr->state)) { |
1165 |
✗✓ | 3124229 |
if ( curr->timestamp > t_now) { |
1166 |
/* There was an overflow on the counter */ |
||
1167 |
#ifndef LBM64 |
||
1168 |
t_diff = (0xFFFFFFFF - curr->timestamp) + t_now; |
||
1169 |
#else |
||
1170 |
t_diff = (0xFFFFFFFFFFFFFFFF - curr->timestamp) + t_now; |
||
1171 |
#endif |
||
1172 |
} else { |
||
1173 |
3124229 |
t_diff = t_now - curr->timestamp; |
|
1174 |
} |
||
1175 |
|||
1176 |
✓✓ | 3124229 |
if (t_diff >= curr->sleep_us) { |
1177 |
31311 |
eval_context_t *wake_ctx = curr; |
|
1178 |
✓✓ | 31311 |
if (curr == q->last) { |
1179 |
✓✓ | 31270 |
if (curr->prev) { |
1180 |
2287 |
q->last = curr->prev; |
|
1181 |
2287 |
q->last->next = NULL; |
|
1182 |
} else { |
||
1183 |
28983 |
q->first = NULL; |
|
1184 |
28983 |
q->last = NULL; |
|
1185 |
} |
||
1186 |
✓✗ | 41 |
} else if (curr->prev == NULL) { |
1187 |
41 |
q->first = curr->next; |
|
1188 |
41 |
q->first->prev = NULL; |
|
1189 |
} else { |
||
1190 |
curr->prev->next = curr->next; |
||
1191 |
if (curr->next) { |
||
1192 |
curr->next->prev = curr->prev; |
||
1193 |
} |
||
1194 |
} |
||
1195 |
31311 |
wake_ctx->next = NULL; |
|
1196 |
31311 |
wake_ctx->prev = NULL; |
|
1197 |
✓✓ | 31311 |
if (LBM_IS_STATE_TIMEOUT(curr->state)) { |
1198 |
140 |
mailbox_add_mail(wake_ctx, ENC_SYM_TIMEOUT); |
|
1199 |
140 |
wake_ctx->r = ENC_SYM_TIMEOUT; |
|
1200 |
} |
||
1201 |
31311 |
wake_ctx->state = LBM_THREAD_STATE_READY; |
|
1202 |
31311 |
enqueue_ctx_nm(&queue, wake_ctx); |
|
1203 |
} |
||
1204 |
} |
||
1205 |
3355741 |
curr = next; |
|
1206 |
} |
||
1207 |
93328586 |
} |
|
1208 |
|||
1209 |
31258 |
static void yield_ctx(lbm_uint sleep_us) { |
|
1210 |
✓✓ | 31258 |
if (is_atomic) atomic_error(); |
1211 |
✓✗ | 31230 |
if (timestamp_us_callback) { |
1212 |
31230 |
ctx_running->timestamp = timestamp_us_callback(); |
|
1213 |
31230 |
ctx_running->sleep_us = sleep_us; |
|
1214 |
31230 |
ctx_running->state = LBM_THREAD_STATE_SLEEPING; |
|
1215 |
} else { |
||
1216 |
ctx_running->timestamp = 0; |
||
1217 |
ctx_running->sleep_us = 0; |
||
1218 |
ctx_running->state = LBM_THREAD_STATE_SLEEPING; |
||
1219 |
} |
||
1220 |
31230 |
ctx_running->r = ENC_SYM_TRUE; |
|
1221 |
31230 |
ctx_running->app_cont = true; |
|
1222 |
31230 |
enqueue_ctx(&blocked,ctx_running); |
|
1223 |
31230 |
ctx_running = NULL; |
|
1224 |
31230 |
} |
|
1225 |
|||
1226 |
22652 |
static lbm_cid lbm_create_ctx_parent(lbm_value program, lbm_value env, lbm_uint stack_size, lbm_cid parent, uint32_t context_flags, char *name) { |
|
1227 |
|||
1228 |
✗✓ | 22652 |
if (!lbm_is_cons(program)) return -1; |
1229 |
|||
1230 |
22652 |
eval_context_t *ctx = NULL; |
|
1231 |
#ifdef LBM_ALWAYS_GC |
||
1232 |
{ |
||
1233 |
lbm_uint roots[2] = {program, env}; |
||
1234 |
lbm_gc_mark_roots(roots, 2); |
||
1235 |
gc(); |
||
1236 |
} |
||
1237 |
#endif |
||
1238 |
22652 |
ctx = (eval_context_t*)lbm_malloc(sizeof(eval_context_t)); |
|
1239 |
✗✓ | 22652 |
if (ctx == NULL) { |
1240 |
lbm_uint roots[2] = {program, env}; |
||
1241 |
lbm_gc_mark_roots(roots, 2); |
||
1242 |
gc(); |
||
1243 |
ctx = (eval_context_t*)lbm_malloc(sizeof(eval_context_t)); |
||
1244 |
} |
||
1245 |
✗✓ | 22652 |
if (ctx == NULL) return -1; |
1246 |
#ifdef LBM_ALWAYS_GC |
||
1247 |
{ |
||
1248 |
lbm_uint roots[2] = {program, env}; |
||
1249 |
lbm_gc_mark_roots(roots, 2); |
||
1250 |
gc(); |
||
1251 |
} |
||
1252 |
#endif |
||
1253 |
✓✓ | 22652 |
if (!lbm_stack_allocate(&ctx->K, stack_size)) { |
1254 |
28 |
lbm_uint roots[2] = {program, env}; |
|
1255 |
28 |
lbm_gc_mark_roots(roots, 2); |
|
1256 |
28 |
gc(); |
|
1257 |
✓✗ | 28 |
if (!lbm_stack_allocate(&ctx->K, stack_size)) { |
1258 |
28 |
lbm_memory_free((lbm_uint*)ctx); |
|
1259 |
28 |
return -1; |
|
1260 |
} |
||
1261 |
} |
||
1262 |
|||
1263 |
22624 |
lbm_value *mailbox = NULL; |
|
1264 |
#ifdef LBM_ALWAYS_GC |
||
1265 |
{ |
||
1266 |
lbm_uint roots[2] = {program, env}; |
||
1267 |
lbm_gc_mark_roots(roots, 2); |
||
1268 |
gc(); |
||
1269 |
} |
||
1270 |
#endif |
||
1271 |
22624 |
mailbox = (lbm_value*)lbm_memory_allocate(EVAL_CPS_DEFAULT_MAILBOX_SIZE); |
|
1272 |
✗✓ | 22624 |
if (mailbox == NULL) { |
1273 |
lbm_value roots[2] = {program, env}; |
||
1274 |
lbm_gc_mark_roots(roots,2); |
||
1275 |
gc(); |
||
1276 |
mailbox = (lbm_value *)lbm_memory_allocate(EVAL_CPS_DEFAULT_MAILBOX_SIZE); |
||
1277 |
} |
||
1278 |
✗✓ | 22624 |
if (mailbox == NULL) { |
1279 |
lbm_stack_free(&ctx->K); |
||
1280 |
lbm_memory_free((lbm_uint*)ctx); |
||
1281 |
return -1; |
||
1282 |
} |
||
1283 |
|||
1284 |
// TODO: Limit names to 19 chars + 1 char for 0? (or something similar). |
||
1285 |
✓✓ | 22624 |
if (name) { |
1286 |
140 |
lbm_uint name_len = strlen(name) + 1; |
|
1287 |
#ifdef LBM_ALWAYS_GC |
||
1288 |
{ |
||
1289 |
lbm_uint roots[2] = {program, env}; |
||
1290 |
lbm_gc_mark_roots(roots, 2); |
||
1291 |
gc(); |
||
1292 |
} |
||
1293 |
#endif |
||
1294 |
140 |
ctx->name = lbm_malloc(name_len); |
|
1295 |
✗✓ | 140 |
if (ctx->name == NULL) { |
1296 |
lbm_value roots[2] = {program, env}; |
||
1297 |
lbm_gc_mark_roots(roots, 2); |
||
1298 |
gc(); |
||
1299 |
ctx->name = lbm_malloc(name_len); |
||
1300 |
} |
||
1301 |
✗✓ | 140 |
if (ctx->name == NULL) { |
1302 |
lbm_stack_free(&ctx->K); |
||
1303 |
lbm_memory_free((lbm_uint*)mailbox); |
||
1304 |
lbm_memory_free((lbm_uint*)ctx); |
||
1305 |
return -1; |
||
1306 |
} |
||
1307 |
140 |
memcpy(ctx->name, name, name_len); |
|
1308 |
} else { |
||
1309 |
22484 |
ctx->name = NULL; |
|
1310 |
} |
||
1311 |
|||
1312 |
22624 |
lbm_int cid = lbm_memory_address_to_ix((lbm_uint*)ctx); |
|
1313 |
|||
1314 |
22624 |
ctx->program = lbm_cdr(program); |
|
1315 |
22624 |
ctx->curr_exp = lbm_car(program); |
|
1316 |
22624 |
ctx->curr_env = env; |
|
1317 |
22624 |
ctx->r = ENC_SYM_NIL; |
|
1318 |
22624 |
ctx->error_reason = NULL; |
|
1319 |
22624 |
ctx->mailbox = mailbox; |
|
1320 |
22624 |
ctx->mailbox_size = EVAL_CPS_DEFAULT_MAILBOX_SIZE; |
|
1321 |
22624 |
ctx->flags = context_flags; |
|
1322 |
22624 |
ctx->num_mail = 0; |
|
1323 |
22624 |
ctx->app_cont = false; |
|
1324 |
22624 |
ctx->timestamp = 0; |
|
1325 |
22624 |
ctx->sleep_us = 0; |
|
1326 |
22624 |
ctx->state = LBM_THREAD_STATE_READY; |
|
1327 |
22624 |
ctx->prev = NULL; |
|
1328 |
22624 |
ctx->next = NULL; |
|
1329 |
|||
1330 |
22624 |
ctx->row0 = -1; |
|
1331 |
22624 |
ctx->row1 = -1; |
|
1332 |
|||
1333 |
22624 |
ctx->id = cid; |
|
1334 |
22624 |
ctx->parent = parent; |
|
1335 |
|||
1336 |
✗✓ | 22624 |
if (!lbm_push(&ctx->K, DONE)) { |
1337 |
lbm_memory_free((lbm_uint*)ctx->mailbox); |
||
1338 |
lbm_stack_free(&ctx->K); |
||
1339 |
lbm_memory_free((lbm_uint*)ctx); |
||
1340 |
return -1; |
||
1341 |
} |
||
1342 |
|||
1343 |
22624 |
enqueue_ctx(&queue,ctx); |
|
1344 |
|||
1345 |
22624 |
return ctx->id; |
|
1346 |
} |
||
1347 |
|||
1348 |
21588 |
lbm_cid lbm_create_ctx(lbm_value program, lbm_value env, lbm_uint stack_size, char *name) { |
|
1349 |
// Creates a parentless context. |
||
1350 |
21588 |
return lbm_create_ctx_parent(program, |
|
1351 |
env, |
||
1352 |
stack_size, |
||
1353 |
-1, |
||
1354 |
EVAL_CPS_CONTEXT_FLAG_NOTHING, |
||
1355 |
name); |
||
1356 |
} |
||
1357 |
|||
1358 |
140 |
bool lbm_mailbox_change_size(eval_context_t *ctx, lbm_uint new_size) { |
|
1359 |
|||
1360 |
140 |
lbm_value *mailbox = NULL; |
|
1361 |
#ifdef LBM_ALWAYS_GC |
||
1362 |
gc(); |
||
1363 |
#endif |
||
1364 |
140 |
mailbox = (lbm_value*)lbm_memory_allocate(new_size); |
|
1365 |
✓✓ | 140 |
if (mailbox == NULL) { |
1366 |
28 |
gc(); |
|
1367 |
28 |
mailbox = (lbm_value *)lbm_memory_allocate(new_size); |
|
1368 |
} |
||
1369 |
✓✓ | 140 |
if (mailbox == NULL) { |
1370 |
28 |
return false; |
|
1371 |
} |
||
1372 |
|||
1373 |
✗✓ | 112 |
for (lbm_uint i = 0; i < ctx->num_mail; i ++ ) { |
1374 |
mailbox[i] = ctx->mailbox[i]; |
||
1375 |
} |
||
1376 |
112 |
lbm_memory_free(ctx->mailbox); |
|
1377 |
112 |
ctx->mailbox = mailbox; |
|
1378 |
112 |
ctx->mailbox_size = (uint32_t)new_size; |
|
1379 |
112 |
return true; |
|
1380 |
} |
||
1381 |
|||
1382 |
6188 |
static void mailbox_remove_mail(eval_context_t *ctx, lbm_uint ix) { |
|
1383 |
|||
1384 |
✓✓ | 22121 |
for (lbm_uint i = ix; i < ctx->num_mail-1; i ++) { |
1385 |
15933 |
ctx->mailbox[i] = ctx->mailbox[i+1]; |
|
1386 |
} |
||
1387 |
6188 |
ctx->num_mail --; |
|
1388 |
6188 |
} |
|
1389 |
|||
1390 |
7140 |
static bool mailbox_add_mail(eval_context_t *ctx, lbm_value mail) { |
|
1391 |
|||
1392 |
✓✓ | 7140 |
if (ctx->num_mail >= ctx->mailbox_size) { |
1393 |
588 |
mailbox_remove_mail(ctx, 0); |
|
1394 |
} |
||
1395 |
|||
1396 |
7140 |
ctx->mailbox[ctx->num_mail] = mail; |
|
1397 |
7140 |
ctx->num_mail ++; |
|
1398 |
7140 |
return true; |
|
1399 |
} |
||
1400 |
|||
1401 |
/************************************************************** |
||
1402 |
* Advance execution to the next expression in the program. |
||
1403 |
* Assumes programs are not malformed. Apply_eval_program |
||
1404 |
* ensures programs are lists ending in nil. The reader |
||
1405 |
* ensures this likewise. |
||
1406 |
*************************************************************/ |
||
1407 |
64611 |
static void advance_ctx(eval_context_t *ctx) { |
|
1408 |
✓✓ | 64611 |
if (ctx->program) { // fast not-nil check, assume cons if not nil. |
1409 |
42500 |
stack_reserve(ctx, 1)[0] = DONE; |
|
1410 |
42500 |
lbm_cons_t *cell = lbm_ref_cell(ctx->program); |
|
1411 |
42500 |
ctx->curr_exp = cell->car; |
|
1412 |
42500 |
ctx->program = cell->cdr; |
|
1413 |
42500 |
ctx->curr_env = ENC_SYM_NIL; |
|
1414 |
} else { |
||
1415 |
✓✗ | 22111 |
if (ctx_running == ctx) { // This should always be the case because of odd historical reasons. |
1416 |
22111 |
ok_ctx(); |
|
1417 |
} |
||
1418 |
} |
||
1419 |
64611 |
} |
|
1420 |
|||
1421 |
84 |
bool lbm_unblock_ctx(lbm_cid cid, lbm_flat_value_t *fv) { |
|
1422 |
84 |
return event_internal(LBM_EVENT_UNBLOCK_CTX, (lbm_uint)cid, (lbm_uint)fv->buf, fv->buf_size); |
|
1423 |
} |
||
1424 |
|||
1425 |
28 |
bool lbm_unblock_ctx_r(lbm_cid cid) { |
|
1426 |
28 |
mutex_lock(&blocking_extension_mutex); |
|
1427 |
28 |
bool r = false; |
|
1428 |
28 |
eval_context_t *found = NULL; |
|
1429 |
28 |
mutex_lock(&qmutex); |
|
1430 |
28 |
found = lookup_ctx_nm(&blocked, cid); |
|
1431 |
✓✗✓✗ |
28 |
if (found && (LBM_IS_STATE_UNBLOCKABLE(found->state))) { |
1432 |
28 |
drop_ctx_nm(&blocked,found); |
|
1433 |
28 |
found->state = LBM_THREAD_STATE_READY; |
|
1434 |
28 |
enqueue_ctx_nm(&queue,found); |
|
1435 |
28 |
r = true; |
|
1436 |
} |
||
1437 |
28 |
mutex_unlock(&qmutex); |
|
1438 |
28 |
mutex_unlock(&blocking_extension_mutex); |
|
1439 |
28 |
return r; |
|
1440 |
} |
||
1441 |
|||
1442 |
// unblock unboxed is also safe for rmbr:ed things. |
||
1443 |
bool lbm_unblock_ctx_unboxed(lbm_cid cid, lbm_value unboxed) { |
||
1444 |
mutex_lock(&blocking_extension_mutex); |
||
1445 |
bool r = false; |
||
1446 |
eval_context_t *found = NULL; |
||
1447 |
mutex_lock(&qmutex); |
||
1448 |
found = lookup_ctx_nm(&blocked, cid); |
||
1449 |
if (found && (LBM_IS_STATE_UNBLOCKABLE(found->state))) { |
||
1450 |
drop_ctx_nm(&blocked,found); |
||
1451 |
found->r = unboxed; |
||
1452 |
if (lbm_is_error(unboxed)) { |
||
1453 |
get_stack_ptr(found, 1)[0] = TERMINATE; // replace TOS |
||
1454 |
found->app_cont = true; |
||
1455 |
} |
||
1456 |
found->state = LBM_THREAD_STATE_READY; |
||
1457 |
enqueue_ctx_nm(&queue,found); |
||
1458 |
r = true; |
||
1459 |
} |
||
1460 |
mutex_unlock(&qmutex); |
||
1461 |
mutex_unlock(&blocking_extension_mutex); |
||
1462 |
return r; |
||
1463 |
} |
||
1464 |
|||
1465 |
112 |
static bool lbm_block_ctx_base(bool timeout, float t_s) { |
|
1466 |
112 |
mutex_lock(&blocking_extension_mutex); |
|
1467 |
112 |
blocking_extension = true; |
|
1468 |
✗✓ | 112 |
if (timeout) { |
1469 |
blocking_extension_timeout_us = S_TO_US(t_s); |
||
1470 |
blocking_extension_timeout = true; |
||
1471 |
} else { |
||
1472 |
112 |
blocking_extension_timeout = false; |
|
1473 |
} |
||
1474 |
112 |
return true; |
|
1475 |
} |
||
1476 |
|||
1477 |
void lbm_block_ctx_from_extension_timeout(float s) { |
||
1478 |
lbm_block_ctx_base(true, s); |
||
1479 |
} |
||
1480 |
|||
1481 |
112 |
void lbm_block_ctx_from_extension(void) { |
|
1482 |
112 |
lbm_block_ctx_base(false, 0); |
|
1483 |
112 |
} |
|
1484 |
|||
1485 |
// todo: May need to pop rmbrs from stack, if present. |
||
1486 |
// Suspect that the letting the discard cont run is really not a problem. |
||
1487 |
// Either way will be quite confusing what happens to allocated things when undoing block. |
||
1488 |
void lbm_undo_block_ctx_from_extension(void) { |
||
1489 |
blocking_extension = false; |
||
1490 |
blocking_extension_timeout_us = 0; |
||
1491 |
blocking_extension_timeout = false; |
||
1492 |
mutex_unlock(&blocking_extension_mutex); |
||
1493 |
} |
||
1494 |
|||
1495 |
#define LBM_RECEIVER_FOUND 0 |
||
1496 |
#define LBM_RECEIVER_FOUND_MAIL_DELIVERY_FAILED -1 |
||
1497 |
#define LBM_RECEIVER_NOT_FOUND -2 |
||
1498 |
|||
1499 |
10601 |
int lbm_find_receiver_and_send(lbm_cid cid, lbm_value msg) { |
|
1500 |
10601 |
mutex_lock(&qmutex); |
|
1501 |
10601 |
eval_context_t *found = NULL; |
|
1502 |
|||
1503 |
10601 |
found = lookup_ctx_nm(&blocked, cid); |
|
1504 |
✓✓ | 10601 |
if (found) { |
1505 |
✓✓ | 3001 |
if (LBM_IS_STATE_RECV(found->state)) { // only if unblock receivers here. |
1506 |
2987 |
drop_ctx_nm(&blocked,found); |
|
1507 |
2987 |
found->state = LBM_THREAD_STATE_READY; |
|
1508 |
2987 |
enqueue_ctx_nm(&queue,found); |
|
1509 |
} |
||
1510 |
✗✓ | 3001 |
if (!mailbox_add_mail(found, msg)) { |
1511 |
mutex_unlock(&qmutex); |
||
1512 |
return LBM_RECEIVER_FOUND_MAIL_DELIVERY_FAILED; |
||
1513 |
} |
||
1514 |
3001 |
mutex_unlock(&qmutex); |
|
1515 |
3001 |
return LBM_RECEIVER_FOUND; |
|
1516 |
} |
||
1517 |
|||
1518 |
7600 |
found = lookup_ctx_nm(&queue, cid); |
|
1519 |
✓✓ | 7600 |
if (found) { |
1520 |
✗✓ | 1003 |
if (!mailbox_add_mail(found, msg)) { |
1521 |
mutex_unlock(&qmutex); |
||
1522 |
return LBM_RECEIVER_FOUND_MAIL_DELIVERY_FAILED; |
||
1523 |
} |
||
1524 |
1003 |
mutex_unlock(&qmutex); |
|
1525 |
1003 |
return LBM_RECEIVER_FOUND; |
|
1526 |
} |
||
1527 |
|||
1528 |
/* check the current context */ |
||
1529 |
✓✗✓✓ |
6597 |
if (ctx_running && ctx_running->id == cid) { |
1530 |
✗✓ | 2996 |
if (!mailbox_add_mail(ctx_running, msg)) { |
1531 |
mutex_unlock(&qmutex); |
||
1532 |
return LBM_RECEIVER_FOUND_MAIL_DELIVERY_FAILED; |
||
1533 |
} |
||
1534 |
2996 |
mutex_unlock(&qmutex); |
|
1535 |
2996 |
return LBM_RECEIVER_FOUND; |
|
1536 |
} |
||
1537 |
3601 |
mutex_unlock(&qmutex); |
|
1538 |
3601 |
return LBM_RECEIVER_NOT_FOUND; |
|
1539 |
} |
||
1540 |
|||
1541 |
// a match binder looks like (? x) or (? _) for example. |
||
1542 |
// It is a list of two elements where the first is a ? and the second is a symbol. |
||
1543 |
22356 |
static inline lbm_value get_match_binder_variable(lbm_value exp) { |
|
1544 |
22356 |
lbm_value var = ENC_SYM_NIL; // 0 false |
|
1545 |
✓✓ | 22356 |
if (lbm_is_cons(exp)) { |
1546 |
14908 |
lbm_cons_t *e_cell = lbm_ref_cell(exp); |
|
1547 |
14908 |
lbm_value bt = e_cell->car; |
|
1548 |
✓✓✓✗ |
14908 |
if (bt == ENC_SYM_MATCH_ANY && lbm_is_cons(e_cell->cdr)) { |
1549 |
8468 |
var = lbm_ref_cell(e_cell->cdr)->car; |
|
1550 |
} |
||
1551 |
} |
||
1552 |
22356 |
return var; |
|
1553 |
} |
||
1554 |
|||
1555 |
/* Pattern matching is currently implemented as a recursive |
||
1556 |
function and make use of stack relative to the size of |
||
1557 |
expressions that are being matched. */ |
||
1558 |
22356 |
static bool match(lbm_value p, lbm_value e, lbm_value *env, bool *gc) { |
|
1559 |
22356 |
bool r = false; |
|
1560 |
22356 |
lbm_value var = get_match_binder_variable(p); |
|
1561 |
✓✓ | 22356 |
if (var) { |
1562 |
8468 |
lbm_value binding = lbm_cons(var, e); |
|
1563 |
✓✓ | 8468 |
if (lbm_is_cons(binding)) { |
1564 |
8456 |
lbm_value new_env = lbm_cons(binding, *env); |
|
1565 |
✓✗ | 8456 |
if (lbm_is_cons(new_env)) { |
1566 |
8456 |
*env = new_env; |
|
1567 |
8456 |
r = true; |
|
1568 |
} |
||
1569 |
} |
||
1570 |
8468 |
*gc = !r; |
|
1571 |
✓✓ | 13888 |
} else if (lbm_is_symbol(p)) { |
1572 |
✓✓ | 5768 |
if (p == ENC_SYM_DONTCARE) r = true; |
1573 |
4508 |
else r = (p == e); |
|
1574 |
✓✓✓✓ |
8120 |
} else if (lbm_is_cons(p) && lbm_is_cons(e) ) { |
1575 |
5264 |
lbm_cons_t *p_cell = lbm_ref_cell(p); |
|
1576 |
5264 |
lbm_cons_t *e_cell = lbm_ref_cell(e); |
|
1577 |
5264 |
lbm_value headp = p_cell->car; |
|
1578 |
5264 |
lbm_value tailp = p_cell->cdr; |
|
1579 |
5264 |
lbm_value heade = e_cell->car; |
|
1580 |
5264 |
lbm_value taile = e_cell->cdr; |
|
1581 |
5264 |
r = match(headp, heade, env, gc); |
|
1582 |
✓✓✓✓ |
5264 |
r = r && match (tailp, taile, env, gc); |
1583 |
} else { |
||
1584 |
2856 |
r = struct_eq(p, e); |
|
1585 |
} |
||
1586 |
22356 |
return r; |
|
1587 |
} |
||
1588 |
|||
1589 |
// Find match is not very picky about syntax. |
||
1590 |
// A completely malformed recv form is most likely to |
||
1591 |
// just return no_match. |
||
1592 |
5686 |
static int find_match(lbm_value plist, lbm_value *earr, lbm_uint num, lbm_value *e, lbm_value *env) { |
|
1593 |
|||
1594 |
// A pattern list is a list of pattern, expression lists. |
||
1595 |
// ( (p1 e1) (p2 e2) ... (pn en)) |
||
1596 |
5686 |
lbm_value curr_p = plist; |
|
1597 |
5686 |
int n = 0; |
|
1598 |
5686 |
bool need_gc = false; |
|
1599 |
✓✓ | 6286 |
for (int i = 0; i < (int)num; i ++ ) { |
1600 |
6200 |
lbm_value curr_e = earr[i]; |
|
1601 |
✓✓ | 7528 |
while (!lbm_is_symbol_nil(curr_p)) { |
1602 |
6928 |
lbm_value me = get_car(curr_p); |
|
1603 |
✓✓ | 6928 |
if (match(get_car(me), curr_e, env, &need_gc)) { |
1604 |
✗✓ | 5600 |
if (need_gc) return FM_NEED_GC; |
1605 |
5600 |
*e = get_cadr(me); |
|
1606 |
|||
1607 |
✗✓ | 5600 |
if (!lbm_is_symbol_nil(get_cadr(get_cdr(me)))) { |
1608 |
return FM_PATTERN_ERROR; |
||
1609 |
} |
||
1610 |
5600 |
return n; |
|
1611 |
} |
||
1612 |
1328 |
curr_p = get_cdr(curr_p); |
|
1613 |
} |
||
1614 |
600 |
curr_p = plist; /* search all patterns against next exp */ |
|
1615 |
600 |
n ++; |
|
1616 |
} |
||
1617 |
|||
1618 |
86 |
return FM_NO_MATCH; |
|
1619 |
} |
||
1620 |
|||
1621 |
/****************************************************/ |
||
1622 |
/* Garbage collection */ |
||
1623 |
|||
1624 |
360891 |
static void mark_context(eval_context_t *ctx, void *arg1, void *arg2) { |
|
1625 |
(void) arg1; |
||
1626 |
(void) arg2; |
||
1627 |
360891 |
lbm_value roots[3] = {ctx->curr_exp, ctx->program, ctx->r}; |
|
1628 |
360891 |
lbm_gc_mark_env(ctx->curr_env); |
|
1629 |
360891 |
lbm_gc_mark_roots(roots, 3); |
|
1630 |
360891 |
lbm_gc_mark_roots(ctx->mailbox, ctx->num_mail); |
|
1631 |
360891 |
lbm_gc_mark_aux(ctx->K.data, ctx->K.sp); |
|
1632 |
360891 |
} |
|
1633 |
|||
1634 |
347733 |
static int gc(void) { |
|
1635 |
✓✓ | 347733 |
if (ctx_running) { |
1636 |
347705 |
ctx_running->state = ctx_running->state | LBM_THREAD_STATE_GC_BIT; |
|
1637 |
} |
||
1638 |
|||
1639 |
347733 |
gc_requested = false; |
|
1640 |
347733 |
lbm_gc_state_inc(); |
|
1641 |
|||
1642 |
// The freelist should generally be NIL when GC runs. |
||
1643 |
347733 |
lbm_nil_freelist(); |
|
1644 |
347733 |
lbm_value *env = lbm_get_global_env(); |
|
1645 |
✓✓ | 11475189 |
for (int i = 0; i < GLOBAL_ENV_ROOTS; i ++) { |
1646 |
11127456 |
lbm_gc_mark_env(env[i]); |
|
1647 |
} |
||
1648 |
|||
1649 |
347733 |
mutex_lock(&qmutex); // Lock the queues. |
|
1650 |
// Any concurrent messing with the queues |
||
1651 |
// while doing GC cannot possibly be good. |
||
1652 |
347733 |
queue_iterator_nm(&queue, mark_context, NULL, NULL); |
|
1653 |
347733 |
queue_iterator_nm(&blocked, mark_context, NULL, NULL); |
|
1654 |
|||
1655 |
✓✓ | 347733 |
if (ctx_running) { |
1656 |
347705 |
mark_context(ctx_running, NULL, NULL); |
|
1657 |
} |
||
1658 |
347733 |
mutex_unlock(&qmutex); |
|
1659 |
|||
1660 |
#ifdef VISUALIZE_HEAP |
||
1661 |
heap_vis_gen_image(); |
||
1662 |
#endif |
||
1663 |
|||
1664 |
347733 |
int r = lbm_gc_sweep_phase(); |
|
1665 |
347733 |
lbm_heap_new_freelist_length(); |
|
1666 |
347733 |
lbm_memory_update_min_free(); |
|
1667 |
|||
1668 |
✓✓ | 347733 |
if (ctx_running) { |
1669 |
347705 |
ctx_running->state = ctx_running->state & ~LBM_THREAD_STATE_GC_BIT; |
|
1670 |
} |
||
1671 |
347733 |
return r; |
|
1672 |
} |
||
1673 |
|||
1674 |
13812 |
int lbm_perform_gc(void) { |
|
1675 |
13812 |
return gc(); |
|
1676 |
} |
||
1677 |
|||
1678 |
/****************************************************/ |
||
1679 |
/* Evaluation functions */ |
||
1680 |
|||
1681 |
|||
1682 |
224402016 |
static void eval_symbol(eval_context_t *ctx) { |
|
1683 |
224402016 |
lbm_uint s = lbm_dec_sym(ctx->curr_exp); |
|
1684 |
✓✓ | 224402016 |
if (s >= RUNTIME_SYMBOLS_START) { |
1685 |
145524450 |
lbm_value res = ENC_SYM_NIL; |
|
1686 |
✓✓✓✓ |
171569526 |
if (lbm_env_lookup_b(&res, ctx->curr_exp, ctx->curr_env) || |
1687 |
26045076 |
lbm_global_env_lookup(&res, ctx->curr_exp)) { |
|
1688 |
145519606 |
ctx->r = res; |
|
1689 |
145519606 |
ctx->app_cont = true; |
|
1690 |
145519606 |
return; |
|
1691 |
} |
||
1692 |
// Dynamic load attempt |
||
1693 |
// Only symbols of kind RUNTIME can be dynamically loaded. |
||
1694 |
4844 |
const char *sym_str = lbm_get_name_by_symbol(s); |
|
1695 |
4844 |
const char *code_str = NULL; |
|
1696 |
✓✓ | 4844 |
if (!dynamic_load_callback(sym_str, &code_str)) { |
1697 |
84 |
error_at_ctx(ENC_SYM_NOT_FOUND, ctx->curr_exp); |
|
1698 |
} |
||
1699 |
4760 |
lbm_value *sptr = stack_reserve(ctx, 3); |
|
1700 |
4760 |
sptr[0] = ctx->curr_exp; |
|
1701 |
4760 |
sptr[1] = ctx->curr_env; |
|
1702 |
4760 |
sptr[2] = RESUME; |
|
1703 |
|||
1704 |
4760 |
lbm_value chan = ENC_SYM_NIL; |
|
1705 |
#ifdef LBM_ALWAYS_GC |
||
1706 |
gc(); |
||
1707 |
#endif |
||
1708 |
✗✓ | 4760 |
if (!create_string_channel((char *)code_str, &chan, ENC_SYM_NIL)) { |
1709 |
gc(); |
||
1710 |
if (!create_string_channel((char *)code_str, &chan, ENC_SYM_NIL)) { |
||
1711 |
error_ctx(ENC_SYM_MERROR); |
||
1712 |
} |
||
1713 |
} |
||
1714 |
|||
1715 |
// Here, chan has either been assigned or execution has terminated. |
||
1716 |
|||
1717 |
lbm_value loader; |
||
1718 |
✗✓✗✗ |
4760 |
WITH_GC_RMBR_1(loader, lbm_heap_allocate_list_init(2, |
1719 |
ENC_SYM_READ, |
||
1720 |
chan), chan); |
||
1721 |
lbm_value evaluator; |
||
1722 |
✗✓✗✗ |
4760 |
WITH_GC_RMBR_1(evaluator, lbm_heap_allocate_list_init(2, |
1723 |
ENC_SYM_EVAL, |
||
1724 |
loader), loader); |
||
1725 |
4760 |
ctx->curr_exp = evaluator; |
|
1726 |
4760 |
ctx->curr_env = ENC_SYM_NIL; // dynamics should be evaluable in empty local env |
|
1727 |
} else { |
||
1728 |
//special symbols and extensions can be handled the same way. |
||
1729 |
78877566 |
ctx->r = ctx->curr_exp; |
|
1730 |
78877566 |
ctx->app_cont = true; |
|
1731 |
} |
||
1732 |
} |
||
1733 |
|||
1734 |
// (quote e) => e |
||
1735 |
4660259 |
static void eval_quote(eval_context_t *ctx) { |
|
1736 |
4660259 |
ctx->r = get_cadr(ctx->curr_exp); |
|
1737 |
4660259 |
ctx->app_cont = true; |
|
1738 |
4660259 |
} |
|
1739 |
|||
1740 |
// a => a |
||
1741 |
96775550 |
static void eval_selfevaluating(eval_context_t *ctx) { |
|
1742 |
96775550 |
ctx->r = ctx->curr_exp; |
|
1743 |
96775550 |
ctx->app_cont = true; |
|
1744 |
96775550 |
} |
|
1745 |
|||
1746 |
// (progn e1 ... en) |
||
1747 |
14310180 |
static void eval_progn(eval_context_t *ctx) { |
|
1748 |
14310180 |
lbm_value exps = get_cdr(ctx->curr_exp); |
|
1749 |
|||
1750 |
✓✓ | 14310180 |
if (lbm_is_cons(exps)) { |
1751 |
14310152 |
lbm_cons_t *cell = lbm_ref_cell(exps); // already checked that it's cons. |
|
1752 |
14310152 |
ctx->curr_exp = cell->car; |
|
1753 |
✓✓ | 14310152 |
if (lbm_is_cons(cell->cdr)) { // malformed progn not ending in nil is tolerated |
1754 |
11507968 |
lbm_uint *sptr = stack_reserve(ctx, 4); |
|
1755 |
11507968 |
sptr[0] = ctx->curr_env; // env to restore between expressions in progn |
|
1756 |
11507968 |
sptr[1] = lbm_enc_u(0); // Has env been copied (needed for progn local bindings) |
|
1757 |
11507968 |
sptr[2] = cell->cdr; // Requirement: sptr[2] is a cons. |
|
1758 |
11507968 |
sptr[3] = PROGN_REST; |
|
1759 |
} |
||
1760 |
✓✗ | 28 |
} else if (lbm_is_symbol_nil(exps)) { // Empty progn is nil |
1761 |
28 |
ctx->r = ENC_SYM_NIL; |
|
1762 |
28 |
ctx->app_cont = true; |
|
1763 |
} else { |
||
1764 |
error_ctx(ENC_SYM_EERROR); |
||
1765 |
} |
||
1766 |
14310180 |
} |
|
1767 |
|||
1768 |
// (atomic e1 ... en) |
||
1769 |
252 |
static void eval_atomic(eval_context_t *ctx) { |
|
1770 |
✗✓ | 252 |
if (is_atomic) atomic_error(); |
1771 |
252 |
stack_reserve(ctx, 1)[0] = EXIT_ATOMIC; |
|
1772 |
252 |
is_atomic = true; |
|
1773 |
252 |
eval_progn(ctx); |
|
1774 |
252 |
} |
|
1775 |
|||
1776 |
// (call-cc (lambda (k) .... )) |
||
1777 |
224 |
static void eval_callcc(eval_context_t *ctx) { |
|
1778 |
lbm_value cont_array; |
||
1779 |
224 |
lbm_uint *sptr0 = stack_reserve(ctx, 1); |
|
1780 |
✗✓ | 224 |
sptr0[0] = is_atomic ? ENC_SYM_TRUE : ENC_SYM_NIL; |
1781 |
#ifdef LBM_ALWAYS_GC |
||
1782 |
gc(); |
||
1783 |
#endif |
||
1784 |
✗✓ | 224 |
if (!lbm_heap_allocate_lisp_array(&cont_array, ctx->K.sp)) { |
1785 |
gc(); |
||
1786 |
lbm_heap_allocate_lisp_array(&cont_array, ctx->K.sp); |
||
1787 |
} |
||
1788 |
✓✗ | 224 |
if (lbm_is_ptr(cont_array)) { |
1789 |
224 |
lbm_array_header_t *arr = assume_array(cont_array); |
|
1790 |
224 |
memcpy(arr->data, ctx->K.data, ctx->K.sp * sizeof(lbm_uint)); |
|
1791 |
// The stored stack contains the is_atomic flag. |
||
1792 |
// This flag is overwritten in the following execution path. |
||
1793 |
|||
1794 |
224 |
lbm_value acont = cons_with_gc(ENC_SYM_CONT, cont_array, ENC_SYM_NIL); |
|
1795 |
224 |
lbm_value arg_list = cons_with_gc(acont, ENC_SYM_NIL, ENC_SYM_NIL); |
|
1796 |
// Go directly into application evaluation without passing go |
||
1797 |
224 |
lbm_uint *sptr = stack_reserve(ctx, 2); |
|
1798 |
224 |
sptr0[0] = ctx->curr_env; |
|
1799 |
224 |
sptr[0] = arg_list; |
|
1800 |
224 |
sptr[1] = APPLICATION_START; |
|
1801 |
224 |
ctx->curr_exp = get_cadr(ctx->curr_exp); |
|
1802 |
} else { |
||
1803 |
// failed to create continuation array. |
||
1804 |
error_ctx(ENC_SYM_MERROR); |
||
1805 |
} |
||
1806 |
224 |
} |
|
1807 |
|||
1808 |
// (call-cc-unsafe (lambda (k) ... )) |
||
1809 |
// cc-unsafe: continuation should not be bound to any global directly or indirectly. |
||
1810 |
// invoking the continuation must check that target SP holds a continuation that |
||
1811 |
// can be applied using app_cont, otherwise error. The continuation need not be correct |
||
1812 |
// in case user globally bound the continuation, but it may rule out disastrous failure. |
||
1813 |
140 |
static void eval_call_cc_unsafe(eval_context_t *ctx) { |
|
1814 |
140 |
lbm_uint sp = ctx->K.sp; |
|
1815 |
// The stored stack contains the is_atomic flag. |
||
1816 |
// This flag is overwritten in the following execution path. |
||
1817 |
140 |
lbm_value acont = lbm_heap_allocate_list_init(3, |
|
1818 |
ENC_SYM_CONT_SP, |
||
1819 |
lbm_enc_i((int32_t)sp), |
||
1820 |
✗✓ | 140 |
is_atomic ? ENC_SYM_TRUE : ENC_SYM_NIL, ENC_SYM_NIL); |
1821 |
140 |
lbm_value arg_list = cons_with_gc(acont, ENC_SYM_NIL, ENC_SYM_NIL); |
|
1822 |
// Go directly into application evaluation without passing go |
||
1823 |
140 |
lbm_uint *sptr = stack_reserve(ctx, 3); |
|
1824 |
140 |
sptr[0] = ctx->curr_env; |
|
1825 |
140 |
sptr[1] = arg_list; |
|
1826 |
140 |
sptr[2] = APPLICATION_START; |
|
1827 |
140 |
ctx->curr_exp = get_cadr(ctx->curr_exp); |
|
1828 |
140 |
} |
|
1829 |
|||
1830 |
// (define sym exp) |
||
1831 |
#define KEY 1 |
||
1832 |
#define VAL 2 |
||
1833 |
4267004 |
static void eval_define(eval_context_t *ctx) { |
|
1834 |
lbm_value parts[3]; |
||
1835 |
4267004 |
lbm_value rest = extract_n(ctx->curr_exp, parts, 3); |
|
1836 |
4267004 |
lbm_uint *sptr = stack_reserve(ctx, 2); |
|
1837 |
✓✗✓✗ |
4267004 |
if (lbm_is_symbol(parts[KEY]) && lbm_is_symbol_nil(rest)) { |
1838 |
4267004 |
lbm_uint sym_val = lbm_dec_sym(parts[KEY]); |
|
1839 |
4267004 |
sptr[0] = parts[KEY]; |
|
1840 |
✓✗ | 4267004 |
if (sym_val >= RUNTIME_SYMBOLS_START) { |
1841 |
4267004 |
sptr[1] = SET_GLOBAL_ENV; |
|
1842 |
✓✓ | 4267004 |
if (ctx->flags & EVAL_CPS_CONTEXT_FLAG_CONST) { |
1843 |
14 |
stack_reserve(ctx, 1)[0] = MOVE_VAL_TO_FLASH_DISPATCH; |
|
1844 |
} |
||
1845 |
4267004 |
ctx->curr_exp = parts[VAL]; |
|
1846 |
4267004 |
return; |
|
1847 |
} |
||
1848 |
} |
||
1849 |
error_at_ctx(ENC_SYM_EERROR, ctx->curr_exp); |
||
1850 |
} |
||
1851 |
|||
1852 |
/* Allocate closure is only used in eval_lambda currently. |
||
1853 |
Inlining it should use no extra storage. |
||
1854 |
*/ |
||
1855 |
12152 |
static inline lbm_value allocate_closure(lbm_value params, lbm_value body, lbm_value env) { |
|
1856 |
|||
1857 |
#ifdef LBM_ALWAYS_GC |
||
1858 |
gc(); |
||
1859 |
if (lbm_heap_num_free() < 4) { |
||
1860 |
error_ctx(ENC_SYM_MERROR); |
||
1861 |
} |
||
1862 |
#else |
||
1863 |
✗✓ | 12152 |
if (lbm_heap_num_free() < 4) { |
1864 |
gc(); |
||
1865 |
if (lbm_heap_num_free() < 4) { |
||
1866 |
error_ctx(ENC_SYM_MERROR); |
||
1867 |
} |
||
1868 |
} |
||
1869 |
#endif |
||
1870 |
// The freelist will always contain just plain heap-cells. |
||
1871 |
// So dec_ptr is sufficient. |
||
1872 |
12152 |
lbm_value res = lbm_heap_state.freelist; |
|
1873 |
// CONS check is not needed. If num_free is correct, then freelist is a cons-cell. |
||
1874 |
12152 |
lbm_cons_t *heap = lbm_heap_state.heap; |
|
1875 |
12152 |
lbm_uint ix = lbm_dec_ptr(res); |
|
1876 |
12152 |
heap[ix].car = ENC_SYM_CLOSURE; |
|
1877 |
12152 |
ix = lbm_dec_ptr(heap[ix].cdr); |
|
1878 |
12152 |
heap[ix].car = params; |
|
1879 |
12152 |
ix = lbm_dec_ptr(heap[ix].cdr); |
|
1880 |
12152 |
heap[ix].car = body; |
|
1881 |
12152 |
ix = lbm_dec_ptr(heap[ix].cdr); |
|
1882 |
12152 |
heap[ix].car = env; |
|
1883 |
12152 |
lbm_heap_state.freelist = heap[ix].cdr; |
|
1884 |
12152 |
heap[ix].cdr = ENC_SYM_NIL; |
|
1885 |
12152 |
lbm_heap_state.num_alloc+=4; |
|
1886 |
12152 |
return res; |
|
1887 |
} |
||
1888 |
|||
1889 |
/* Eval lambda is cheating, a lot! It does this |
||
1890 |
for performance reasons. The cheats are that |
||
1891 |
1. When closure is created, a reference to the local env |
||
1892 |
in which the lambda was evaluated is added to the closure. |
||
1893 |
Ideally it should have created a list of free variables in the function |
||
1894 |
and then looked up the values of these creating a new environment. |
||
1895 |
2. The global env is considered global constant. As there is no copying |
||
1896 |
of environment bindings into the closure, undefine may break closures. |
||
1897 |
|||
1898 |
some obscure programs such as test_setq_local_closure.lisp does not |
||
1899 |
work properly due to this cheating. |
||
1900 |
*/ |
||
1901 |
// (lambda param-list body-exp) -> (closure param-list body-exp env) |
||
1902 |
12152 |
static void eval_lambda(eval_context_t *ctx) { |
|
1903 |
lbm_value vals[3]; |
||
1904 |
12152 |
extract_n(ctx->curr_exp, vals, 3); |
|
1905 |
12152 |
ctx->r = allocate_closure(vals[1],vals[2], ctx->curr_env); |
|
1906 |
#ifdef CLEAN_UP_CLOSURES |
||
1907 |
lbm_uint sym_id = 0; |
||
1908 |
if (clean_cl_env_symbol) { |
||
1909 |
lbm_value tail = cons_with_gc(ctx->r, ENC_SYM_NIL, ENC_SYM_NIL); |
||
1910 |
lbm_value app = cons_with_gc(clean_cl_env_symbol, tail, tail); |
||
1911 |
ctx->curr_exp = app; |
||
1912 |
} else if (lbm_get_symbol_by_name("clean-cl-env", &sym_id)) { |
||
1913 |
clean_cl_env_symbol = lbm_enc_sym(sym_id); |
||
1914 |
lbm_value tail = cons_with_gc(ctx->r, ENC_SYM_NIL, ENC_SYM_NIL); |
||
1915 |
lbm_value app = cons_with_gc(clean_cl_env_symbol, tail, tail); |
||
1916 |
ctx->curr_exp = app; |
||
1917 |
} else { |
||
1918 |
ctx->app_cont = true; |
||
1919 |
} |
||
1920 |
#else |
||
1921 |
12152 |
ctx->app_cont = true; |
|
1922 |
#endif |
||
1923 |
12152 |
} |
|
1924 |
|||
1925 |
// (if cond-expr then-expr else-expr) |
||
1926 |
21761606 |
static void eval_if(eval_context_t *ctx) { |
|
1927 |
21761606 |
lbm_value cdr = get_cdr(ctx->curr_exp); |
|
1928 |
21761606 |
lbm_value *sptr = stack_reserve(ctx, 3); |
|
1929 |
21761606 |
sptr[0] = get_cdr(cdr); |
|
1930 |
21761606 |
sptr[1] = ctx->curr_env; |
|
1931 |
21761606 |
sptr[2] = IF; |
|
1932 |
21761606 |
ctx->curr_exp = get_car(cdr); |
|
1933 |
21761606 |
} |
|
1934 |
|||
1935 |
// (cond (cond-expr-1 expr-1) |
||
1936 |
// ... |
||
1937 |
// (cond-expr-N expr-N)) |
||
1938 |
1316 |
static void eval_cond(eval_context_t *ctx) { |
|
1939 |
lbm_value cond1[2]; |
||
1940 |
1316 |
lbm_value rest_conds = extract_n(ctx->curr_exp, cond1, 2); |
|
1941 |
|||
1942 |
// end recursion at (cond ) |
||
1943 |
✓✓ | 1316 |
if (lbm_is_symbol_nil(cond1[1])) { |
1944 |
28 |
ctx->r = ENC_SYM_NIL; |
|
1945 |
28 |
ctx->app_cont = true; |
|
1946 |
} else { |
||
1947 |
// Cond is one of the few places where a bit of syntax checking takes place at runtime.. |
||
1948 |
// Maybe dont bother? |
||
1949 |
1288 |
lbm_uint len = lbm_list_length(cond1[1]); |
|
1950 |
✗✓ | 1288 |
if (len != 2) { |
1951 |
lbm_set_error_reason("Incorrect syntax in cond"); |
||
1952 |
error_ctx(ENC_SYM_EERROR); |
||
1953 |
} |
||
1954 |
lbm_value cond_expr[2]; |
||
1955 |
1288 |
extract_n(cond1[1], cond_expr, 2); |
|
1956 |
lbm_value rest; |
||
1957 |
✗✓✗✗ |
1288 |
WITH_GC(rest, lbm_heap_allocate_list_init(2, |
1958 |
cond_expr[1], // Then branch |
||
1959 |
cons_with_gc(ENC_SYM_COND, rest_conds , ENC_SYM_NIL))); |
||
1960 |
1288 |
lbm_value *sptr = stack_reserve(ctx, 3); |
|
1961 |
1288 |
sptr[0] = rest; |
|
1962 |
1288 |
sptr[1] = ctx->curr_env; |
|
1963 |
1288 |
sptr[2] = IF; |
|
1964 |
1288 |
ctx->curr_exp = cond_expr[0]; //condition; |
|
1965 |
} |
||
1966 |
1316 |
} |
|
1967 |
|||
1968 |
11406 |
static void eval_app_cont(eval_context_t *ctx) { |
|
1969 |
11406 |
lbm_stack_drop(&ctx->K, 1); |
|
1970 |
11406 |
ctx->app_cont = true; |
|
1971 |
11406 |
} |
|
1972 |
|||
1973 |
// Create a named location in an environment to later receive a value. |
||
1974 |
40969702 |
static binding_location_status create_binding_location_internal(lbm_value key, lbm_value *env) { |
|
1975 |
✓✓ | 40969702 |
if (lbm_type_of(key) == LBM_TYPE_SYMBOL) { // default case |
1976 |
✓✓✓✓ |
26935608 |
if (key == ENC_SYM_NIL || key == ENC_SYM_DONTCARE) return BL_OK; |
1977 |
lbm_value binding; |
||
1978 |
lbm_value new_env_tmp; |
||
1979 |
21325500 |
binding = lbm_cons(key, ENC_SYM_PLACEHOLDER); |
|
1980 |
21325500 |
new_env_tmp = lbm_cons(binding, *env); |
|
1981 |
✓✓✓✓ |
21325500 |
if (lbm_is_symbol(binding) || lbm_is_symbol(new_env_tmp)) { |
1982 |
21656 |
return BL_NO_MEMORY; |
|
1983 |
} |
||
1984 |
21303844 |
*env = new_env_tmp; |
|
1985 |
✓✗ | 14034094 |
} else if (lbm_is_cons(key)) { // deconstruct case |
1986 |
14034094 |
int r = create_binding_location_internal(get_car(key), env); |
|
1987 |
✓✓ | 14034094 |
if (r == BL_OK) { |
1988 |
14027470 |
r = create_binding_location_internal(get_cdr(key), env); |
|
1989 |
} |
||
1990 |
14034094 |
return r; |
|
1991 |
} |
||
1992 |
21303844 |
return BL_OK; |
|
1993 |
} |
||
1994 |
|||
1995 |
12886482 |
static void create_binding_location(lbm_value key, lbm_value *env) { |
|
1996 |
|||
1997 |
12886482 |
lbm_value env_tmp = *env; |
|
1998 |
#ifdef LBM_ALWAYS_GC |
||
1999 |
lbm_gc_mark_phase(env_tmp); |
||
2000 |
gc(); |
||
2001 |
#endif |
||
2002 |
12886482 |
binding_location_status r = create_binding_location_internal(key, &env_tmp); |
|
2003 |
✓✓ | 12886482 |
if (r != BL_OK) { |
2004 |
✓✗ | 21656 |
if (r == BL_NO_MEMORY) { |
2005 |
21656 |
env_tmp = *env; |
|
2006 |
21656 |
lbm_gc_mark_phase(env_tmp); |
|
2007 |
21656 |
gc(); |
|
2008 |
21656 |
r = create_binding_location_internal(key, &env_tmp); |
|
2009 |
} |
||
2010 |
✓✗✗✗ |
21656 |
switch(r) { |
2011 |
21656 |
case BL_OK: |
|
2012 |
21656 |
break; |
|
2013 |
case BL_NO_MEMORY: |
||
2014 |
error_ctx(ENC_SYM_MERROR); |
||
2015 |
break; |
||
2016 |
case BL_INCORRECT_KEY: |
||
2017 |
error_ctx(ENC_SYM_TERROR); |
||
2018 |
break; |
||
2019 |
} |
||
2020 |
12864826 |
} |
|
2021 |
12886482 |
*env = env_tmp; |
|
2022 |
12886482 |
} |
|
2023 |
|||
2024 |
12128844 |
static void let_bind_values_eval(lbm_value binds, lbm_value exp, lbm_value env, eval_context_t *ctx) { |
|
2025 |
✓✗ | 12128844 |
if (lbm_is_cons(binds)) { |
2026 |
// Preallocate binding locations. |
||
2027 |
12128844 |
lbm_value curr = binds; |
|
2028 |
✓✓ | 24371592 |
while (lbm_is_cons(curr)) { |
2029 |
12242748 |
lbm_value new_env_tmp = env; |
|
2030 |
12242748 |
lbm_cons_t *cell = lbm_ref_cell(curr); // already checked that cons. |
|
2031 |
12242748 |
lbm_value car_curr = cell->car; |
|
2032 |
12242748 |
lbm_value cdr_curr = cell->cdr; |
|
2033 |
12242748 |
lbm_value key = get_car(car_curr); |
|
2034 |
12242748 |
create_binding_location(key, &new_env_tmp); |
|
2035 |
12242748 |
env = new_env_tmp; |
|
2036 |
12242748 |
curr = cdr_curr; |
|
2037 |
} |
||
2038 |
|||
2039 |
12128844 |
lbm_cons_t *cell = lbm_ref_cell(binds); // already checked that cons. |
|
2040 |
12128844 |
lbm_value car_binds = cell->car; |
|
2041 |
12128844 |
lbm_value cdr_binds = cell->cdr; |
|
2042 |
lbm_value key_val[2]; |
||
2043 |
12128844 |
extract_n(car_binds, key_val, 2); |
|
2044 |
|||
2045 |
12128844 |
lbm_uint *sptr = stack_reserve(ctx, 5); |
|
2046 |
12128844 |
sptr[0] = exp; |
|
2047 |
12128844 |
sptr[1] = cdr_binds; |
|
2048 |
12128844 |
sptr[2] = env; |
|
2049 |
12128844 |
sptr[3] = key_val[0]; |
|
2050 |
12128844 |
sptr[4] = BIND_TO_KEY_REST; |
|
2051 |
12128844 |
ctx->curr_exp = key_val[1]; |
|
2052 |
12128844 |
ctx->curr_env = env; |
|
2053 |
} else { |
||
2054 |
ctx->curr_exp = exp; |
||
2055 |
} |
||
2056 |
12128844 |
} |
|
2057 |
|||
2058 |
// (var x (...)) - local binding inside of an progn |
||
2059 |
// var has to take, place root-level nesting within progn. |
||
2060 |
// (progn ... (var a 10) ...) OK! |
||
2061 |
// (progn ... (something (var a 10)) ... ) NOT OK! |
||
2062 |
/* progn stack |
||
2063 |
sp-4 : env |
||
2064 |
sp-3 : 0 |
||
2065 |
sp-2 : rest |
||
2066 |
sp-1 : PROGN_REST |
||
2067 |
*/ |
||
2068 |
643734 |
static void eval_var(eval_context_t *ctx) { |
|
2069 |
✓✗ | 643734 |
if (ctx->K.sp >= 4) { // Possibly in progn |
2070 |
643734 |
lbm_value sv = ctx->K.data[ctx->K.sp - 1]; |
|
2071 |
✓✗✓✗ |
643734 |
if (IS_CONTINUATION(sv) && (sv == PROGN_REST)) { |
2072 |
643734 |
lbm_uint sp = ctx->K.sp; |
|
2073 |
643734 |
uint32_t is_copied = lbm_dec_as_u32(ctx->K.data[sp-3]); |
|
2074 |
✓✓ | 643734 |
if (is_copied == 0) { |
2075 |
lbm_value env; |
||
2076 |
✓✓✗✓ |
632002 |
WITH_GC(env, lbm_env_copy_spine(ctx->K.data[sp-4])); |
2077 |
632002 |
ctx->K.data[sp-3] = lbm_enc_u(1); |
|
2078 |
632002 |
ctx->K.data[sp-4] = env; |
|
2079 |
} |
||
2080 |
643734 |
lbm_value new_env = ctx->K.data[sp-4]; |
|
2081 |
643734 |
lbm_value args = get_cdr(ctx->curr_exp); |
|
2082 |
643734 |
lbm_value key = get_car(args); |
|
2083 |
|||
2084 |
643734 |
create_binding_location(key, &new_env); |
|
2085 |
|||
2086 |
643734 |
ctx->K.data[sp-4] = new_env; |
|
2087 |
|||
2088 |
643734 |
lbm_value v_exp = get_cadr(args); |
|
2089 |
643734 |
lbm_value *sptr = stack_reserve(ctx, 3); |
|
2090 |
643734 |
sptr[0] = new_env; |
|
2091 |
643734 |
sptr[1] = key; |
|
2092 |
643734 |
sptr[2] = PROGN_VAR; |
|
2093 |
// Activating the new environment before the evaluation of the value to be bound. |
||
2094 |
// This would normally shadow the existing value, but create_binding_location sets |
||
2095 |
// the binding to be $placeholder, which is ignored when looking up the value. |
||
2096 |
// The way closures work, the var-variable needs to be in scope during val |
||
2097 |
// evaluation for a recursive closure to be possible. |
||
2098 |
643734 |
ctx->curr_env = new_env; |
|
2099 |
643734 |
ctx->curr_exp = v_exp; |
|
2100 |
643734 |
return; |
|
2101 |
} |
||
2102 |
} |
||
2103 |
lbm_set_error_reason((char*)lbm_error_str_var_outside_progn); |
||
2104 |
error_ctx(ENC_SYM_EERROR); |
||
2105 |
} |
||
2106 |
|||
2107 |
// (setq x (...)) - same as (set 'x (...)) or (setvar 'x (...)) |
||
2108 |
// does not error when given incorrect number of arguments. |
||
2109 |
1775550 |
static void eval_setq(eval_context_t *ctx) { |
|
2110 |
lbm_value parts[3]; |
||
2111 |
1775550 |
extract_n(ctx->curr_exp, parts, 3); |
|
2112 |
1775550 |
lbm_value *sptr = stack_reserve(ctx, 3); |
|
2113 |
1775550 |
sptr[0] = ctx->curr_env; |
|
2114 |
1775550 |
sptr[1] = parts[1]; |
|
2115 |
1775550 |
sptr[2] = SETQ; |
|
2116 |
1775550 |
ctx->curr_exp = parts[2]; |
|
2117 |
1775550 |
} |
|
2118 |
|||
2119 |
364 |
static void eval_move_to_flash(eval_context_t *ctx) { |
|
2120 |
364 |
lbm_value args = get_cdr(ctx->curr_exp); |
|
2121 |
364 |
lbm_value *sptr = stack_reserve(ctx,2); |
|
2122 |
364 |
sptr[0] = args; |
|
2123 |
364 |
sptr[1] = MOVE_TO_FLASH; |
|
2124 |
364 |
ctx->app_cont = true; |
|
2125 |
364 |
} |
|
2126 |
|||
2127 |
// (loop list-of-local-bindings |
||
2128 |
// condition-exp |
||
2129 |
// body-exp) |
||
2130 |
280 |
static void eval_loop(eval_context_t *ctx) { |
|
2131 |
280 |
lbm_value env = ctx->curr_env; |
|
2132 |
lbm_value parts[3]; |
||
2133 |
280 |
extract_n(get_cdr(ctx->curr_exp), parts, 3); |
|
2134 |
280 |
lbm_value *sptr = stack_reserve(ctx, 3); |
|
2135 |
280 |
sptr[0] = parts[LOOP_BODY]; |
|
2136 |
280 |
sptr[1] = parts[LOOP_COND]; |
|
2137 |
280 |
sptr[2] = LOOP_CONDITION; |
|
2138 |
280 |
let_bind_values_eval(parts[LOOP_BINDS], parts[LOOP_COND], env, ctx); |
|
2139 |
280 |
} |
|
2140 |
|||
2141 |
/* (trap expression) |
||
2142 |
* |
||
2143 |
* suggested use: |
||
2144 |
* (match (trap expression) |
||
2145 |
* ((exit-error (? err)) (error-handler err)) |
||
2146 |
* ((exit-ok (? v)) (value-handler v))) |
||
2147 |
*/ |
||
2148 |
8120 |
static void eval_trap(eval_context_t *ctx) { |
|
2149 |
|||
2150 |
8120 |
lbm_value expr = get_cadr(ctx->curr_exp); |
|
2151 |
lbm_value retval; |
||
2152 |
✗✓✗✗ |
8120 |
WITH_GC(retval, lbm_heap_allocate_list(2)); |
2153 |
8120 |
lbm_set_car(retval, ENC_SYM_EXIT_OK); // Assume things will go well. |
|
2154 |
8120 |
lbm_uint *sptr = stack_reserve(ctx,3); |
|
2155 |
8120 |
sptr[0] = retval; |
|
2156 |
8120 |
sptr[1] = ctx->flags; |
|
2157 |
8120 |
sptr[2] = EXCEPTION_HANDLER; |
|
2158 |
8120 |
ctx->flags |= EVAL_CPS_CONTEXT_FLAG_TRAP_UNROLL_RETURN; |
|
2159 |
8120 |
ctx->curr_exp = expr; |
|
2160 |
8120 |
} |
|
2161 |
|||
2162 |
// (let list-of-binding s |
||
2163 |
// body-exp) |
||
2164 |
12128564 |
static void eval_let(eval_context_t *ctx) { |
|
2165 |
12128564 |
lbm_value env = ctx->curr_env; |
|
2166 |
lbm_value parts[3]; |
||
2167 |
12128564 |
extract_n(ctx->curr_exp, parts, 3); |
|
2168 |
12128564 |
let_bind_values_eval(parts[1], parts[2], env, ctx); |
|
2169 |
12128564 |
} |
|
2170 |
|||
2171 |
// (and exp0 ... expN) |
||
2172 |
1982008 |
static void eval_and(eval_context_t *ctx) { |
|
2173 |
1982008 |
lbm_value rest = get_cdr(ctx->curr_exp); |
|
2174 |
✓✓ | 1982008 |
if (lbm_is_symbol_nil(rest)) { |
2175 |
28 |
ctx->app_cont = true; |
|
2176 |
28 |
ctx->r = ENC_SYM_TRUE; |
|
2177 |
} else { |
||
2178 |
1981980 |
lbm_value *sptr = stack_reserve(ctx, 3); |
|
2179 |
1981980 |
sptr[0] = ctx->curr_env; |
|
2180 |
1981980 |
sptr[1] = get_cdr(rest); |
|
2181 |
1981980 |
sptr[2] = AND; |
|
2182 |
1981980 |
ctx->curr_exp = get_car(rest); |
|
2183 |
} |
||
2184 |
1982008 |
} |
|
2185 |
|||
2186 |
// (or exp0 ... expN) |
||
2187 |
7224 |
static void eval_or(eval_context_t *ctx) { |
|
2188 |
7224 |
lbm_value rest = get_cdr(ctx->curr_exp); |
|
2189 |
✓✓ | 7224 |
if (lbm_is_symbol_nil(rest)) { |
2190 |
28 |
ctx->app_cont = true; |
|
2191 |
28 |
ctx->r = ENC_SYM_NIL; |
|
2192 |
} else { |
||
2193 |
7196 |
lbm_value *sptr = stack_reserve(ctx, 3); |
|
2194 |
7196 |
sptr[0] = ctx->curr_env; |
|
2195 |
7196 |
sptr[1] = get_cdr(rest); |
|
2196 |
7196 |
sptr[2] = OR; |
|
2197 |
7196 |
ctx->curr_exp = get_car(rest); |
|
2198 |
} |
||
2199 |
7224 |
} |
|
2200 |
|||
2201 |
// Pattern matching |
||
2202 |
// format: |
||
2203 |
// (match e (pattern body) |
||
2204 |
// (pattern body) |
||
2205 |
// ... ) |
||
2206 |
// |
||
2207 |
// There can be an optional pattern guard: |
||
2208 |
// (match e (pattern guard body) |
||
2209 |
// ... ) |
||
2210 |
// a guard is a boolean expression. |
||
2211 |
// Guards make match, pattern matching more complicated |
||
2212 |
// than the recv pattern matching and requires staged execution |
||
2213 |
// via the continuation system rather than a while loop over a list. |
||
2214 |
2828 |
static void eval_match(eval_context_t *ctx) { |
|
2215 |
|||
2216 |
2828 |
lbm_value rest = get_cdr(ctx->curr_exp); |
|
2217 |
✓✗ | 2828 |
if (lbm_is_cons(rest)) { |
2218 |
2828 |
lbm_cons_t *cell = lbm_ref_cell(rest); |
|
2219 |
2828 |
lbm_value cdr_rest = cell->cdr; |
|
2220 |
2828 |
ctx->curr_exp = cell->car; |
|
2221 |
2828 |
lbm_value *sptr = stack_reserve(ctx, 3); |
|
2222 |
2828 |
sptr[0] = cdr_rest; |
|
2223 |
2828 |
sptr[1] = ctx->curr_env; |
|
2224 |
2828 |
sptr[2] = MATCH; |
|
2225 |
} else { |
||
2226 |
// syntax error to not include at least one pattern |
||
2227 |
error_ctx(ENC_SYM_EERROR); |
||
2228 |
} |
||
2229 |
2828 |
} |
|
2230 |
|||
2231 |
8559 |
static void receive_base(eval_context_t *ctx, lbm_value pats) { |
|
2232 |
✓✓ | 8559 |
if (ctx->num_mail == 0) { |
2233 |
3069 |
block_current_ctx(LBM_THREAD_STATE_RECV_BL,0,false); |
|
2234 |
} else { |
||
2235 |
5490 |
lbm_value *msgs = ctx->mailbox; |
|
2236 |
5490 |
lbm_uint num = ctx->num_mail; |
|
2237 |
|||
2238 |
lbm_value e; |
||
2239 |
5490 |
lbm_value new_env = ctx->curr_env; |
|
2240 |
#ifdef LBM_ALWAYS_GC |
||
2241 |
gc(); |
||
2242 |
#endif |
||
2243 |
5490 |
int n = find_match(pats, msgs, num, &e, &new_env); |
|
2244 |
✗✓ | 5490 |
if (n == FM_NEED_GC) { |
2245 |
gc(); |
||
2246 |
new_env = ctx->curr_env; |
||
2247 |
n = find_match(pats, msgs, num, &e, &new_env); |
||
2248 |
if (n == FM_NEED_GC) { |
||
2249 |
error_ctx(ENC_SYM_MERROR); |
||
2250 |
} |
||
2251 |
} |
||
2252 |
✗✓ | 5490 |
if (n == FM_PATTERN_ERROR) { |
2253 |
lbm_set_error_reason("Incorrect pattern format for recv"); |
||
2254 |
error_at_ctx(ENC_SYM_EERROR,pats); |
||
2255 |
✓✓ | 5490 |
} else if (n >= 0 ) { /* Match */ |
2256 |
5488 |
mailbox_remove_mail(ctx, (lbm_uint)n); |
|
2257 |
5488 |
ctx->curr_env = new_env; |
|
2258 |
5488 |
ctx->curr_exp = e; |
|
2259 |
} else { /* No match go back to sleep */ |
||
2260 |
2 |
ctx->r = ENC_SYM_NO_MATCH; |
|
2261 |
2 |
block_current_ctx(LBM_THREAD_STATE_RECV_BL, 0,false); |
|
2262 |
} |
||
2263 |
} |
||
2264 |
8559 |
return; |
|
2265 |
} |
||
2266 |
|||
2267 |
// Receive-timeout |
||
2268 |
// (recv-to timeout (pattern expr) |
||
2269 |
// (pattern expr)) |
||
2270 |
252 |
static void eval_receive_timeout(eval_context_t *ctx) { |
|
2271 |
✗✓ | 252 |
if (is_atomic) atomic_error(); |
2272 |
252 |
lbm_value timeout_val = get_cadr(ctx->curr_exp); |
|
2273 |
252 |
lbm_value pats = get_cdr(get_cdr(ctx->curr_exp)); |
|
2274 |
✓✓ | 252 |
if (lbm_is_symbol_nil(pats)) { |
2275 |
56 |
lbm_set_error_reason((char*)lbm_error_str_num_args); |
|
2276 |
56 |
error_at_ctx(ENC_SYM_EERROR, ctx->curr_exp); |
|
2277 |
} else { |
||
2278 |
196 |
lbm_value *sptr = stack_reserve(ctx, 2); |
|
2279 |
196 |
sptr[0] = pats; |
|
2280 |
196 |
sptr[1] = RECV_TO; |
|
2281 |
196 |
ctx->curr_exp = timeout_val; |
|
2282 |
} |
||
2283 |
196 |
} |
|
2284 |
|||
2285 |
// Receive |
||
2286 |
// (recv (pattern expr) |
||
2287 |
// (pattern expr)) |
||
2288 |
8587 |
static void eval_receive(eval_context_t *ctx) { |
|
2289 |
✗✓ | 8587 |
if (is_atomic) atomic_error(); |
2290 |
8587 |
lbm_value pats = get_cdr(ctx->curr_exp); |
|
2291 |
✓✓ | 8587 |
if (lbm_is_symbol_nil(pats)) { |
2292 |
28 |
lbm_set_error_reason((char*)lbm_error_str_num_args); |
|
2293 |
28 |
error_at_ctx(ENC_SYM_EERROR,ctx->curr_exp); |
|
2294 |
} else { |
||
2295 |
8559 |
receive_base(ctx, pats); |
|
2296 |
} |
||
2297 |
8559 |
} |
|
2298 |
|||
2299 |
/*********************************************************/ |
||
2300 |
/* Continuation functions */ |
||
2301 |
|||
2302 |
// cont_set_global_env: |
||
2303 |
// |
||
2304 |
// s[sp-1] = Key-symbol |
||
2305 |
// |
||
2306 |
// ctx->r = Value |
||
2307 |
4267508 |
static void cont_set_global_env(eval_context_t *ctx){ |
|
2308 |
|||
2309 |
lbm_value key; |
||
2310 |
4267508 |
lbm_value val = ctx->r; |
|
2311 |
|||
2312 |
4267508 |
lbm_pop(&ctx->K, &key); |
|
2313 |
4267508 |
lbm_uint dec_key = lbm_dec_sym(key); |
|
2314 |
4267508 |
lbm_uint ix_key = dec_key & GLOBAL_ENV_MASK; |
|
2315 |
4267508 |
lbm_value *global_env = lbm_get_global_env(); |
|
2316 |
4267508 |
lbm_uint orig_env = global_env[ix_key]; |
|
2317 |
lbm_value new_env; |
||
2318 |
// A key is a symbol and should not need to be remembered. |
||
2319 |
✓✓✗✓ |
4267508 |
WITH_GC(new_env, lbm_env_set(orig_env,key,val)); |
2320 |
|||
2321 |
4267508 |
global_env[ix_key] = new_env; |
|
2322 |
4267508 |
ctx->r = val; |
|
2323 |
|||
2324 |
4267508 |
ctx->app_cont = true; |
|
2325 |
|||
2326 |
4267508 |
return; |
|
2327 |
} |
||
2328 |
|||
2329 |
// cont_resume: |
||
2330 |
// |
||
2331 |
// s[sp-2] = Expression |
||
2332 |
// s[sp-1] = Environment |
||
2333 |
// |
||
2334 |
// ctx->r = Irrelevant. |
||
2335 |
4760 |
static void cont_resume(eval_context_t *ctx) { |
|
2336 |
4760 |
lbm_pop_2(&ctx->K, &ctx->curr_env, &ctx->curr_exp); |
|
2337 |
4760 |
} |
|
2338 |
|||
2339 |
// cont_progn_rest: |
||
2340 |
// |
||
2341 |
// s[sp-3] = Environment to evaluate each expression in. |
||
2342 |
// s[sp-2] = Flag indicating if env has been copied. |
||
2343 |
// s[sp-1] = list of expressions to evaluate. |
||
2344 |
// |
||
2345 |
// ctx->r = Result of last evaluated expression. |
||
2346 |
13862596 |
static void cont_progn_rest(eval_context_t *ctx) { |
|
2347 |
13862596 |
lbm_value *sptr = get_stack_ptr(ctx, 3); |
|
2348 |
|||
2349 |
13862596 |
lbm_value env = sptr[0]; |
|
2350 |
// eval_progn and cont_progn_rest both ensure that sptr[2] is a list |
||
2351 |
// whenever cont_progn_rest is called. |
||
2352 |
|||
2353 |
13862596 |
lbm_cons_t *rest_cell = lbm_ref_cell(sptr[2]); |
|
2354 |
13862596 |
lbm_value rest_cdr = rest_cell->cdr; |
|
2355 |
13862596 |
ctx->curr_exp = rest_cell->car;; |
|
2356 |
13862596 |
ctx->curr_env = env; |
|
2357 |
✓✓ | 13862596 |
if (lbm_is_cons(rest_cdr)) { |
2358 |
2354894 |
sptr[2] = rest_cdr; // Requirement: rest_cdr is a cons |
|
2359 |
2354894 |
stack_reserve(ctx, 1)[0] = PROGN_REST; |
|
2360 |
} else { |
||
2361 |
// Nothing is pushed to stack for final element in progn. (tail-call req) |
||
2362 |
11507702 |
lbm_stack_drop(&ctx->K, 3); |
|
2363 |
} |
||
2364 |
13862596 |
} |
|
2365 |
|||
2366 |
84 |
static void cont_wait(eval_context_t *ctx) { |
|
2367 |
|||
2368 |
lbm_value cid_val; |
||
2369 |
84 |
lbm_pop(&ctx->K, &cid_val); |
|
2370 |
84 |
lbm_cid cid = (lbm_cid)lbm_dec_i(cid_val); |
|
2371 |
|||
2372 |
84 |
bool exists = false; |
|
2373 |
|||
2374 |
84 |
lbm_blocked_iterator(context_exists, &cid, &exists); |
|
2375 |
84 |
lbm_running_iterator(context_exists, &cid, &exists); |
|
2376 |
|||
2377 |
✗✓ | 84 |
if (ctx_running->id == cid) { |
2378 |
exists = true; |
||
2379 |
} |
||
2380 |
|||
2381 |
✓✓ | 84 |
if (exists) { |
2382 |
28 |
lbm_value *sptr = stack_reserve(ctx, 2); |
|
2383 |
28 |
sptr[0] = lbm_enc_i(cid); |
|
2384 |
28 |
sptr[1] = WAIT; |
|
2385 |
28 |
ctx->r = ENC_SYM_TRUE; |
|
2386 |
28 |
ctx->app_cont = true; |
|
2387 |
28 |
yield_ctx(50000); |
|
2388 |
} else { |
||
2389 |
56 |
ctx->r = ENC_SYM_TRUE; |
|
2390 |
56 |
ctx->app_cont = true; |
|
2391 |
} |
||
2392 |
84 |
} |
|
2393 |
|||
2394 |
1775844 |
static lbm_value perform_setvar(lbm_value key, lbm_value val, lbm_value env) { |
|
2395 |
|||
2396 |
1775844 |
lbm_uint s = lbm_dec_sym(key); |
|
2397 |
✓✓ | 1775844 |
if (s >= RUNTIME_SYMBOLS_START) { |
2398 |
1775816 |
lbm_value new_env = lbm_env_modify_binding(env, key, val); |
|
2399 |
✓✓✓✗ |
1775816 |
if (lbm_is_symbol(new_env) && new_env == ENC_SYM_NOT_FOUND) { |
2400 |
841372 |
lbm_uint ix_key = lbm_dec_sym(key) & GLOBAL_ENV_MASK; |
|
2401 |
841372 |
lbm_value *glob_env = lbm_get_global_env(); |
|
2402 |
841372 |
new_env = lbm_env_modify_binding(glob_env[ix_key], key, val); |
|
2403 |
841372 |
glob_env[ix_key] = new_env; |
|
2404 |
} |
||
2405 |
✓✓✓✗ |
1775816 |
if (lbm_is_symbol(new_env) && new_env == ENC_SYM_NOT_FOUND) { |
2406 |
28 |
lbm_set_error_reason((char*)lbm_error_str_variable_not_bound); |
|
2407 |
28 |
error_at_ctx(ENC_SYM_NOT_FOUND, key); |
|
2408 |
} |
||
2409 |
1775788 |
return val; |
|
2410 |
} |
||
2411 |
28 |
error_at_ctx(ENC_SYM_EERROR, ENC_SYM_SETVAR); |
|
2412 |
return ENC_SYM_NIL; // unreachable |
||
2413 |
} |
||
2414 |
|||
2415 |
420 |
static void apply_setvar(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) { |
|
2416 |
✓✓✓✓ |
420 |
if (nargs == 2 && lbm_is_symbol(args[0])) { |
2417 |
lbm_value res; |
||
2418 |
✗✓✗✗ |
308 |
WITH_GC(res, perform_setvar(args[0], args[1], ctx->curr_env)); |
2419 |
308 |
ctx->r = args[1]; |
|
2420 |
308 |
lbm_stack_drop(&ctx->K, nargs+1); |
|
2421 |
308 |
ctx->app_cont = true; |
|
2422 |
} else { |
||
2423 |
✓✓ | 112 |
if (nargs == 2) lbm_set_error_reason((char*)lbm_error_str_incorrect_arg); |
2424 |
56 |
else lbm_set_error_reason((char*)lbm_error_str_num_args); |
|
2425 |
112 |
error_at_ctx(ENC_SYM_EERROR, ENC_SYM_SETVAR); |
|
2426 |
} |
||
2427 |
308 |
} |
|
2428 |
|||
2429 |
|||
2430 |
#define READING_EXPRESSION ((0 << LBM_VAL_SHIFT) | LBM_TYPE_U) |
||
2431 |
#define READING_PROGRAM ((1 << LBM_VAL_SHIFT) | LBM_TYPE_U) |
||
2432 |
#define READING_PROGRAM_INCREMENTALLY ((2 << LBM_VAL_SHIFT) | LBM_TYPE_U) |
||
2433 |
|||
2434 |
330428 |
static void apply_read_base(lbm_value *args, lbm_uint nargs, eval_context_t *ctx, bool program, bool incremental) { |
|
2435 |
✓✓ | 330428 |
if (nargs == 1) { |
2436 |
330400 |
lbm_value chan = ENC_SYM_NIL; |
|
2437 |
✓✓ | 330400 |
if (lbm_type_of_functional(args[0]) == LBM_TYPE_ARRAY) { |
2438 |
304052 |
char *str = lbm_dec_str(args[0]); |
|
2439 |
✓✓ | 304052 |
if (str) { |
2440 |
#ifdef LBM_ALWAYS_GC |
||
2441 |
gc(); |
||
2442 |
#endif |
||
2443 |
✓✓ | 303940 |
if (!create_string_channel(lbm_dec_str(args[0]), &chan, args[0])) { |
2444 |
1288 |
gc(); |
|
2445 |
✗✓ | 1288 |
if (!create_string_channel(lbm_dec_str(args[0]), &chan, args[0])) { |
2446 |
error_ctx(ENC_SYM_MERROR); |
||
2447 |
} |
||
2448 |
} |
||
2449 |
} else { |
||
2450 |
112 |
error_ctx(ENC_SYM_EERROR); |
|
2451 |
} |
||
2452 |
✓✗ | 26348 |
} else if (lbm_type_of(args[0]) == LBM_TYPE_CHANNEL) { |
2453 |
26348 |
chan = args[0]; |
|
2454 |
// Streaming transfers can freeze the evaluator if the stream is cut while |
||
2455 |
// the reader is reading inside of an atomic block. |
||
2456 |
// It is generally not advisable to read in an atomic block but now it is also |
||
2457 |
// enforced in the case where it can cause problems. |
||
2458 |
✓✓✗✓ |
26348 |
if (lbm_channel_may_block(lbm_dec_channel(chan)) && is_atomic) { |
2459 |
lbm_set_error_reason((char*)lbm_error_str_forbidden_in_atomic); |
||
2460 |
is_atomic = false; |
||
2461 |
error_ctx(ENC_SYM_EERROR); |
||
2462 |
} |
||
2463 |
} else { |
||
2464 |
error_ctx(ENC_SYM_EERROR); |
||
2465 |
} |
||
2466 |
330288 |
lbm_value *sptr = get_stack_ptr(ctx, 2); |
|
2467 |
|||
2468 |
// If we are inside a reader, its settings are stored. |
||
2469 |
330288 |
sptr[0] = lbm_enc_u(ctx->flags); // flags stored. |
|
2470 |
330288 |
sptr[1] = chan; |
|
2471 |
330288 |
lbm_value *rptr = stack_reserve(ctx,2); |
|
2472 |
✓✓✓✗ |
330288 |
if (!program && !incremental) { |
2473 |
297024 |
rptr[0] = READING_EXPRESSION; |
|
2474 |
✓✗✓✓ |
33264 |
} else if (program && !incremental) { |
2475 |
11186 |
rptr[0] = READING_PROGRAM; |
|
2476 |
✓✗✓✗ |
22078 |
} else if (program && incremental) { |
2477 |
22078 |
rptr[0] = READING_PROGRAM_INCREMENTALLY; |
|
2478 |
} // the last combo is illegal |
||
2479 |
330288 |
rptr[1] = READ_DONE; |
|
2480 |
|||
2481 |
// Each reader starts in a fresh situation |
||
2482 |
330288 |
ctx->flags &= ~EVAL_CPS_CONTEXT_READER_FLAGS_MASK; |
|
2483 |
330288 |
ctx->r = ENC_SYM_NIL; // set r to a known state. |
|
2484 |
|||
2485 |
✓✓ | 330288 |
if (program) { |
2486 |
✓✓ | 33264 |
if (incremental) { |
2487 |
22078 |
ctx->flags |= EVAL_CPS_CONTEXT_FLAG_INCREMENTAL_READ; |
|
2488 |
22078 |
lbm_value *rptr1 = stack_reserve(ctx,3); |
|
2489 |
22078 |
rptr1[0] = chan; |
|
2490 |
22078 |
rptr1[1] = ctx->curr_env; |
|
2491 |
22078 |
rptr1[2] = READ_EVAL_CONTINUE; |
|
2492 |
} else { |
||
2493 |
11186 |
ctx->flags &= ~EVAL_CPS_CONTEXT_FLAG_INCREMENTAL_READ; |
|
2494 |
11186 |
lbm_value *rptr1 = stack_reserve(ctx,4); |
|
2495 |
11186 |
rptr1[0] = ENC_SYM_NIL; |
|
2496 |
11186 |
rptr1[1] = ENC_SYM_NIL; |
|
2497 |
11186 |
rptr1[2] = chan; |
|
2498 |
11186 |
rptr1[3] = READ_APPEND_CONTINUE; |
|
2499 |
} |
||
2500 |
} |
||
2501 |
330288 |
rptr = stack_reserve(ctx,3); // reuse of variable rptr |
|
2502 |
330288 |
rptr[0] = chan; |
|
2503 |
330288 |
rptr[1] = lbm_enc_u(1); |
|
2504 |
330288 |
rptr[2] = READ_NEXT_TOKEN; |
|
2505 |
330288 |
ctx->app_cont = true; |
|
2506 |
} else { |
||
2507 |
28 |
lbm_set_error_reason((char*)lbm_error_str_num_args); |
|
2508 |
28 |
error_ctx(ENC_SYM_EERROR); |
|
2509 |
} |
||
2510 |
330288 |
} |
|
2511 |
|||
2512 |
11270 |
static void apply_read_program(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) { |
|
2513 |
11270 |
apply_read_base(args,nargs,ctx,true,false); |
|
2514 |
11186 |
} |
|
2515 |
|||
2516 |
22078 |
static void apply_read_eval_program(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) { |
|
2517 |
22078 |
apply_read_base(args,nargs,ctx,true,true); |
|
2518 |
22078 |
} |
|
2519 |
|||
2520 |
297080 |
static void apply_read(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) { |
|
2521 |
297080 |
apply_read_base(args,nargs,ctx,false,false); |
|
2522 |
297024 |
} |
|
2523 |
|||
2524 |
1064 |
static void apply_spawn_base(lbm_value *args, lbm_uint nargs, eval_context_t *ctx, uint32_t context_flags) { |
|
2525 |
|||
2526 |
1064 |
lbm_uint stack_size = EVAL_CPS_DEFAULT_STACK_SIZE; |
|
2527 |
1064 |
lbm_uint closure_pos = 0; |
|
2528 |
1064 |
char *name = NULL; |
|
2529 |
// allowed arguments: |
||
2530 |
// (spawn opt-name opt-stack-size closure arg1 ... argN) |
||
2531 |
|||
2532 |
✓✗✓✓ |
2128 |
if (nargs >= 1 && |
2533 |
1064 |
lbm_is_closure(args[0])) { |
|
2534 |
840 |
closure_pos = 0; |
|
2535 |
✓✗✓✓ |
448 |
} else if (nargs >= 2 && |
2536 |
✓✗ | 308 |
lbm_is_number(args[0]) && |
2537 |
84 |
lbm_is_closure(args[1])) { |
|
2538 |
84 |
stack_size = lbm_dec_as_u32(args[0]); |
|
2539 |
84 |
closure_pos = 1; |
|
2540 |
✓✗✓✗ |
280 |
} else if (nargs >= 2 && |
2541 |
✗✓ | 280 |
lbm_is_array_r(args[0]) && |
2542 |
140 |
lbm_is_closure(args[1])) { |
|
2543 |
name = lbm_dec_str(args[0]); |
||
2544 |
closure_pos = 1; |
||
2545 |
✓✗✓✗ |
280 |
} else if (nargs >= 3 && |
2546 |
✓✗ | 280 |
lbm_is_array_r(args[0]) && |
2547 |
✓✗ | 280 |
lbm_is_number(args[1]) && |
2548 |
140 |
lbm_is_closure(args[2])) { |
|
2549 |
140 |
stack_size = lbm_dec_as_u32(args[1]); |
|
2550 |
140 |
closure_pos = 2; |
|
2551 |
140 |
name = lbm_dec_str(args[0]); |
|
2552 |
} else { |
||
2553 |
if (context_flags & EVAL_CPS_CONTEXT_FLAG_TRAP) |
||
2554 |
error_at_ctx(ENC_SYM_TERROR,ENC_SYM_SPAWN_TRAP); |
||
2555 |
else |
||
2556 |
error_at_ctx(ENC_SYM_TERROR,ENC_SYM_SPAWN); |
||
2557 |
} |
||
2558 |
|||
2559 |
lbm_value cl[3]; |
||
2560 |
1064 |
extract_n(get_cdr(args[closure_pos]), cl, 3); |
|
2561 |
1064 |
lbm_value curr_param = cl[CLO_PARAMS]; |
|
2562 |
1064 |
lbm_value clo_env = cl[CLO_ENV]; |
|
2563 |
1064 |
lbm_uint i = closure_pos + 1; |
|
2564 |
✓✓✓✗ |
1820 |
while (lbm_is_cons(curr_param) && i <= nargs) { |
2565 |
756 |
lbm_value entry = cons_with_gc(get_car(curr_param), args[i], clo_env); |
|
2566 |
756 |
lbm_value aug_env = cons_with_gc(entry, clo_env,ENC_SYM_NIL); |
|
2567 |
756 |
clo_env = aug_env; |
|
2568 |
756 |
curr_param = get_cdr(curr_param); |
|
2569 |
756 |
i ++; |
|
2570 |
} |
||
2571 |
|||
2572 |
1064 |
lbm_stack_drop(&ctx->K, nargs+1); |
|
2573 |
|||
2574 |
1064 |
lbm_value program = cons_with_gc(cl[CLO_BODY], ENC_SYM_NIL, clo_env); |
|
2575 |
|||
2576 |
1064 |
lbm_cid cid = lbm_create_ctx_parent(program, |
|
2577 |
clo_env, |
||
2578 |
stack_size, |
||
2579 |
lbm_get_current_cid(), |
||
2580 |
context_flags, |
||
2581 |
name); |
||
2582 |
1064 |
ctx->r = lbm_enc_i(cid); |
|
2583 |
1064 |
ctx->app_cont = true; |
|
2584 |
✓✓ | 1064 |
if (cid == -1) error_ctx(ENC_SYM_MERROR); // Kill parent and signal out of memory. |
2585 |
1036 |
} |
|
2586 |
|||
2587 |
728 |
static void apply_spawn(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) { |
|
2588 |
728 |
apply_spawn_base(args,nargs,ctx, EVAL_CPS_CONTEXT_FLAG_NOTHING); |
|
2589 |
700 |
} |
|
2590 |
|||
2591 |
336 |
static void apply_spawn_trap(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) { |
|
2592 |
336 |
apply_spawn_base(args,nargs,ctx, EVAL_CPS_CONTEXT_FLAG_TRAP); |
|
2593 |
336 |
} |
|
2594 |
|||
2595 |
28405 |
static void apply_yield(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) { |
|
2596 |
✓✗✓✗ |
56810 |
if (nargs == 1 && lbm_is_number(args[0])) { |
2597 |
28405 |
lbm_uint ts = lbm_dec_as_u32(args[0]); |
|
2598 |
28405 |
lbm_stack_drop(&ctx->K, nargs+1); |
|
2599 |
28405 |
yield_ctx(ts); |
|
2600 |
} else { |
||
2601 |
lbm_set_error_reason((char*)lbm_error_str_no_number); |
||
2602 |
error_at_ctx(ENC_SYM_TERROR, ENC_SYM_YIELD); |
||
2603 |
} |
||
2604 |
28405 |
} |
|
2605 |
|||
2606 |
2128 |
static void apply_sleep(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) { |
|
2607 |
✓✗✓✗ |
4228 |
if (nargs == 1 && lbm_is_number(args[0])) { |
2608 |
2128 |
lbm_uint ts = (lbm_uint)(1000000.0f * lbm_dec_as_float(args[0])); |
|
2609 |
2128 |
lbm_stack_drop(&ctx->K, nargs+1); |
|
2610 |
2128 |
yield_ctx(ts); |
|
2611 |
} else { |
||
2612 |
lbm_set_error_reason((char*)lbm_error_str_no_number); |
||
2613 |
error_at_ctx(ENC_SYM_TERROR, ENC_SYM_SLEEP); |
||
2614 |
} |
||
2615 |
2100 |
} |
|
2616 |
|||
2617 |
56 |
static void apply_wait(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) { |
|
2618 |
✓✗✓✗ |
112 |
if (nargs == 1 && lbm_type_of(args[0]) == LBM_TYPE_I) { |
2619 |
56 |
lbm_cid cid = (lbm_cid)lbm_dec_i(args[0]); |
|
2620 |
56 |
lbm_value *sptr = get_stack_ptr(ctx, 2); |
|
2621 |
56 |
sptr[0] = lbm_enc_i(cid); |
|
2622 |
56 |
sptr[1] = WAIT; |
|
2623 |
56 |
ctx->r = ENC_SYM_TRUE; |
|
2624 |
56 |
ctx->app_cont = true; |
|
2625 |
56 |
yield_ctx(50000); |
|
2626 |
} else { |
||
2627 |
error_at_ctx(ENC_SYM_TERROR, ENC_SYM_WAIT); |
||
2628 |
} |
||
2629 |
56 |
} |
|
2630 |
|||
2631 |
/* (eval expr) |
||
2632 |
(eval env expr) */ |
||
2633 |
3181444 |
static void apply_eval(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) { |
|
2634 |
✓✗ | 3181444 |
if ( nargs == 1) { |
2635 |
3181444 |
ctx->curr_exp = args[0]; |
|
2636 |
} else if (nargs == 2) { |
||
2637 |
ctx->curr_exp = args[1]; |
||
2638 |
ctx->curr_env = args[0]; |
||
2639 |
} else { |
||
2640 |
lbm_set_error_reason((char*)lbm_error_str_num_args); |
||
2641 |
error_at_ctx(ENC_SYM_EERROR, ENC_SYM_EVAL); |
||
2642 |
} |
||
2643 |
3181444 |
lbm_stack_drop(&ctx->K, nargs+1); |
|
2644 |
3181444 |
} |
|
2645 |
|||
2646 |
11410 |
static void apply_eval_program(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) { |
|
2647 |
11410 |
int prg_pos = 0; |
|
2648 |
✗✓ | 11410 |
if (nargs == 2) { |
2649 |
prg_pos = 1; |
||
2650 |
ctx->curr_env = args[0]; // No check that args[0] is an actual env. |
||
2651 |
} |
||
2652 |
✗✓✗✗ |
11410 |
if (nargs == 1 || nargs == 2) { |
2653 |
11410 |
lbm_value prg = args[prg_pos]; // No check that this is a program. |
|
2654 |
lbm_value app_cont; |
||
2655 |
lbm_value app_cont_prg; |
||
2656 |
lbm_value new_prg; |
||
2657 |
lbm_value prg_copy; |
||
2658 |
|||
2659 |
11410 |
int len = -1; |
|
2660 |
✗✓✗✗ |
11410 |
WITH_GC(prg_copy, lbm_list_copy(&len, prg)); |
2661 |
11410 |
lbm_stack_drop(&ctx->K, nargs+1); |
|
2662 |
// There is always a continuation (DONE). |
||
2663 |
// If ctx->program is nil, the stack should contain DONE. |
||
2664 |
// after adding an intermediate done for prg, stack becomes DONE, DONE. |
||
2665 |
11410 |
app_cont = cons_with_gc(ENC_SYM_APP_CONT, ENC_SYM_NIL, prg_copy); |
|
2666 |
11410 |
app_cont_prg = cons_with_gc(app_cont, ENC_SYM_NIL, prg_copy); |
|
2667 |
11410 |
new_prg = lbm_list_append(app_cont_prg, ctx->program); |
|
2668 |
11410 |
new_prg = lbm_list_append(prg_copy, new_prg); |
|
2669 |
// new_prg is guaranteed to be a cons cell or nil |
||
2670 |
// even if the eval-program application is syntactically broken. |
||
2671 |
11410 |
stack_reserve(ctx, 1)[0] = DONE; |
|
2672 |
11410 |
ctx->program = get_cdr(new_prg); |
|
2673 |
11410 |
ctx->curr_exp = get_car(new_prg); |
|
2674 |
} else { |
||
2675 |
lbm_set_error_reason((char*)lbm_error_str_num_args); |
||
2676 |
error_at_ctx(ENC_SYM_EERROR, ENC_SYM_EVAL_PROGRAM); |
||
2677 |
} |
||
2678 |
11410 |
} |
|
2679 |
|||
2680 |
3332 |
static void apply_send(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) { |
|
2681 |
✓✗ | 3332 |
if (nargs == 2) { |
2682 |
✓✗ | 3332 |
if (lbm_type_of(args[0]) == LBM_TYPE_I) { |
2683 |
3332 |
lbm_cid cid = (lbm_cid)lbm_dec_i(args[0]); |
|
2684 |
3332 |
lbm_value msg = args[1]; |
|
2685 |
3332 |
int r = lbm_find_receiver_and_send(cid, msg); |
|
2686 |
/* return the status */ |
||
2687 |
3332 |
lbm_stack_drop(&ctx->K, nargs+1); |
|
2688 |
✓✗ | 3332 |
ctx->r = r == 0 ? ENC_SYM_TRUE : ENC_SYM_NIL; |
2689 |
3332 |
ctx->app_cont = true; |
|
2690 |
} else { |
||
2691 |
error_at_ctx(ENC_SYM_TERROR, ENC_SYM_SEND); |
||
2692 |
} |
||
2693 |
} else { |
||
2694 |
lbm_set_error_reason((char*)lbm_error_str_num_args); |
||
2695 |
error_at_ctx(ENC_SYM_EERROR, ENC_SYM_SEND); |
||
2696 |
} |
||
2697 |
3332 |
} |
|
2698 |
|||
2699 |
static void apply_ok(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) { |
||
2700 |
lbm_value ok_val = ENC_SYM_TRUE; |
||
2701 |
if (nargs >= 1) { |
||
2702 |
ok_val = args[0]; |
||
2703 |
} |
||
2704 |
ctx->r = ok_val; |
||
2705 |
ok_ctx(); |
||
2706 |
} |
||
2707 |
|||
2708 |
28 |
static void apply_error(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) { |
|
2709 |
(void) ctx; |
||
2710 |
28 |
lbm_value err_val = ENC_SYM_EERROR; |
|
2711 |
✓✗ | 28 |
if (nargs >= 1) { |
2712 |
28 |
err_val = args[0]; |
|
2713 |
} |
||
2714 |
28 |
error_at_ctx(err_val, ENC_SYM_EXIT_ERROR); |
|
2715 |
} |
||
2716 |
|||
2717 |
// //////////////////////////////////////////////////////////// |
||
2718 |
// Map takes a function f and a list ls as arguments. |
||
2719 |
// The function f is applied to each element of ls. |
||
2720 |
// |
||
2721 |
// Normally when applying a function to an argument this happens: |
||
2722 |
// 1. the function is evaluated |
||
2723 |
// 2. the argument is evaluated |
||
2724 |
// 3. the result of evaluating the function is applied to the result of evaluating |
||
2725 |
// the argument. |
||
2726 |
// |
||
2727 |
// When doing (map f arg-list) I assume one means to apply f to each element of arg-list |
||
2728 |
// exactly as those elements are. That is, no evaluation of the argument. |
||
2729 |
// The implementation of map below makes sure that the elements of the arg-list are not |
||
2730 |
// evaluated by wrapping them each in a `quote`. |
||
2731 |
// |
||
2732 |
// Map creates a structure in memory that looks like this (f (quote dummy . nil) . nil). |
||
2733 |
// Then, for each element from arg-list (example a1 ... aN) the object |
||
2734 |
// (f (quote aM . nil) . nil) is created by substituting dummy for an element of the list. |
||
2735 |
// after this substitution the evaluator is fired up to evaluate the entire (f (quote aM . nil) . nil) |
||
2736 |
// structure resulting in an element for the result list. |
||
2737 |
// |
||
2738 |
// Here comes the fun part, if you (map quote arg-list), then the object |
||
2739 |
// (quote (quote aM . nil) . nil) is created and evaluated. Now note that quote just gives back |
||
2740 |
// exactly what you give to it when evaluated. |
||
2741 |
// So (quote (quote aM . nil) . nil) gives you as result (quote aM . nil) and now also note that |
||
2742 |
// this is a list, and a list is really just an address on the heap! |
||
2743 |
// This leads to the very fun behavior that: |
||
2744 |
// |
||
2745 |
// # (map quote '(1 2 3 4)) |
||
2746 |
// > ((quote 4) (quote 4) (quote 4) (quote 4)) |
||
2747 |
// |
||
2748 |
// A potential fix is to instead of creating the object (f (quote aM . nil) . nil) |
||
2749 |
// we create the object (f var) for some unique var and then extend the environment |
||
2750 |
// for each round of evaluation with a binding var => aM. |
||
2751 |
|||
2752 |
// (map f arg-list) |
||
2753 |
896 |
static void apply_map(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) { |
|
2754 |
✓✗✓✓ |
896 |
if (nargs == 2 && lbm_is_cons(args[1])) { |
2755 |
784 |
lbm_value *sptr = get_stack_ptr(ctx, 3); |
|
2756 |
|||
2757 |
784 |
lbm_value f = args[0]; |
|
2758 |
784 |
lbm_cons_t *args1_cell = lbm_ref_cell(args[1]); |
|
2759 |
784 |
lbm_value h = args1_cell->car; |
|
2760 |
784 |
lbm_value t = args1_cell->cdr; |
|
2761 |
|||
2762 |
lbm_value appli_1; |
||
2763 |
lbm_value appli; |
||
2764 |
✗✓✗✗ |
784 |
WITH_GC(appli_1, lbm_heap_allocate_list(2)); |
2765 |
✗✓✗✗ |
784 |
WITH_GC_RMBR_1(appli, lbm_heap_allocate_list(2), appli_1); |
2766 |
|||
2767 |
784 |
lbm_value appli_0 = get_cdr(appli_1); |
|
2768 |
|||
2769 |
784 |
lbm_set_car_and_cdr(appli_0, h, ENC_SYM_NIL); |
|
2770 |
784 |
lbm_set_car(appli_1, ENC_SYM_QUOTE); |
|
2771 |
|||
2772 |
784 |
lbm_set_car_and_cdr(get_cdr(appli), appli_1, ENC_SYM_NIL); |
|
2773 |
784 |
lbm_set_car(appli, f); |
|
2774 |
|||
2775 |
784 |
lbm_value elt = cons_with_gc(ctx->r, ENC_SYM_NIL, appli); |
|
2776 |
784 |
sptr[0] = t; // reuse stack space |
|
2777 |
784 |
sptr[1] = ctx->curr_env; |
|
2778 |
784 |
sptr[2] = elt; |
|
2779 |
784 |
lbm_value *rptr = stack_reserve(ctx,4); |
|
2780 |
784 |
rptr[0] = elt; |
|
2781 |
784 |
rptr[1] = appli; |
|
2782 |
784 |
rptr[2] = appli_0; |
|
2783 |
784 |
rptr[3] = MAP; |
|
2784 |
784 |
ctx->curr_exp = appli; |
|
2785 |
✓✗✓✗ |
112 |
} else if (nargs == 2 && lbm_is_symbol_nil(args[1])) { |
2786 |
112 |
lbm_stack_drop(&ctx->K, 3); |
|
2787 |
112 |
ctx->r = ENC_SYM_NIL; |
|
2788 |
112 |
ctx->app_cont = true; |
|
2789 |
} else { |
||
2790 |
lbm_set_error_reason((char*)lbm_error_str_num_args); |
||
2791 |
error_at_ctx(ENC_SYM_EERROR, ENC_SYM_MAP); |
||
2792 |
} |
||
2793 |
896 |
} |
|
2794 |
|||
2795 |
140 |
static void apply_reverse(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) { |
|
2796 |
✓✗✓✗ |
140 |
if (nargs == 1 && lbm_is_list(args[0])) { |
2797 |
140 |
lbm_value curr = args[0]; |
|
2798 |
|||
2799 |
140 |
lbm_value new_list = ENC_SYM_NIL; |
|
2800 |
✓✓ | 3332 |
while (lbm_is_cons(curr)) { |
2801 |
3192 |
lbm_cons_t *curr_cell = lbm_ref_cell(curr); // known cons. |
|
2802 |
3192 |
lbm_value tmp = cons_with_gc(curr_cell->car, new_list, ENC_SYM_NIL); |
|
2803 |
3192 |
new_list = tmp; |
|
2804 |
3192 |
curr = curr_cell->cdr; |
|
2805 |
} |
||
2806 |
140 |
lbm_stack_drop(&ctx->K, 2); |
|
2807 |
140 |
ctx->r = new_list; |
|
2808 |
140 |
ctx->app_cont = true; |
|
2809 |
} else { |
||
2810 |
lbm_set_error_reason("Reverse requires a list argument"); |
||
2811 |
error_at_ctx(ENC_SYM_EERROR, ENC_SYM_REVERSE); |
||
2812 |
} |
||
2813 |
140 |
} |
|
2814 |
|||
2815 |
34594 |
static void apply_flatten(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) { |
|
2816 |
✓✓ | 34594 |
if (nargs == 1) { |
2817 |
#ifdef LBM_ALWAYS_GC |
||
2818 |
gc(); |
||
2819 |
#endif |
||
2820 |
34566 |
lbm_value v = flatten_value(args[0]); |
|
2821 |
✓✓ | 34566 |
if ( v == ENC_SYM_MERROR) { |
2822 |
2 |
gc(); |
|
2823 |
2 |
v = flatten_value(args[0]); |
|
2824 |
} |
||
2825 |
|||
2826 |
✓✓ | 34566 |
if (lbm_is_symbol(v)) { |
2827 |
28 |
error_at_ctx(v, ENC_SYM_FLATTEN); |
|
2828 |
} else { |
||
2829 |
34538 |
lbm_stack_drop(&ctx->K, 2); |
|
2830 |
34538 |
ctx->r = v; |
|
2831 |
34538 |
ctx->app_cont = true; |
|
2832 |
} |
||
2833 |
34538 |
return; |
|
2834 |
} |
||
2835 |
28 |
error_at_ctx(ENC_SYM_TERROR, ENC_SYM_FLATTEN); |
|
2836 |
} |
||
2837 |
|||
2838 |
34510 |
static void apply_unflatten(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) { |
|
2839 |
lbm_array_header_t *array; |
||
2840 |
✓✗✓✗ |
34510 |
if(nargs == 1 && (array = lbm_dec_array_r(args[0]))) { |
2841 |
lbm_flat_value_t fv; |
||
2842 |
34510 |
fv.buf = (uint8_t*)array->data; |
|
2843 |
34510 |
fv.buf_size = array->size; |
|
2844 |
34510 |
fv.buf_pos = 0; |
|
2845 |
|||
2846 |
lbm_value res; |
||
2847 |
|||
2848 |
34510 |
ctx->r = ENC_SYM_NIL; |
|
2849 |
✓✗ | 34510 |
if (lbm_unflatten_value(&fv, &res)) { |
2850 |
34510 |
ctx->r = res; |
|
2851 |
} |
||
2852 |
34510 |
lbm_stack_drop(&ctx->K, 2); |
|
2853 |
34510 |
ctx->app_cont = true; |
|
2854 |
34510 |
return; |
|
2855 |
} |
||
2856 |
error_at_ctx(ENC_SYM_TERROR, ENC_SYM_UNFLATTEN); |
||
2857 |
} |
||
2858 |
|||
2859 |
84 |
static void apply_kill(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) { |
|
2860 |
✓✗✓✗ |
84 |
if (nargs == 2 && lbm_is_number(args[0])) { |
2861 |
84 |
lbm_cid cid = lbm_dec_as_i32(args[0]); |
|
2862 |
|||
2863 |
✗✓ | 84 |
if (ctx->id == cid) { |
2864 |
ctx->r = args[1]; |
||
2865 |
finish_ctx(); |
||
2866 |
return; |
||
2867 |
} |
||
2868 |
84 |
mutex_lock(&qmutex); |
|
2869 |
84 |
eval_context_t *found = NULL; |
|
2870 |
84 |
found = lookup_ctx_nm(&blocked, cid); |
|
2871 |
✗✓ | 84 |
if (found) |
2872 |
drop_ctx_nm(&blocked, found); |
||
2873 |
else |
||
2874 |
84 |
found = lookup_ctx_nm(&queue, cid); |
|
2875 |
✓✗ | 84 |
if (found) |
2876 |
84 |
drop_ctx_nm(&queue, found); |
|
2877 |
|||
2878 |
✓✗ | 84 |
if (found) { |
2879 |
84 |
found->K.data[found->K.sp - 1] = KILL; |
|
2880 |
84 |
found->r = args[1]; |
|
2881 |
84 |
found->app_cont = true; |
|
2882 |
84 |
found->state = LBM_THREAD_STATE_READY; |
|
2883 |
84 |
enqueue_ctx_nm(&queue,found); |
|
2884 |
84 |
ctx->r = ENC_SYM_TRUE; |
|
2885 |
} else { |
||
2886 |
ctx->r = ENC_SYM_NIL; |
||
2887 |
} |
||
2888 |
84 |
lbm_stack_drop(&ctx->K, 3); |
|
2889 |
84 |
ctx->app_cont = true; |
|
2890 |
84 |
mutex_unlock(&qmutex); |
|
2891 |
84 |
return; |
|
2892 |
} |
||
2893 |
error_at_ctx(ENC_SYM_TERROR, ENC_SYM_KILL); |
||
2894 |
} |
||
2895 |
|||
2896 |
282828 |
static lbm_value cmp_to_clo(lbm_value cmp) { |
|
2897 |
lbm_value closure; |
||
2898 |
✓✓✗✓ |
282828 |
WITH_GC(closure, lbm_heap_allocate_list(4)); |
2899 |
282828 |
lbm_set_car(closure, ENC_SYM_CLOSURE); |
|
2900 |
282828 |
lbm_value cl1 = lbm_cdr(closure); |
|
2901 |
lbm_value par; |
||
2902 |
✓✓✗✓ |
282828 |
WITH_GC_RMBR_1(par, lbm_heap_allocate_list_init(2, symbol_x, symbol_y), closure); |
2903 |
282828 |
lbm_set_car(cl1, par); |
|
2904 |
282828 |
lbm_value cl2 = lbm_cdr(cl1); |
|
2905 |
lbm_value body; |
||
2906 |
✓✓✗✓ |
282828 |
WITH_GC_RMBR_1(body, lbm_heap_allocate_list_init(3, cmp, symbol_x, symbol_y), closure); |
2907 |
282828 |
lbm_set_car(cl2, body); |
|
2908 |
282828 |
lbm_value cl3 = lbm_cdr(cl2); |
|
2909 |
282828 |
lbm_set_car(cl3, ENC_SYM_NIL); |
|
2910 |
282828 |
return closure; |
|
2911 |
} |
||
2912 |
|||
2913 |
// (merge comparator list1 list2) |
||
2914 |
420 |
static void apply_merge(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) { |
|
2915 |
✓✗✓✗ ✓✗ |
420 |
if (nargs == 3 && lbm_is_list(args[1]) && lbm_is_list(args[2])) { |
2916 |
|||
2917 |
✓✓ | 420 |
if (!lbm_is_closure(args[0])) { |
2918 |
28 |
args[0] = cmp_to_clo(args[0]); |
|
2919 |
} |
||
2920 |
|||
2921 |
// Copy input lists for functional behaviour at top-level |
||
2922 |
// merge itself is in-place in the copied lists. |
||
2923 |
lbm_value a; |
||
2924 |
lbm_value b; |
||
2925 |
420 |
int len_a = -1; |
|
2926 |
420 |
int len_b = -1; |
|
2927 |
✗✓✗✗ |
420 |
WITH_GC(a, lbm_list_copy(&len_a, args[1])); |
2928 |
✗✓✗✗ |
420 |
WITH_GC_RMBR_1(b, lbm_list_copy(&len_b, args[2]), a); |
2929 |
|||
2930 |
✓✓ | 420 |
if (len_a == 0) { |
2931 |
56 |
ctx->r = b; |
|
2932 |
56 |
lbm_stack_drop(&ctx->K, 4); |
|
2933 |
56 |
ctx->app_cont = true; |
|
2934 |
56 |
return; |
|
2935 |
} |
||
2936 |
✓✓ | 364 |
if (len_b == 0) { |
2937 |
56 |
ctx->r = a; |
|
2938 |
56 |
lbm_stack_drop(&ctx->K, 4); |
|
2939 |
56 |
ctx->app_cont = true; |
|
2940 |
56 |
return; |
|
2941 |
} |
||
2942 |
|||
2943 |
308 |
args[1] = a; // keep safe by replacing the original on stack. |
|
2944 |
308 |
args[2] = b; |
|
2945 |
|||
2946 |
308 |
lbm_value a_1 = a; |
|
2947 |
308 |
lbm_value a_rest = lbm_cdr(a); |
|
2948 |
308 |
lbm_value b_1 = b; |
|
2949 |
308 |
lbm_value b_rest = lbm_cdr(b); |
|
2950 |
|||
2951 |
lbm_value cl[3]; // Comparator closure |
||
2952 |
308 |
extract_n(lbm_cdr(args[0]), cl, 3); |
|
2953 |
308 |
lbm_value cmp_env = cl[CLO_ENV]; |
|
2954 |
308 |
lbm_value par1 = ENC_SYM_NIL; |
|
2955 |
308 |
lbm_value par2 = ENC_SYM_NIL; |
|
2956 |
308 |
lbm_uint len = lbm_list_length(cl[CLO_PARAMS]); |
|
2957 |
✓✗ | 308 |
if (len == 2) { |
2958 |
308 |
par1 = get_car(cl[CLO_PARAMS]); |
|
2959 |
308 |
par2 = get_cadr(cl[CLO_PARAMS]); |
|
2960 |
lbm_value new_env0; |
||
2961 |
lbm_value new_env; |
||
2962 |
✗✓✗✗ |
308 |
WITH_GC(new_env0, lbm_env_set(cmp_env, par1, lbm_car(a_1))); |
2963 |
✗✓✗✗ |
308 |
WITH_GC_RMBR_1(new_env, lbm_env_set(new_env0, par2, lbm_car(b_1)),new_env0); |
2964 |
308 |
cmp_env = new_env; |
|
2965 |
} else { |
||
2966 |
error_at_ctx(ENC_SYM_TERROR, args[0]); |
||
2967 |
} |
||
2968 |
308 |
lbm_set_cdr(a_1, b_1); |
|
2969 |
308 |
lbm_set_cdr(b_1, ENC_SYM_NIL); |
|
2970 |
308 |
lbm_value cmp = cl[CLO_BODY]; |
|
2971 |
|||
2972 |
308 |
lbm_stack_drop(&ctx->K, 4); // TODO: Optimize drop 4 alloc 10 into alloc 6 |
|
2973 |
308 |
lbm_uint *sptr = stack_reserve(ctx, 10); |
|
2974 |
308 |
sptr[0] = ENC_SYM_NIL; // head of merged list |
|
2975 |
308 |
sptr[1] = ENC_SYM_NIL; // last of merged list |
|
2976 |
308 |
sptr[2] = a_1; |
|
2977 |
308 |
sptr[3] = a_rest; |
|
2978 |
308 |
sptr[4] = b_rest; |
|
2979 |
308 |
sptr[5] = cmp; |
|
2980 |
308 |
sptr[6] = cmp_env; |
|
2981 |
308 |
sptr[7] = par1; |
|
2982 |
308 |
sptr[8] = par2; |
|
2983 |
308 |
sptr[9] = MERGE_REST; |
|
2984 |
308 |
ctx->curr_exp = cl[CLO_BODY]; |
|
2985 |
308 |
ctx->curr_env = cmp_env; |
|
2986 |
308 |
return; |
|
2987 |
} |
||
2988 |
error_at_ctx(ENC_SYM_TERROR, ENC_SYM_MERGE); |
||
2989 |
} |
||
2990 |
|||
2991 |
// (sort comparator list) |
||
2992 |
283136 |
static void apply_sort(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) { |
|
2993 |
✓✗✓✗ |
283136 |
if (nargs == 2 && lbm_is_list(args[1])) { |
2994 |
|||
2995 |
✓✓ | 283136 |
if (!lbm_is_closure(args[0])) { |
2996 |
282800 |
args[0] = cmp_to_clo(args[0]); |
|
2997 |
} |
||
2998 |
|||
2999 |
283136 |
int len = -1; |
|
3000 |
lbm_value list_copy; |
||
3001 |
✓✓✗✓ |
283136 |
WITH_GC(list_copy, lbm_list_copy(&len, args[1])); |
3002 |
✓✓ | 283136 |
if (len <= 1) { |
3003 |
28 |
lbm_stack_drop(&ctx->K, 3); |
|
3004 |
28 |
ctx->r = list_copy; |
|
3005 |
28 |
ctx->app_cont = true; |
|
3006 |
28 |
return; |
|
3007 |
} |
||
3008 |
|||
3009 |
283108 |
args[1] = list_copy; // Keep safe, original replaced on stack. |
|
3010 |
|||
3011 |
// Take the headmost 2, 1-element sublists. |
||
3012 |
283108 |
lbm_value a = list_copy; |
|
3013 |
283108 |
lbm_value b = lbm_cdr(a); |
|
3014 |
283108 |
lbm_value rest = lbm_cdr(b); |
|
3015 |
// Do not terminate b. keep rest of list safe from GC in the following |
||
3016 |
// closure extraction. |
||
3017 |
//lbm_set_cdr(a, b); // This is void |
||
3018 |
|||
3019 |
lbm_value cl[3]; // Comparator closure |
||
3020 |
283108 |
extract_n(lbm_cdr(args[0]), cl, 3); |
|
3021 |
283108 |
lbm_value cmp_env = cl[CLO_ENV]; |
|
3022 |
283108 |
lbm_value par1 = ENC_SYM_NIL; |
|
3023 |
283108 |
lbm_value par2 = ENC_SYM_NIL; |
|
3024 |
283108 |
lbm_uint cl_len = lbm_list_length(cl[CLO_PARAMS]); |
|
3025 |
✓✗ | 283108 |
if (cl_len == 2) { |
3026 |
283108 |
par1 = get_car(cl[CLO_PARAMS]); |
|
3027 |
283108 |
par2 = get_cadr(cl[CLO_PARAMS]); |
|
3028 |
lbm_value new_env0; |
||
3029 |
lbm_value new_env; |
||
3030 |
✓✓✗✓ |
283108 |
WITH_GC(new_env0, lbm_env_set(cmp_env, par1, lbm_car(a))); |
3031 |
✓✓✗✓ |
283108 |
WITH_GC_RMBR_1(new_env, lbm_env_set(new_env0, par2, lbm_car(b)), new_env0); |
3032 |
283108 |
cmp_env = new_env; |
|
3033 |
} else { |
||
3034 |
error_at_ctx(ENC_SYM_TERROR, args[0]); |
||
3035 |
} |
||
3036 |
283108 |
lbm_value cmp = cl[CLO_BODY]; |
|
3037 |
|||
3038 |
// Terminate the comparator argument list. |
||
3039 |
283108 |
lbm_set_cdr(b, ENC_SYM_NIL); |
|
3040 |
|||
3041 |
283108 |
lbm_stack_drop(&ctx->K, 3); //TODO: optimize drop 3, alloc 20 into alloc 17 |
|
3042 |
283108 |
lbm_uint *sptr = stack_reserve(ctx, 20); |
|
3043 |
283108 |
sptr[0] = cmp; |
|
3044 |
283108 |
sptr[1] = cmp_env; |
|
3045 |
283108 |
sptr[2] = par1; |
|
3046 |
283108 |
sptr[3] = par2; |
|
3047 |
283108 |
sptr[4] = ENC_SYM_NIL; // head of merged accumulation of sublists |
|
3048 |
283108 |
sptr[5] = ENC_SYM_NIL; // last of merged accumulation of sublists |
|
3049 |
283108 |
sptr[6] = rest; |
|
3050 |
283108 |
sptr[7] = lbm_enc_i(1); |
|
3051 |
283108 |
sptr[8] = lbm_enc_i(len); //TODO: 28 bit i vs 32 bit i |
|
3052 |
283108 |
sptr[9] = MERGE_LAYER; |
|
3053 |
283108 |
sptr[10] = ENC_SYM_NIL; // head of merged sublist |
|
3054 |
283108 |
sptr[11] = ENC_SYM_NIL; // last of merged sublist |
|
3055 |
283108 |
sptr[12] = a; |
|
3056 |
283108 |
sptr[13] = ENC_SYM_NIL; // no a_rest, 1 element lists in layer 1. |
|
3057 |
283108 |
sptr[14] = ENC_SYM_NIL; // no b_rest, 1 element lists in layer 1. |
|
3058 |
283108 |
sptr[15] = cmp; |
|
3059 |
283108 |
sptr[16] = cmp_env; |
|
3060 |
283108 |
sptr[17] = par1; |
|
3061 |
283108 |
sptr[18] = par2; |
|
3062 |
283108 |
sptr[19] = MERGE_REST; |
|
3063 |
283108 |
ctx->curr_exp = cmp; |
|
3064 |
283108 |
ctx->curr_env = cmp_env; |
|
3065 |
283108 |
return; |
|
3066 |
} |
||
3067 |
error_ctx(ENC_SYM_TERROR); |
||
3068 |
} |
||
3069 |
|||
3070 |
616308 |
static void apply_rest_args(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) { |
|
3071 |
lbm_value res; |
||
3072 |
✓✓ | 616308 |
if (lbm_env_lookup_b(&res, ENC_SYM_REST_ARGS, ctx->curr_env)) { |
3073 |
✓✓✓✗ |
616280 |
if (nargs == 1 && lbm_is_number(args[0])) { |
3074 |
56140 |
int32_t ix = lbm_dec_as_i32(args[0]); |
|
3075 |
56140 |
res = lbm_index_list(res, ix); |
|
3076 |
} |
||
3077 |
616280 |
ctx->r = res; |
|
3078 |
} else { |
||
3079 |
28 |
ctx->r = ENC_SYM_NIL; |
|
3080 |
} |
||
3081 |
616308 |
lbm_stack_drop(&ctx->K, nargs+1); |
|
3082 |
616308 |
ctx->app_cont = true; |
|
3083 |
616308 |
} |
|
3084 |
|||
3085 |
/* (rotate list-expr dist/dir-expr) */ |
||
3086 |
84 |
static void apply_rotate(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) { |
|
3087 |
✓✗✓✗ ✓✗ |
84 |
if (nargs == 2 && lbm_is_list(args[0]) && lbm_is_number(args[1])) { |
3088 |
84 |
int len = -1; |
|
3089 |
lbm_value ls; |
||
3090 |
✗✓✗✗ |
84 |
WITH_GC(ls, lbm_list_copy(&len, args[0])); |
3091 |
84 |
int dist = lbm_dec_as_i32(args[1]); |
|
3092 |
✓✓✓✗ |
84 |
if (len > 0 && dist != 0) { |
3093 |
56 |
int d = dist; |
|
3094 |
✓✓ | 56 |
if (dist > 0) { |
3095 |
28 |
ls = lbm_list_destructive_reverse(ls); |
|
3096 |
} else { |
||
3097 |
28 |
d = -dist; |
|
3098 |
} |
||
3099 |
|||
3100 |
56 |
lbm_value start = ls; |
|
3101 |
56 |
lbm_value end = ENC_SYM_NIL; |
|
3102 |
56 |
lbm_value curr = start; |
|
3103 |
✓✓ | 308 |
while (lbm_is_cons(curr)) { |
3104 |
252 |
end = curr; |
|
3105 |
252 |
curr = get_cdr(curr); |
|
3106 |
} |
||
3107 |
|||
3108 |
✓✓ | 168 |
for (int i = 0; i < d; i ++) { |
3109 |
112 |
lbm_value a = start; |
|
3110 |
112 |
start = lbm_cdr(start); |
|
3111 |
112 |
lbm_set_cdr(a, ENC_SYM_NIL); |
|
3112 |
112 |
lbm_set_cdr(end, a); |
|
3113 |
112 |
end = a; |
|
3114 |
} |
||
3115 |
56 |
ls = start; |
|
3116 |
✓✓ | 56 |
if (dist > 0) { |
3117 |
28 |
ls = lbm_list_destructive_reverse(ls); |
|
3118 |
} |
||
3119 |
} |
||
3120 |
84 |
lbm_stack_drop(&ctx->K, nargs+1); |
|
3121 |
84 |
ctx->app_cont = true; |
|
3122 |
84 |
ctx->r = ls; |
|
3123 |
84 |
return; |
|
3124 |
} |
||
3125 |
error_ctx(ENC_SYM_EERROR); |
||
3126 |
} |
||
3127 |
|||
3128 |
/***************************************************/ |
||
3129 |
/* Application lookup table */ |
||
3130 |
|||
3131 |
typedef void (*apply_fun)(lbm_value *, lbm_uint, eval_context_t *); |
||
3132 |
static const apply_fun fun_table[] = |
||
3133 |
{ |
||
3134 |
apply_setvar, |
||
3135 |
apply_read, |
||
3136 |
apply_read_program, |
||
3137 |
apply_read_eval_program, |
||
3138 |
apply_spawn, |
||
3139 |
apply_spawn_trap, |
||
3140 |
apply_yield, |
||
3141 |
apply_wait, |
||
3142 |
apply_eval, |
||
3143 |
apply_eval_program, |
||
3144 |
apply_send, |
||
3145 |
apply_ok, |
||
3146 |
apply_error, |
||
3147 |
apply_map, |
||
3148 |
apply_reverse, |
||
3149 |
apply_flatten, |
||
3150 |
apply_unflatten, |
||
3151 |
apply_kill, |
||
3152 |
apply_sleep, |
||
3153 |
apply_merge, |
||
3154 |
apply_sort, |
||
3155 |
apply_rest_args, |
||
3156 |
apply_rotate, |
||
3157 |
}; |
||
3158 |
|||
3159 |
/***************************************************/ |
||
3160 |
/* Application of function that takes arguments */ |
||
3161 |
/* passed over the stack. */ |
||
3162 |
|||
3163 |
77990317 |
static void application(eval_context_t *ctx, lbm_value *fun_args, lbm_uint arg_count) { |
|
3164 |
/* If arriving here, we know that the fun is a symbol. |
||
3165 |
* and can be a built in operation or an extension. |
||
3166 |
*/ |
||
3167 |
77990317 |
lbm_value fun = fun_args[0]; |
|
3168 |
|||
3169 |
77990317 |
lbm_uint fun_val = lbm_dec_sym(fun); |
|
3170 |
77990317 |
lbm_uint fun_kind = SYMBOL_KIND(fun_val); |
|
3171 |
|||
3172 |
✓✓✓✗ |
77990317 |
switch (fun_kind) { |
3173 |
184956 |
case SYMBOL_KIND_EXTENSION: { |
|
3174 |
184956 |
extension_fptr f = extension_table[SYMBOL_IX(fun_val)].fptr; |
|
3175 |
|||
3176 |
lbm_value ext_res; |
||
3177 |
✓✓✗✓ |
184956 |
WITH_GC(ext_res, f(&fun_args[1], arg_count)); |
3178 |
✓✓ | 184956 |
if (lbm_is_error(ext_res)) { //Error other than merror |
3179 |
2828 |
error_at_ctx(ext_res, fun); |
|
3180 |
} |
||
3181 |
182128 |
lbm_stack_drop(&ctx->K, arg_count + 1); |
|
3182 |
|||
3183 |
182128 |
ctx->app_cont = true; |
|
3184 |
182128 |
ctx->r = ext_res; |
|
3185 |
|||
3186 |
✓✓ | 182128 |
if (blocking_extension) { |
3187 |
✗✓ | 112 |
if (is_atomic) { |
3188 |
// Check atomic_error explicitly so that the mutex |
||
3189 |
// can be released if there is an error. |
||
3190 |
blocking_extension = false; |
||
3191 |
mutex_unlock(&blocking_extension_mutex); |
||
3192 |
atomic_error(); |
||
3193 |
} |
||
3194 |
112 |
blocking_extension = false; |
|
3195 |
✗✓ | 112 |
if (blocking_extension_timeout) { |
3196 |
blocking_extension_timeout = false; |
||
3197 |
block_current_ctx(LBM_THREAD_STATE_TIMEOUT, blocking_extension_timeout_us,true); |
||
3198 |
} else { |
||
3199 |
112 |
block_current_ctx(LBM_THREAD_STATE_BLOCKED, 0,true); |
|
3200 |
} |
||
3201 |
112 |
mutex_unlock(&blocking_extension_mutex); |
|
3202 |
} |
||
3203 |
182128 |
} break; |
|
3204 |
73276474 |
case SYMBOL_KIND_FUNDAMENTAL: |
|
3205 |
73276474 |
call_fundamental(SYMBOL_IX(fun_val), &fun_args[1], arg_count, ctx); |
|
3206 |
73271818 |
break; |
|
3207 |
4528887 |
case SYMBOL_KIND_APPFUN: |
|
3208 |
4528887 |
fun_table[SYMBOL_IX(fun_val)](&fun_args[1], arg_count, ctx); |
|
3209 |
4528495 |
break; |
|
3210 |
default: |
||
3211 |
// Symbols that are "special" but not in the way caught above |
||
3212 |
// ends up here. |
||
3213 |
lbm_set_error_reason("Symbol does not represent a function"); |
||
3214 |
error_at_ctx(ENC_SYM_EERROR,fun_args[0]); |
||
3215 |
break; |
||
3216 |
} |
||
3217 |
77982441 |
} |
|
3218 |
|||
3219 |
59418902 |
static void cont_closure_application_args(eval_context_t *ctx) { |
|
3220 |
59418902 |
lbm_uint* sptr = get_stack_ptr(ctx, 5); |
|
3221 |
|||
3222 |
59418902 |
lbm_value arg_env = (lbm_value)sptr[0]; |
|
3223 |
59418902 |
lbm_value exp = (lbm_value)sptr[1]; |
|
3224 |
59418902 |
lbm_value clo_env = (lbm_value)sptr[2]; |
|
3225 |
59418902 |
lbm_value params = (lbm_value)sptr[3]; |
|
3226 |
59418902 |
lbm_value args = (lbm_value)sptr[4]; |
|
3227 |
|||
3228 |
lbm_value car_params, cdr_params; |
||
3229 |
59418902 |
get_car_and_cdr(params, &car_params, &cdr_params); |
|
3230 |
|||
3231 |
59418902 |
bool a_nil = lbm_is_symbol_nil(args); |
|
3232 |
59418902 |
bool p_nil = lbm_is_symbol_nil(cdr_params); |
|
3233 |
|||
3234 |
59418902 |
lbm_value binder = allocate_binding(car_params, ctx->r, clo_env); |
|
3235 |
|||
3236 |
✓✓✓✓ |
59418874 |
if (!a_nil && !p_nil) { |
3237 |
lbm_value car_args, cdr_args; |
||
3238 |
33207244 |
get_car_and_cdr(args, &car_args, &cdr_args); |
|
3239 |
33207244 |
sptr[2] = binder; |
|
3240 |
33207244 |
sptr[3] = cdr_params; |
|
3241 |
33207244 |
sptr[4] = cdr_args; |
|
3242 |
33207244 |
stack_reserve(ctx,1)[0] = CLOSURE_ARGS; |
|
3243 |
33207244 |
ctx->curr_exp = car_args; |
|
3244 |
33207244 |
ctx->curr_env = arg_env; |
|
3245 |
✓✓✓✗ |
26211630 |
} else if (a_nil && p_nil) { |
3246 |
// Arguments and parameters match up in number |
||
3247 |
26183406 |
lbm_stack_drop(&ctx->K, 5); |
|
3248 |
26183406 |
ctx->curr_env = binder; |
|
3249 |
26183406 |
ctx->curr_exp = exp; |
|
3250 |
✓✗ | 28224 |
} else if (p_nil) { |
3251 |
28224 |
lbm_value rest_binder = allocate_binding(ENC_SYM_REST_ARGS, ENC_SYM_NIL, binder); |
|
3252 |
28224 |
sptr[2] = rest_binder; |
|
3253 |
28224 |
sptr[3] = get_cdr(args); |
|
3254 |
28224 |
sptr[4] = get_car(rest_binder); // last element of rest_args so far |
|
3255 |
28224 |
stack_reserve(ctx,1)[0] = CLOSURE_ARGS_REST; |
|
3256 |
28224 |
ctx->curr_exp = get_car(args); |
|
3257 |
28224 |
ctx->curr_env = arg_env; |
|
3258 |
} else { |
||
3259 |
lbm_set_error_reason((char*)lbm_error_str_num_args); |
||
3260 |
error_ctx(ENC_SYM_EERROR); |
||
3261 |
} |
||
3262 |
59418874 |
} |
|
3263 |
|||
3264 |
|||
3265 |
5797008 |
static void cont_closure_args_rest(eval_context_t *ctx) { |
|
3266 |
5797008 |
lbm_uint* sptr = get_stack_ptr(ctx, 5); |
|
3267 |
5797008 |
lbm_value arg_env = (lbm_value)sptr[0]; |
|
3268 |
5797008 |
lbm_value exp = (lbm_value)sptr[1]; |
|
3269 |
5797008 |
lbm_value clo_env = (lbm_value)sptr[2]; |
|
3270 |
5797008 |
lbm_value args = (lbm_value)sptr[3]; |
|
3271 |
5797008 |
lbm_value last = (lbm_value)sptr[4]; |
|
3272 |
5797008 |
lbm_cons_t* heap = lbm_heap_state.heap; |
|
3273 |
#ifdef LBM_ALWAYS_GC |
||
3274 |
gc(); |
||
3275 |
#endif |
||
3276 |
5797008 |
lbm_value binding = lbm_heap_state.freelist; |
|
3277 |
✓✓ | 5797008 |
if (binding == ENC_SYM_NIL) { |
3278 |
7498 |
gc(); |
|
3279 |
7498 |
binding = lbm_heap_state.freelist; |
|
3280 |
✗✓ | 7498 |
if (binding == ENC_SYM_NIL) error_ctx(ENC_SYM_MERROR); |
3281 |
} |
||
3282 |
5797008 |
lbm_uint binding_ix = lbm_dec_ptr(binding); |
|
3283 |
5797008 |
lbm_heap_state.freelist = heap[binding_ix].cdr; |
|
3284 |
5797008 |
lbm_heap_state.num_alloc += 1; |
|
3285 |
5797008 |
heap[binding_ix].car = ctx->r; |
|
3286 |
5797008 |
heap[binding_ix].cdr = ENC_SYM_NIL; |
|
3287 |
|||
3288 |
|||
3289 |
5797008 |
lbm_set_cdr(last, binding); |
|
3290 |
5797008 |
sptr[4] = binding; |
|
3291 |
|||
3292 |
✓✓ | 5797008 |
if (args == ENC_SYM_NIL) { |
3293 |
588252 |
lbm_stack_drop(&ctx->K, 5); |
|
3294 |
588252 |
ctx->curr_env = clo_env; |
|
3295 |
588252 |
ctx->curr_exp = exp; |
|
3296 |
} else { |
||
3297 |
5208756 |
stack_reserve(ctx,1)[0] = CLOSURE_ARGS_REST; |
|
3298 |
5208756 |
sptr[3] = get_cdr(args); |
|
3299 |
5208756 |
ctx->curr_exp = get_car(args); |
|
3300 |
5208756 |
ctx->curr_env = arg_env; |
|
3301 |
} |
||
3302 |
5797008 |
} |
|
3303 |
|||
3304 |
247668184 |
static void cont_application_args(eval_context_t *ctx) { |
|
3305 |
247668184 |
lbm_uint *sptr = get_stack_ptr(ctx, 3); |
|
3306 |
|||
3307 |
247668184 |
lbm_value env = sptr[0]; |
|
3308 |
247668184 |
lbm_value rest = sptr[1]; |
|
3309 |
247668184 |
lbm_value count = sptr[2]; |
|
3310 |
|||
3311 |
247668184 |
ctx->curr_env = env; |
|
3312 |
247668184 |
sptr[0] = ctx->r; // Function 1st then Arguments |
|
3313 |
✓✓ | 247668184 |
if (lbm_is_cons(rest)) { |
3314 |
169677867 |
lbm_cons_t *cell = lbm_ref_cell(rest); |
|
3315 |
169677867 |
sptr[1] = env; |
|
3316 |
169677867 |
sptr[2] = cell->cdr; |
|
3317 |
169677867 |
lbm_value *rptr = stack_reserve(ctx,2); |
|
3318 |
169677867 |
rptr[0] = count + (1 << LBM_VAL_SHIFT); |
|
3319 |
169677867 |
rptr[1] = APPLICATION_ARGS; |
|
3320 |
169677867 |
ctx->curr_exp = cell->car; |
|
3321 |
} else { |
||
3322 |
// No more arguments |
||
3323 |
77990317 |
lbm_stack_drop(&ctx->K, 2); |
|
3324 |
77990317 |
lbm_uint nargs = lbm_dec_u(count); |
|
3325 |
77990317 |
lbm_value *args = get_stack_ptr(ctx, (uint32_t)(nargs + 1)); |
|
3326 |
77990317 |
application(ctx,args, nargs); |
|
3327 |
} |
||
3328 |
247660308 |
} |
|
3329 |
|||
3330 |
3985688 |
static void cont_and(eval_context_t *ctx) { |
|
3331 |
lbm_value env; |
||
3332 |
lbm_value rest; |
||
3333 |
3985688 |
lbm_value arg = ctx->r; |
|
3334 |
3985688 |
lbm_pop_2(&ctx->K, &rest, &env); |
|
3335 |
✓✓ | 3985688 |
if (lbm_is_symbol_nil(arg)) { |
3336 |
280056 |
ctx->app_cont = true; |
|
3337 |
280056 |
ctx->r = ENC_SYM_NIL; |
|
3338 |
✓✓ | 3705632 |
} else if (lbm_is_symbol_nil(rest)) { |
3339 |
1701924 |
ctx->app_cont = true; |
|
3340 |
} else { |
||
3341 |
2003708 |
lbm_value *sptr = stack_reserve(ctx, 3); |
|
3342 |
2003708 |
sptr[0] = env; |
|
3343 |
2003708 |
sptr[1] = get_cdr(rest); |
|
3344 |
2003708 |
sptr[2] = AND; |
|
3345 |
2003708 |
ctx->curr_env = env; |
|
3346 |
2003708 |
ctx->curr_exp = get_car(rest); |
|
3347 |
} |
||
3348 |
3985688 |
} |
|
3349 |
|||
3350 |
15988 |
static void cont_or(eval_context_t *ctx) { |
|
3351 |
lbm_value env; |
||
3352 |
lbm_value rest; |
||
3353 |
15988 |
lbm_value arg = ctx->r; |
|
3354 |
15988 |
lbm_pop_2(&ctx->K, &rest, &env); |
|
3355 |
✓✓ | 15988 |
if (!lbm_is_symbol_nil(arg)) { |
3356 |
840 |
ctx->app_cont = true; |
|
3357 |
✓✓ | 15148 |
} else if (lbm_is_symbol_nil(rest)) { |
3358 |
6356 |
ctx->app_cont = true; |
|
3359 |
6356 |
ctx->r = ENC_SYM_NIL; |
|
3360 |
} else { |
||
3361 |
8792 |
lbm_value *sptr = stack_reserve(ctx, 3); |
|
3362 |
8792 |
sptr[0] = env; |
|
3363 |
8792 |
sptr[1] = get_cdr(rest); |
|
3364 |
8792 |
sptr[2] = OR; |
|
3365 |
8792 |
ctx->curr_exp = get_car(rest); |
|
3366 |
8792 |
ctx->curr_env = env; |
|
3367 |
} |
||
3368 |
15988 |
} |
|
3369 |
|||
3370 |
40888554 |
static int fill_binding_location(lbm_value key, lbm_value value, lbm_value env) { |
|
3371 |
✓✓ | 40888554 |
if (lbm_type_of(key) == LBM_TYPE_SYMBOL) { |
3372 |
✓✓ | 26887518 |
if (key == ENC_SYM_DONTCARE) return FB_OK; |
3373 |
24087406 |
lbm_env_modify_binding(env,key,value); |
|
3374 |
24087406 |
return FB_OK; |
|
3375 |
✓✗✓✗ |
28002072 |
} else if (lbm_is_cons(key) && |
3376 |
14001036 |
lbm_is_cons(value)) { |
|
3377 |
14001036 |
int r = fill_binding_location(get_car(key), get_car(value), env); |
|
3378 |
✓✗ | 14001036 |
if (r == FB_OK) { |
3379 |
14001036 |
r = fill_binding_location(get_cdr(key), get_cdr(value), env); |
|
3380 |
} |
||
3381 |
14001036 |
return r; |
|
3382 |
} |
||
3383 |
return FB_TYPE_ERROR; |
||
3384 |
} |
||
3385 |
|||
3386 |
12242748 |
static void cont_bind_to_key_rest(eval_context_t *ctx) { |
|
3387 |
|||
3388 |
12242748 |
lbm_value *sptr = get_stack_ptr(ctx, 4); |
|
3389 |
|||
3390 |
12242748 |
lbm_value rest = sptr[1]; |
|
3391 |
12242748 |
lbm_value env = sptr[2]; |
|
3392 |
12242748 |
lbm_value key = sptr[3]; |
|
3393 |
|||
3394 |
✗✓ | 12242748 |
if (fill_binding_location(key, ctx->r, env) < 0) { |
3395 |
lbm_set_error_reason("Incorrect type of name/key in let-binding"); |
||
3396 |
error_at_ctx(ENC_SYM_TERROR, key); |
||
3397 |
} |
||
3398 |
|||
3399 |
✓✓ | 12242748 |
if (lbm_is_cons(rest)) { |
3400 |
113904 |
lbm_value car_rest = get_car(rest); |
|
3401 |
lbm_value key_val[2]; |
||
3402 |
113904 |
extract_n(car_rest, key_val, 2); |
|
3403 |
|||
3404 |
113904 |
sptr[1] = get_cdr(rest); |
|
3405 |
113904 |
sptr[3] = key_val[0]; |
|
3406 |
113904 |
stack_reserve(ctx,1)[0] = BIND_TO_KEY_REST; |
|
3407 |
113904 |
ctx->curr_exp = key_val[1]; |
|
3408 |
113904 |
ctx->curr_env = env; |
|
3409 |
} else { |
||
3410 |
// Otherwise evaluate the expression in the populated env |
||
3411 |
12128844 |
ctx->curr_exp = sptr[0]; |
|
3412 |
12128844 |
ctx->curr_env = env; |
|
3413 |
12128844 |
lbm_stack_drop(&ctx->K, 4); |
|
3414 |
} |
||
3415 |
12242748 |
} |
|
3416 |
|||
3417 |
21762887 |
static void cont_if(eval_context_t *ctx) { |
|
3418 |
|||
3419 |
21762887 |
lbm_value arg = ctx->r; |
|
3420 |
|||
3421 |
21762887 |
lbm_value *sptr = pop_stack_ptr(ctx, 2); |
|
3422 |
|||
3423 |
21762887 |
ctx->curr_env = sptr[1]; |
|
3424 |
✓✓ | 21762887 |
if (lbm_is_symbol_nil(arg)) { |
3425 |
21739560 |
ctx->curr_exp = get_cadr(sptr[0]); // else branch |
|
3426 |
} else { |
||
3427 |
23327 |
ctx->curr_exp = get_car(sptr[0]); // then branch |
|
3428 |
} |
||
3429 |
21762887 |
} |
|
3430 |
|||
3431 |
5460 |
static void cont_match(eval_context_t *ctx) { |
|
3432 |
5460 |
lbm_value e = ctx->r; |
|
3433 |
5460 |
bool do_gc = false; |
|
3434 |
|||
3435 |
5460 |
lbm_uint *sptr = get_stack_ptr(ctx, 2); |
|
3436 |
5460 |
lbm_value patterns = (lbm_value)sptr[0]; |
|
3437 |
5460 |
lbm_value orig_env = (lbm_value)sptr[1]; // restore enclosing environment. |
|
3438 |
5460 |
lbm_value new_env = orig_env; |
|
3439 |
|||
3440 |
✗✓ | 5460 |
if (lbm_is_symbol_nil(patterns)) { |
3441 |
// no more patterns |
||
3442 |
lbm_stack_drop(&ctx->K, 2); |
||
3443 |
ctx->r = ENC_SYM_NO_MATCH; |
||
3444 |
ctx->app_cont = true; |
||
3445 |
✓✗ | 5460 |
} else if (lbm_is_cons(patterns)) { |
3446 |
5460 |
lbm_value match_case = get_car(patterns); |
|
3447 |
5460 |
lbm_value pattern = get_car(match_case); |
|
3448 |
5460 |
lbm_value n1 = get_cadr(match_case); |
|
3449 |
5460 |
lbm_value n2 = get_cadr(get_cdr(match_case)); |
|
3450 |
lbm_value body; |
||
3451 |
5460 |
bool check_guard = false; |
|
3452 |
✓✓ | 5460 |
if (lbm_is_symbol_nil(n2)) { // TODO: Not a very robust check. |
3453 |
4200 |
body = n1; |
|
3454 |
} else { |
||
3455 |
1260 |
body = n2; |
|
3456 |
1260 |
check_guard = true; |
|
3457 |
} |
||
3458 |
#ifdef LBM_ALWAYS_GC |
||
3459 |
gc(); |
||
3460 |
#endif |
||
3461 |
5460 |
bool is_match = match(pattern, e, &new_env, &do_gc); |
|
3462 |
✗✓ | 5460 |
if (do_gc) { |
3463 |
gc(); |
||
3464 |
do_gc = false; |
||
3465 |
new_env = orig_env; |
||
3466 |
is_match = match(pattern, e, &new_env, &do_gc); |
||
3467 |
if (do_gc) { |
||
3468 |
error_ctx(ENC_SYM_MERROR); |
||
3469 |
} |
||
3470 |
} |
||
3471 |
✓✓ | 5460 |
if (is_match) { |
3472 |
✓✓ | 3304 |
if (check_guard) { |
3473 |
1260 |
lbm_value *rptr = stack_reserve(ctx,5); |
|
3474 |
1260 |
sptr[0] = get_cdr(patterns); |
|
3475 |
1260 |
sptr[1] = ctx->curr_env; |
|
3476 |
1260 |
rptr[0] = MATCH; |
|
3477 |
1260 |
rptr[1] = new_env; |
|
3478 |
1260 |
rptr[2] = body; |
|
3479 |
1260 |
rptr[3] = e; |
|
3480 |
1260 |
rptr[4] = MATCH_GUARD; |
|
3481 |
1260 |
ctx->curr_env = new_env; |
|
3482 |
1260 |
ctx->curr_exp = n1; // The guard |
|
3483 |
} else { |
||
3484 |
2044 |
lbm_stack_drop(&ctx->K, 2); |
|
3485 |
2044 |
ctx->curr_env = new_env; |
|
3486 |
2044 |
ctx->curr_exp = body; |
|
3487 |
} |
||
3488 |
} else { |
||
3489 |
// set up for checking of next pattern |
||
3490 |
2156 |
sptr[0] = get_cdr(patterns); |
|
3491 |
2156 |
sptr[1] = orig_env; |
|
3492 |
2156 |
stack_reserve(ctx,1)[0] = MATCH; |
|
3493 |
// leave r unaltered |
||
3494 |
2156 |
ctx->app_cont = true; |
|
3495 |
} |
||
3496 |
} else { |
||
3497 |
error_at_ctx(ENC_SYM_TERROR, ENC_SYM_MATCH); |
||
3498 |
} |
||
3499 |
5460 |
} |
|
3500 |
|||
3501 |
224 |
static void cont_exit_atomic(eval_context_t *ctx) { |
|
3502 |
224 |
is_atomic = false; // atomic blocks cannot nest! |
|
3503 |
224 |
ctx->app_cont = true; |
|
3504 |
224 |
} |
|
3505 |
|||
3506 |
// cont_map: |
||
3507 |
// |
||
3508 |
// sptr[0]: s[sp-6] = Rest of the input list. |
||
3509 |
// sptr[1]: s[sp-5] = Environment to restore for the eval of each application. |
||
3510 |
// sptr[2]: s[sp-4] = Result list. |
||
3511 |
// sptr[3]: s[sp-3] = Cell that goes into result list after being populated with application result. |
||
3512 |
// sptr[4]: s[sp-2] = Ref to application. |
||
3513 |
// sptr[5]: s[sp-1] = Ref to application argument. |
||
3514 |
// |
||
3515 |
// ctx->r = eval result of previous application. |
||
3516 |
2296 |
static void cont_map(eval_context_t *ctx) { |
|
3517 |
2296 |
lbm_value *sptr = get_stack_ptr(ctx, 6); |
|
3518 |
2296 |
lbm_value ls = sptr[0]; |
|
3519 |
2296 |
lbm_value env = sptr[1]; |
|
3520 |
2296 |
lbm_value t = sptr[3]; |
|
3521 |
2296 |
lbm_set_car(t, ctx->r); // update car field tailmost position. |
|
3522 |
✓✓ | 2296 |
if (lbm_is_cons(ls)) { |
3523 |
1512 |
lbm_cons_t *cell = lbm_ref_cell(ls); // already checked that cons. |
|
3524 |
1512 |
lbm_value next = cell->car; |
|
3525 |
1512 |
lbm_value rest = cell->cdr; |
|
3526 |
1512 |
sptr[0] = rest; |
|
3527 |
1512 |
stack_reserve(ctx,1)[0] = MAP; |
|
3528 |
1512 |
lbm_set_car(sptr[5], next); // new arguments |
|
3529 |
|||
3530 |
1512 |
lbm_value elt = cons_with_gc(ENC_SYM_NIL, ENC_SYM_NIL, ENC_SYM_NIL); |
|
3531 |
1512 |
lbm_set_cdr(t, elt); |
|
3532 |
1512 |
sptr[3] = elt; // (r1 ... rN . (nil . nil)) |
|
3533 |
1512 |
ctx->curr_exp = sptr[4]; |
|
3534 |
1512 |
ctx->curr_env = env; |
|
3535 |
} else { |
||
3536 |
784 |
ctx->r = sptr[2]; //head of result list |
|
3537 |
784 |
ctx->curr_env = env; |
|
3538 |
784 |
lbm_stack_drop(&ctx->K, 6); |
|
3539 |
784 |
ctx->app_cont = true; |
|
3540 |
} |
||
3541 |
2296 |
} |
|
3542 |
|||
3543 |
1260 |
static void cont_match_guard(eval_context_t *ctx) { |
|
3544 |
✓✓ | 1260 |
if (lbm_is_symbol_nil(ctx->r)) { |
3545 |
lbm_value e; |
||
3546 |
476 |
lbm_pop(&ctx->K, &e); |
|
3547 |
476 |
lbm_stack_drop(&ctx->K, 2); |
|
3548 |
476 |
ctx->r = e; |
|
3549 |
476 |
ctx->app_cont = true; |
|
3550 |
} else { |
||
3551 |
lbm_value body; |
||
3552 |
lbm_value env; |
||
3553 |
784 |
lbm_stack_drop(&ctx->K, 1); |
|
3554 |
784 |
lbm_pop_2(&ctx->K, &body, &env); |
|
3555 |
784 |
lbm_stack_drop(&ctx->K, 3); |
|
3556 |
784 |
ctx->curr_env = env; |
|
3557 |
784 |
ctx->curr_exp = body; |
|
3558 |
} |
||
3559 |
1260 |
} |
|
3560 |
|||
3561 |
28 |
static void cont_terminate(eval_context_t *ctx) { |
|
3562 |
28 |
error_ctx(ctx->r); |
|
3563 |
} |
||
3564 |
|||
3565 |
925148 |
static void cont_loop(eval_context_t *ctx) { |
|
3566 |
925148 |
lbm_value *sptr = get_stack_ptr(ctx, 2); |
|
3567 |
925148 |
stack_reserve(ctx,1)[0] = LOOP_CONDITION; |
|
3568 |
925148 |
ctx->curr_exp = sptr[1]; |
|
3569 |
925148 |
} |
|
3570 |
|||
3571 |
925428 |
static void cont_loop_condition(eval_context_t *ctx) { |
|
3572 |
✓✓ | 925428 |
if (lbm_is_symbol_nil(ctx->r)) { |
3573 |
280 |
lbm_stack_drop(&ctx->K, 2); |
|
3574 |
280 |
ctx->app_cont = true; // A loop returns nil? Makes sense to me... but in general? |
|
3575 |
280 |
return; |
|
3576 |
} |
||
3577 |
925148 |
lbm_value *sptr = get_stack_ptr(ctx, 2); |
|
3578 |
925148 |
stack_reserve(ctx,1)[0] = LOOP; |
|
3579 |
925148 |
ctx->curr_exp = sptr[0]; |
|
3580 |
} |
||
3581 |
|||
3582 |
8791580 |
static void cont_merge_rest(eval_context_t *ctx) { |
|
3583 |
8791580 |
lbm_uint *sptr = get_stack_ptr(ctx, 9); |
|
3584 |
|||
3585 |
// If comparator returns true (result is in ctx->r): |
||
3586 |
// "a" should be moved to the last element position in merged list. |
||
3587 |
// A new element from "a_rest" should be moved into comparator argument 1 pos. |
||
3588 |
// else |
||
3589 |
// "b" should be moved to last element position in merged list. |
||
3590 |
// A new element from "b_rest" should be moved into comparator argument 2 pos. |
||
3591 |
// |
||
3592 |
// If a_rest or b_rest is NIL: |
||
3593 |
// we are done, the remaining elements of |
||
3594 |
// non_nil list should be appended to merged list. |
||
3595 |
// else |
||
3596 |
// Set up for a new comparator evaluation and recurse. |
||
3597 |
8791580 |
lbm_value a = sptr[2]; |
|
3598 |
8791580 |
lbm_value b = lbm_cdr(a); |
|
3599 |
8791580 |
lbm_set_cdr(a, ENC_SYM_NIL); // terminate 1 element list |
|
3600 |
|||
3601 |
✓✓ | 8791580 |
if (ctx->r == ENC_SYM_NIL) { // Comparison false |
3602 |
|||
3603 |
✓✓ | 5102216 |
if (sptr[0] == ENC_SYM_NIL) { |
3604 |
1983576 |
sptr[0] = b; |
|
3605 |
1983576 |
sptr[1] = b; |
|
3606 |
} else { |
||
3607 |
3118640 |
lbm_set_cdr(sptr[1], b); |
|
3608 |
3118640 |
sptr[1] = b; |
|
3609 |
} |
||
3610 |
✓✓ | 5102216 |
if (sptr[4] == ENC_SYM_NIL) { |
3611 |
2549456 |
lbm_set_cdr(a, sptr[3]); |
|
3612 |
2549456 |
lbm_set_cdr(sptr[1], a); |
|
3613 |
2549456 |
ctx->r = sptr[0]; |
|
3614 |
2549456 |
lbm_stack_drop(&ctx->K, 9); |
|
3615 |
2549456 |
ctx->app_cont = true; |
|
3616 |
2549456 |
return; |
|
3617 |
} else { |
||
3618 |
2552760 |
b = sptr[4]; |
|
3619 |
2552760 |
sptr[4] = lbm_cdr(sptr[4]); |
|
3620 |
2552760 |
lbm_set_cdr(b, ENC_SYM_NIL); |
|
3621 |
} |
||
3622 |
} else { |
||
3623 |
✓✓ | 3689364 |
if (sptr[0] == ENC_SYM_NIL) { |
3624 |
1134812 |
sptr[0] = a; |
|
3625 |
1134812 |
sptr[1] = a; |
|
3626 |
} else { |
||
3627 |
2554552 |
lbm_set_cdr(sptr[1], a); |
|
3628 |
2554552 |
sptr[1] = a; |
|
3629 |
} |
||
3630 |
|||
3631 |
✓✓ | 3689364 |
if (sptr[3] == ENC_SYM_NIL) { |
3632 |
568932 |
lbm_set_cdr(b, sptr[4]); |
|
3633 |
568932 |
lbm_set_cdr(sptr[1], b); |
|
3634 |
568932 |
ctx->r = sptr[0]; |
|
3635 |
568932 |
lbm_stack_drop(&ctx->K, 9); |
|
3636 |
568932 |
ctx->app_cont = true; |
|
3637 |
568932 |
return; |
|
3638 |
} else { |
||
3639 |
3120432 |
a = sptr[3]; |
|
3640 |
3120432 |
sptr[3] = lbm_cdr(sptr[3]); |
|
3641 |
3120432 |
lbm_set_cdr(a, ENC_SYM_NIL); |
|
3642 |
} |
||
3643 |
} |
||
3644 |
5673192 |
lbm_set_cdr(a, b); |
|
3645 |
5673192 |
sptr[2] = a; |
|
3646 |
|||
3647 |
5673192 |
lbm_value par1 = sptr[7]; |
|
3648 |
5673192 |
lbm_value par2 = sptr[8]; |
|
3649 |
5673192 |
lbm_value cmp_body = sptr[5]; |
|
3650 |
5673192 |
lbm_value cmp_env = sptr[6]; |
|
3651 |
// Environment should be preallocated already at this point |
||
3652 |
// and the operations below should never need GC. |
||
3653 |
5673192 |
lbm_value new_env0 = lbm_env_set(cmp_env, par1, lbm_car(a)); |
|
3654 |
5673192 |
lbm_value new_env = lbm_env_set(new_env0, par2, lbm_car(b)); |
|
3655 |
✓✗✗✓ |
5673192 |
if (lbm_is_symbol(new_env0) || lbm_is_symbol(new_env)) { |
3656 |
error_ctx(ENC_SYM_FATAL_ERROR); |
||
3657 |
} |
||
3658 |
5673192 |
cmp_env = new_env; |
|
3659 |
|||
3660 |
5673192 |
stack_reserve(ctx,1)[0] = MERGE_REST; |
|
3661 |
5673192 |
ctx->curr_exp = cmp_body; |
|
3662 |
5673192 |
ctx->curr_env = cmp_env; |
|
3663 |
} |
||
3664 |
|||
3665 |
// merge_layer stack contents |
||
3666 |
// s[sp-9] = cmp |
||
3667 |
// s[sp-8] = cmp_env |
||
3668 |
// s[sp-7] = par1 |
||
3669 |
// s[sp-6] = par2 |
||
3670 |
// s[sp-5] = acc - first cell |
||
3671 |
// s[sp-4] = acc - last cell |
||
3672 |
// s[sp-3] = rest; |
||
3673 |
// s[sp-2] = layer |
||
3674 |
// s[sp-1] = length or original list |
||
3675 |
// |
||
3676 |
// ctx->r merged sublist |
||
3677 |
3401272 |
static void cont_merge_layer(eval_context_t *ctx) { |
|
3678 |
3401272 |
lbm_uint *sptr = get_stack_ptr(ctx, 9); |
|
3679 |
3401272 |
lbm_int layer = lbm_dec_i(sptr[7]); |
|
3680 |
3401272 |
lbm_int len = lbm_dec_i(sptr[8]); |
|
3681 |
|||
3682 |
3401272 |
lbm_value r_curr = ctx->r; |
|
3683 |
✓✗ | 13620600 |
while (lbm_is_cons(r_curr)) { |
3684 |
13620600 |
lbm_value next = lbm_cdr(r_curr); |
|
3685 |
✓✓ | 13620600 |
if (next == ENC_SYM_NIL) { |
3686 |
3401272 |
break; |
|
3687 |
} |
||
3688 |
10219328 |
r_curr = next; |
|
3689 |
} |
||
3690 |
|||
3691 |
✓✓ | 3401272 |
if (sptr[4] == ENC_SYM_NIL) { |
3692 |
1132348 |
sptr[4] = ctx->r; |
|
3693 |
1132348 |
sptr[5] = r_curr; |
|
3694 |
} else { |
||
3695 |
2268924 |
lbm_set_cdr(sptr[5], ctx->r); // accumulate merged sublists. |
|
3696 |
2268924 |
sptr[5] = r_curr; |
|
3697 |
} |
||
3698 |
|||
3699 |
3401272 |
lbm_value layer_rest = sptr[6]; |
|
3700 |
// switch layer or done ? |
||
3701 |
✓✓ | 3401272 |
if (layer_rest == ENC_SYM_NIL) { |
3702 |
✓✓ | 1132348 |
if (layer * 2 >= len) { |
3703 |
283108 |
ctx->r = sptr[4]; |
|
3704 |
283108 |
ctx->app_cont = true; |
|
3705 |
283108 |
lbm_stack_drop(&ctx->K, 9); |
|
3706 |
283108 |
return; |
|
3707 |
} else { |
||
3708 |
// Setup for merges of the next layer |
||
3709 |
849240 |
layer = layer * 2; |
|
3710 |
849240 |
sptr[7] = lbm_enc_i(layer); |
|
3711 |
849240 |
layer_rest = sptr[4]; // continue on the accumulation of all sublists. |
|
3712 |
849240 |
sptr[5] = ENC_SYM_NIL; |
|
3713 |
849240 |
sptr[4] = ENC_SYM_NIL; |
|
3714 |
} |
||
3715 |
} |
||
3716 |
// merge another sublist based on current layer. |
||
3717 |
3118164 |
lbm_value a_list = layer_rest; |
|
3718 |
// build sublist a |
||
3719 |
3118164 |
lbm_value curr = layer_rest; |
|
3720 |
✓✓ | 7661080 |
for (int i = 0; i < layer-1; i ++) { |
3721 |
✓✓ | 4543028 |
if (lbm_is_cons(curr)) { |
3722 |
4542916 |
curr = lbm_cdr(curr); |
|
3723 |
} else { |
||
3724 |
112 |
break; |
|
3725 |
} |
||
3726 |
} |
||
3727 |
3118164 |
layer_rest = lbm_cdr(curr); |
|
3728 |
3118164 |
lbm_set_cdr(curr, ENC_SYM_NIL); //terminate sublist. |
|
3729 |
|||
3730 |
3118164 |
lbm_value b_list = layer_rest; |
|
3731 |
// build sublist b |
||
3732 |
3118164 |
curr = layer_rest; |
|
3733 |
✓✓ | 5959800 |
for (int i = 0; i < layer-1; i ++) { |
3734 |
✓✓ | 3407796 |
if (lbm_is_cons(curr)) { |
3735 |
2841636 |
curr = lbm_cdr(curr); |
|
3736 |
} else { |
||
3737 |
566160 |
break; |
|
3738 |
} |
||
3739 |
} |
||
3740 |
3118164 |
layer_rest = lbm_cdr(curr); |
|
3741 |
3118164 |
lbm_set_cdr(curr, ENC_SYM_NIL); //terminate sublist. |
|
3742 |
|||
3743 |
3118164 |
sptr[6] = layer_rest; |
|
3744 |
|||
3745 |
✓✓ | 3118164 |
if (b_list == ENC_SYM_NIL) { |
3746 |
283192 |
stack_reserve(ctx,1)[0] = MERGE_LAYER; |
|
3747 |
283192 |
ctx->r = a_list; |
|
3748 |
283192 |
ctx->app_cont = true; |
|
3749 |
283192 |
return; |
|
3750 |
} |
||
3751 |
// Set up for a merge of sublists. |
||
3752 |
|||
3753 |
2834972 |
lbm_value a_rest = lbm_cdr(a_list); |
|
3754 |
2834972 |
lbm_value b_rest = lbm_cdr(b_list); |
|
3755 |
2834972 |
lbm_value a = a_list; |
|
3756 |
2834972 |
lbm_value b = b_list; |
|
3757 |
2834972 |
lbm_set_cdr(a, b); |
|
3758 |
// Terminating the b list would be incorrect here |
||
3759 |
// if there was any chance that the environment update below |
||
3760 |
// performs GC. |
||
3761 |
2834972 |
lbm_set_cdr(b, ENC_SYM_NIL); |
|
3762 |
|||
3763 |
2834972 |
lbm_value cmp_body = sptr[0]; |
|
3764 |
2834972 |
lbm_value cmp_env = sptr[1]; |
|
3765 |
2834972 |
lbm_value par1 = sptr[2]; |
|
3766 |
2834972 |
lbm_value par2 = sptr[3]; |
|
3767 |
// Environment should be preallocated already at this point |
||
3768 |
// and the operations below should never need GC. |
||
3769 |
2834972 |
lbm_value new_env0 = lbm_env_set(cmp_env, par1, lbm_car(a)); |
|
3770 |
2834972 |
lbm_value new_env = lbm_env_set(cmp_env, par2, lbm_car(b)); |
|
3771 |
✓✗✗✓ |
2834972 |
if (lbm_is_symbol(new_env0) || lbm_is_symbol(new_env)) { |
3772 |
error_ctx(ENC_SYM_FATAL_ERROR); |
||
3773 |
} |
||
3774 |
2834972 |
cmp_env = new_env; |
|
3775 |
|||
3776 |
2834972 |
lbm_uint *merge_cont = stack_reserve(ctx, 11); |
|
3777 |
2834972 |
merge_cont[0] = MERGE_LAYER; |
|
3778 |
2834972 |
merge_cont[1] = ENC_SYM_NIL; |
|
3779 |
2834972 |
merge_cont[2] = ENC_SYM_NIL; |
|
3780 |
2834972 |
merge_cont[3] = a; |
|
3781 |
2834972 |
merge_cont[4] = a_rest; |
|
3782 |
2834972 |
merge_cont[5] = b_rest; |
|
3783 |
2834972 |
merge_cont[6] = cmp_body; |
|
3784 |
2834972 |
merge_cont[7] = cmp_env; |
|
3785 |
2834972 |
merge_cont[8] = par1; |
|
3786 |
2834972 |
merge_cont[9] = par2; |
|
3787 |
2834972 |
merge_cont[10] = MERGE_REST; |
|
3788 |
2834972 |
ctx->curr_exp = cmp_body; |
|
3789 |
2834972 |
ctx->curr_env = cmp_env; |
|
3790 |
2834972 |
return; |
|
3791 |
} |
||
3792 |
|||
3793 |
/****************************************************/ |
||
3794 |
/* READER */ |
||
3795 |
|||
3796 |
33257 |
static void read_finish(lbm_char_channel_t *str, eval_context_t *ctx) { |
|
3797 |
|||
3798 |
/* Tokenizer reached "end of file" |
||
3799 |
The parser could be in a state where it needs |
||
3800 |
more tokens to correctly finish an expression. |
||
3801 |
|||
3802 |
Four cases |
||
3803 |
1. The program / expression is malformed and the context should die. |
||
3804 |
2. We are finished reading a program and should close off the |
||
3805 |
internal representation with a closing parenthesis. Then |
||
3806 |
apply continuation. |
||
3807 |
3. We are finished reading an expression and should |
||
3808 |
apply the continuation |
||
3809 |
4. We are finished read-and-evaluating |
||
3810 |
|||
3811 |
In case 2, we should find the READ_DONE at sp - 5. |
||
3812 |
In case 3, we should find the READ_DONE at sp - 1. |
||
3813 |
In case 4, we should find the READ_DONE at sp - 4. |
||
3814 |
|||
3815 |
case 3 should not end up here, but rather end up in |
||
3816 |
cont_read_done. |
||
3817 |
*/ |
||
3818 |
|||
3819 |
✓✓ | 33257 |
if (lbm_is_symbol(ctx->r)) { |
3820 |
10704 |
lbm_uint sym_val = lbm_dec_sym(ctx->r); |
|
3821 |
✗✓✗✗ |
10704 |
if (sym_val >= TOKENIZER_SYMBOLS_START && |
3822 |
sym_val <= TOKENIZER_SYMBOLS_END) { |
||
3823 |
read_error_ctx(lbm_channel_row(str), lbm_channel_column(str)); |
||
3824 |
} |
||
3825 |
} |
||
3826 |
|||
3827 |
✓✗✓✓ |
33257 |
if (ctx->K.sp > 4 && (ctx->K.data[ctx->K.sp - 4] == READ_DONE) && |
3828 |
✓✗ | 22071 |
(ctx->K.data[ctx->K.sp - 5] == READING_PROGRAM_INCREMENTALLY)) { |
3829 |
/* read and evaluate is done */ |
||
3830 |
lbm_value env; |
||
3831 |
lbm_value s; |
||
3832 |
lbm_value sym; |
||
3833 |
22071 |
lbm_pop_3(&ctx->K, &sym, &env, &s); |
|
3834 |
22071 |
ctx->curr_env = env; |
|
3835 |
22071 |
ctx->app_cont = true; // Program evaluated and result is in ctx->r. |
|
3836 |
✓✗✓✗ |
11186 |
} else if (ctx->K.sp > 5 && (ctx->K.data[ctx->K.sp - 5] == READ_DONE) && |
3837 |
✓✗ | 11186 |
(ctx->K.data[ctx->K.sp - 6] == READING_PROGRAM)) { |
3838 |
/* successfully finished reading a program (CASE 2) */ |
||
3839 |
11186 |
ctx->r = ENC_SYM_CLOSEPAR; |
|
3840 |
11186 |
ctx->app_cont = true; |
|
3841 |
} else { |
||
3842 |
if (lbm_channel_row(str) == 1 && lbm_channel_column(str) == 1) { |
||
3843 |
// (read "") evaluates to nil. |
||
3844 |
ctx->r = ENC_SYM_NIL; |
||
3845 |
ctx->app_cont = true; |
||
3846 |
} else { |
||
3847 |
lbm_channel_reader_close(str); |
||
3848 |
lbm_set_error_reason((char*)lbm_error_str_parse_eof); |
||
3849 |
read_error_ctx(lbm_channel_row(str), lbm_channel_column(str)); |
||
3850 |
} |
||
3851 |
} |
||
3852 |
33257 |
} |
|
3853 |
|||
3854 |
/* cont_read_next_token |
||
3855 |
sp-2 : Stream |
||
3856 |
sp-1 : Grab row |
||
3857 |
*/ |
||
3858 |
5697542 |
static void cont_read_next_token(eval_context_t *ctx) { |
|
3859 |
5697542 |
lbm_value *sptr = get_stack_ptr(ctx, 2); |
|
3860 |
5697542 |
lbm_value stream = sptr[0]; |
|
3861 |
5697542 |
lbm_value grab_row0 = sptr[1]; |
|
3862 |
|||
3863 |
5697542 |
lbm_char_channel_t *chan = lbm_dec_channel(stream); |
|
3864 |
✓✗✗✓ |
5697542 |
if (chan == NULL || chan->state == NULL) { |
3865 |
error_ctx(ENC_SYM_FATAL_ERROR); |
||
3866 |
5697542 |
return; // INFER does not understant that error_ctx longjmps |
|
3867 |
// out of this function. |
||
3868 |
} |
||
3869 |
|||
3870 |
✓✓✓✓ |
5697542 |
if (!lbm_channel_more(chan) && lbm_channel_is_empty(chan)) { |
3871 |
11872 |
lbm_stack_drop(&ctx->K, 2); |
|
3872 |
11872 |
read_finish(chan, ctx); |
|
3873 |
11872 |
return; |
|
3874 |
} |
||
3875 |
/* Eat whitespace and comments */ |
||
3876 |
✓✓ | 5685670 |
if (!tok_clean_whitespace(chan)) { |
3877 |
625 |
sptr[0] = stream; |
|
3878 |
625 |
sptr[1] = lbm_enc_u(0); |
|
3879 |
625 |
stack_reserve(ctx,1)[0] = READ_NEXT_TOKEN; |
|
3880 |
625 |
yield_ctx(EVAL_CPS_MIN_SLEEP); |
|
3881 |
625 |
return; |
|
3882 |
} |
||
3883 |
/* After eating whitespace we may be at end of file/stream */ |
||
3884 |
✓✓✓✓ |
5685045 |
if (!lbm_channel_more(chan) && lbm_channel_is_empty(chan)) { |
3885 |
21385 |
lbm_stack_drop(&ctx->K, 2); |
|
3886 |
21385 |
read_finish(chan, ctx); |
|
3887 |
21385 |
return; |
|
3888 |
} |
||
3889 |
|||
3890 |
✓✓ | 5663660 |
if (lbm_dec_u(grab_row0)) { |
3891 |
377897 |
ctx->row0 = (int32_t)lbm_channel_row(chan); |
|
3892 |
377897 |
ctx->row1 = -1; // a new start, end is unknown |
|
3893 |
} |
||
3894 |
|||
3895 |
/* Attempt to extract tokens from the character stream */ |
||
3896 |
5663660 |
int n = 0; |
|
3897 |
5663660 |
lbm_value res = ENC_SYM_NIL; |
|
3898 |
5663660 |
unsigned int string_len = 0; |
|
3899 |
|||
3900 |
/* |
||
3901 |
* SYNTAX |
||
3902 |
*/ |
||
3903 |
uint32_t tok_match; |
||
3904 |
5663660 |
n = tok_syntax(chan, &tok_match); |
|
3905 |
✓✓ | 5663660 |
if (n > 0) { |
3906 |
✗✓ | 1403640 |
if (!lbm_channel_drop(chan, (unsigned int)n)) { |
3907 |
error_ctx(ENC_SYM_FATAL_ERROR); |
||
3908 |
} |
||
3909 |
1403640 |
ctx->app_cont = true; |
|
3910 |
✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✗ |
1403640 |
switch(tok_match) { |
3911 |
666008 |
case TOKOPENPAR: { |
|
3912 |
666008 |
sptr[0] = ENC_SYM_NIL; |
|
3913 |
666008 |
sptr[1] = ENC_SYM_NIL; |
|
3914 |
666008 |
lbm_value *rptr = stack_reserve(ctx,5); |
|
3915 |
666008 |
rptr[0] = stream; |
|
3916 |
666008 |
rptr[1] = READ_APPEND_CONTINUE; |
|
3917 |
666008 |
rptr[2] = stream; |
|
3918 |
666008 |
rptr[3] = lbm_enc_u(0); |
|
3919 |
666008 |
rptr[4] = READ_NEXT_TOKEN; |
|
3920 |
666008 |
ctx->r = ENC_SYM_OPENPAR; |
|
3921 |
666008 |
} return; |
|
3922 |
666008 |
case TOKCLOSEPAR: { |
|
3923 |
666008 |
lbm_stack_drop(&ctx->K, 2); |
|
3924 |
666008 |
ctx->r = ENC_SYM_CLOSEPAR; |
|
3925 |
666008 |
} return; |
|
3926 |
3304 |
case TOKOPENBRACK: { |
|
3927 |
3304 |
sptr[0] = stream; |
|
3928 |
3304 |
sptr[1] = READ_START_ARRAY; |
|
3929 |
3304 |
lbm_value *rptr = stack_reserve(ctx, 3); |
|
3930 |
3304 |
rptr[0] = stream; |
|
3931 |
3304 |
rptr[1] = lbm_enc_u(0); |
|
3932 |
3304 |
rptr[2] = READ_NEXT_TOKEN; |
|
3933 |
3304 |
ctx->r = ENC_SYM_OPENBRACK; |
|
3934 |
3304 |
} return; |
|
3935 |
3304 |
case TOKCLOSEBRACK: |
|
3936 |
3304 |
lbm_stack_drop(&ctx->K, 2); |
|
3937 |
3304 |
ctx->r = ENC_SYM_CLOSEBRACK; |
|
3938 |
3304 |
return; |
|
3939 |
6104 |
case TOKDOT: |
|
3940 |
6104 |
lbm_stack_drop(&ctx->K, 2); |
|
3941 |
6104 |
ctx->r = ENC_SYM_DOT; |
|
3942 |
6104 |
return; |
|
3943 |
980 |
case TOKDONTCARE: |
|
3944 |
980 |
lbm_stack_drop(&ctx->K, 2); |
|
3945 |
980 |
ctx->r = ENC_SYM_DONTCARE; |
|
3946 |
980 |
return; |
|
3947 |
27188 |
case TOKQUOTE: |
|
3948 |
27188 |
sptr[0] = ENC_SYM_QUOTE; |
|
3949 |
27188 |
sptr[1] = WRAP_RESULT; |
|
3950 |
27188 |
break; |
|
3951 |
5068 |
case TOKBACKQUOTE: { |
|
3952 |
5068 |
sptr[0] = QQ_EXPAND_START; |
|
3953 |
5068 |
sptr[1] = stream; |
|
3954 |
5068 |
lbm_value *rptr = stack_reserve(ctx, 2); |
|
3955 |
5068 |
rptr[0] = lbm_enc_u(0); |
|
3956 |
5068 |
rptr[1] = READ_NEXT_TOKEN; |
|
3957 |
5068 |
ctx->app_cont = true; |
|
3958 |
5068 |
} return; |
|
3959 |
140 |
case TOKCOMMAAT: |
|
3960 |
140 |
sptr[0] = ENC_SYM_COMMAAT; |
|
3961 |
140 |
sptr[1] = WRAP_RESULT; |
|
3962 |
140 |
break; |
|
3963 |
13944 |
case TOKCOMMA: |
|
3964 |
13944 |
sptr[0] = ENC_SYM_COMMA; |
|
3965 |
13944 |
sptr[1] = WRAP_RESULT; |
|
3966 |
13944 |
break; |
|
3967 |
6720 |
case TOKMATCHANY: |
|
3968 |
6720 |
lbm_stack_drop(&ctx->K, 2); |
|
3969 |
6720 |
ctx->r = ENC_SYM_MATCH_ANY; |
|
3970 |
6720 |
return; |
|
3971 |
2408 |
case TOKOPENCURL: { |
|
3972 |
2408 |
sptr[0] = ENC_SYM_NIL; |
|
3973 |
2408 |
sptr[1] = ENC_SYM_NIL; |
|
3974 |
2408 |
lbm_value *rptr = stack_reserve(ctx,2); |
|
3975 |
2408 |
rptr[0] = stream; |
|
3976 |
2408 |
rptr[1] = READ_APPEND_CONTINUE; |
|
3977 |
2408 |
ctx->r = ENC_SYM_PROGN; |
|
3978 |
2408 |
} return; |
|
3979 |
2408 |
case TOKCLOSECURL: |
|
3980 |
2408 |
lbm_stack_drop(&ctx->K, 2); |
|
3981 |
2408 |
ctx->r = ENC_SYM_CLOSEPAR; |
|
3982 |
2408 |
return; |
|
3983 |
56 |
case TOKCONSTSTART: /* fall through */ |
|
3984 |
case TOKCONSTEND: { |
||
3985 |
✓✓ | 56 |
if (tok_match == TOKCONSTSTART) ctx->flags |= EVAL_CPS_CONTEXT_FLAG_CONST; |
3986 |
✓✓ | 56 |
if (tok_match == TOKCONSTEND) ctx->flags &= ~EVAL_CPS_CONTEXT_FLAG_CONST; |
3987 |
56 |
sptr[0] = stream; |
|
3988 |
56 |
sptr[1] = lbm_enc_u(0); |
|
3989 |
56 |
stack_reserve(ctx,1)[0] = READ_NEXT_TOKEN; |
|
3990 |
56 |
ctx->app_cont = true; |
|
3991 |
56 |
} return; |
|
3992 |
default: |
||
3993 |
read_error_ctx(lbm_channel_row(chan), lbm_channel_column(chan)); |
||
3994 |
} |
||
3995 |
// read next token |
||
3996 |
41272 |
lbm_value *rptr = stack_reserve(ctx, 3); |
|
3997 |
41272 |
rptr[0] = stream; |
|
3998 |
41272 |
rptr[1] = lbm_enc_u(0); |
|
3999 |
41272 |
rptr[2] = READ_NEXT_TOKEN; |
|
4000 |
41272 |
ctx->app_cont = true; |
|
4001 |
41272 |
return; |
|
4002 |
✗✓ | 4260020 |
} else if (n < 0) goto retry_token; |
4003 |
|||
4004 |
/* |
||
4005 |
* STRING |
||
4006 |
*/ |
||
4007 |
4260020 |
n = tok_string(chan, &string_len); |
|
4008 |
✓✓ | 4260020 |
if (n >= 2) { |
4009 |
9380 |
lbm_channel_drop(chan, (unsigned int)n); |
|
4010 |
#ifdef LBM_ALWAYS_GC |
||
4011 |
gc(); |
||
4012 |
#endif |
||
4013 |
✗✓ | 9380 |
if (!lbm_heap_allocate_array(&res, (unsigned int)(string_len+1))) { |
4014 |
gc(); |
||
4015 |
lbm_heap_allocate_array(&res, (unsigned int)(string_len+1)); |
||
4016 |
} |
||
4017 |
✓✗ | 9380 |
if (lbm_is_ptr(res)) { |
4018 |
9380 |
lbm_array_header_t *arr = assume_array(res); |
|
4019 |
9380 |
char *data = (char*)arr->data; |
|
4020 |
9380 |
memset(data,0, string_len + 1); |
|
4021 |
9380 |
memcpy(data, tokpar_sym_str, string_len); |
|
4022 |
9380 |
lbm_stack_drop(&ctx->K, 2); |
|
4023 |
9380 |
ctx->r = res; |
|
4024 |
9380 |
ctx->app_cont = true; |
|
4025 |
9380 |
return; |
|
4026 |
} else { |
||
4027 |
error_ctx(ENC_SYM_MERROR); |
||
4028 |
} |
||
4029 |
✓✓ | 4250640 |
} else if (n < 0) goto retry_token; |
4030 |
|||
4031 |
/* |
||
4032 |
* FLOAT |
||
4033 |
*/ |
||
4034 |
token_float f_val; |
||
4035 |
4250639 |
n = tok_double(chan, &f_val); |
|
4036 |
✓✓ | 4250639 |
if (n > 0) { |
4037 |
11452 |
lbm_channel_drop(chan, (unsigned int) n); |
|
4038 |
✓✓✗ | 11452 |
switch(f_val.type) { |
4039 |
8428 |
case TOKTYPEF32: |
|
4040 |
✗✓✗✗ |
8428 |
WITH_GC(res, lbm_enc_float((float)f_val.value)); |
4041 |
8428 |
break; |
|
4042 |
3024 |
case TOKTYPEF64: |
|
4043 |
3024 |
res = lbm_enc_double(f_val.value); |
|
4044 |
3024 |
break; |
|
4045 |
default: |
||
4046 |
read_error_ctx(lbm_channel_row(chan), lbm_channel_column(chan)); |
||
4047 |
} |
||
4048 |
11452 |
lbm_stack_drop(&ctx->K, 2); |
|
4049 |
11452 |
ctx->r = res; |
|
4050 |
11452 |
ctx->app_cont = true; |
|
4051 |
11452 |
return; |
|
4052 |
✓✓ | 4239187 |
} else if (n < 0) goto retry_token; |
4053 |
|||
4054 |
/* |
||
4055 |
* INTEGER |
||
4056 |
*/ |
||
4057 |
token_int int_result; |
||
4058 |
4239184 |
n = tok_integer(chan, &int_result); |
|
4059 |
✓✓ | 4239184 |
if (n > 0) { |
4060 |
3356584 |
lbm_channel_drop(chan, (unsigned int)n); |
|
4061 |
✓✓✓✓ ✓✓✓✗ |
3356584 |
switch(int_result.type) { |
4062 |
2212 |
case TOKTYPEBYTE: |
|
4063 |
✗✓ | 2212 |
res = lbm_enc_char((uint8_t)(int_result.negative ? -int_result.value : int_result.value)); |
4064 |
2212 |
break; |
|
4065 |
3335528 |
case TOKTYPEI: |
|
4066 |
✓✓ | 3335528 |
res = lbm_enc_i((lbm_int)(int_result.negative ? -int_result.value : int_result.value)); |
4067 |
3335528 |
break; |
|
4068 |
3500 |
case TOKTYPEU: |
|
4069 |
✓✓ | 3500 |
res = lbm_enc_u((lbm_uint)(int_result.negative ? -int_result.value : int_result.value)); |
4070 |
3500 |
break; |
|
4071 |
3668 |
case TOKTYPEI32: |
|
4072 |
✓✓✗✓ ✗✗✗✗ |
3668 |
WITH_GC(res, lbm_enc_i32((int32_t)(int_result.negative ? -int_result.value : int_result.value))); |
4073 |
3668 |
break; |
|
4074 |
4480 |
case TOKTYPEU32: |
|
4075 |
✓✓✗✓ ✗✗✗✗ |
4480 |
WITH_GC(res,lbm_enc_u32((uint32_t)(int_result.negative ? -int_result.value : int_result.value))); |
4076 |
4480 |
break; |
|
4077 |
3780 |
case TOKTYPEI64: |
|
4078 |
✓✓✗✓ ✗✗✗✗ |
3780 |
WITH_GC(res,lbm_enc_i64((int64_t)(int_result.negative ? -int_result.value : int_result.value))); |
4079 |
3780 |
break; |
|
4080 |
3416 |
case TOKTYPEU64: |
|
4081 |
✓✓✗✓ ✗✗✗✗ |
3416 |
WITH_GC(res,lbm_enc_u64((uint64_t)(int_result.negative ? -int_result.value : int_result.value))); |
4082 |
3416 |
break; |
|
4083 |
default: |
||
4084 |
read_error_ctx(lbm_channel_row(chan), lbm_channel_column(chan)); |
||
4085 |
} |
||
4086 |
3356584 |
lbm_stack_drop(&ctx->K, 2); |
|
4087 |
3356584 |
ctx->r = res; |
|
4088 |
3356584 |
ctx->app_cont = true; |
|
4089 |
3356584 |
return; |
|
4090 |
✗✓ | 882600 |
} else if (n < 0) goto retry_token; |
4091 |
|||
4092 |
/* |
||
4093 |
* SYMBOL |
||
4094 |
*/ |
||
4095 |
882600 |
n = tok_symbol(chan); |
|
4096 |
✓✓ | 882600 |
if (n > 0) { |
4097 |
882420 |
lbm_channel_drop(chan, (unsigned int) n); |
|
4098 |
lbm_uint symbol_id; |
||
4099 |
✓✓ | 882420 |
if (!lbm_get_symbol_by_name(tokpar_sym_str, &symbol_id)) { |
4100 |
99358 |
int r = 0; |
|
4101 |
✓✓ | 99358 |
if (n > 4 && |
4102 |
✓✓ | 23814 |
tokpar_sym_str[0] == 'e' && |
4103 |
✓✓ | 434 |
tokpar_sym_str[1] == 'x' && |
4104 |
✓✓ | 70 |
tokpar_sym_str[2] == 't' && |
4105 |
✓✓ | 56 |
tokpar_sym_str[3] == '-') { |
4106 |
lbm_uint ext_id; |
||
4107 |
14 |
lbm_uint ext_name_len = (lbm_uint)n + 1; |
|
4108 |
#ifdef LBM_ALWAYS_GC |
||
4109 |
gc(); |
||
4110 |
#endif |
||
4111 |
14 |
char *ext_name = lbm_malloc(ext_name_len); |
|
4112 |
✗✓ | 14 |
if (!ext_name) { |
4113 |
gc(); |
||
4114 |
ext_name = lbm_malloc(ext_name_len); |
||
4115 |
} |
||
4116 |
✓✗ | 14 |
if (ext_name) { |
4117 |
14 |
memcpy(ext_name, tokpar_sym_str, ext_name_len); |
|
4118 |
14 |
r = lbm_add_extension(ext_name, lbm_extensions_default); |
|
4119 |
✗✓ | 14 |
if (!lbm_lookup_extension_id(ext_name, &ext_id)) { |
4120 |
error_ctx(ENC_SYM_FATAL_ERROR); |
||
4121 |
} |
||
4122 |
14 |
symbol_id = ext_id; |
|
4123 |
} else { |
||
4124 |
error_ctx(ENC_SYM_MERROR); |
||
4125 |
} |
||
4126 |
} else { |
||
4127 |
✓✓ | 99344 |
if (ctx->flags & EVAL_CPS_CONTEXT_FLAG_CONST && |
4128 |
✓✓ | 140 |
ctx->flags & EVAL_CPS_CONTEXT_FLAG_INCREMENTAL_READ) { |
4129 |
70 |
r = lbm_add_symbol_base(tokpar_sym_str, &symbol_id, true); //flash |
|
4130 |
✗✓ | 70 |
if (!r) { |
4131 |
lbm_set_error_reason((char*)lbm_error_str_flash_error); |
||
4132 |
error_ctx(ENC_SYM_FATAL_ERROR); |
||
4133 |
} |
||
4134 |
} else { |
||
4135 |
#ifdef LBM_ALWAYS_GC |
||
4136 |
gc(); |
||
4137 |
#endif |
||
4138 |
99274 |
r = lbm_add_symbol_base(tokpar_sym_str, &symbol_id,false); //ram |
|
4139 |
✓✓ | 99274 |
if (!r) { |
4140 |
10 |
gc(); |
|
4141 |
10 |
r = lbm_add_symbol_base(tokpar_sym_str, &symbol_id,false); //ram |
|
4142 |
} |
||
4143 |
} |
||
4144 |
} |
||
4145 |
✗✓ | 99358 |
if (!r) { |
4146 |
read_error_ctx(lbm_channel_row(chan), lbm_channel_column(chan)); |
||
4147 |
} |
||
4148 |
} |
||
4149 |
882420 |
lbm_stack_drop(&ctx->K, 2); |
|
4150 |
882420 |
ctx->r = lbm_enc_sym(symbol_id); |
|
4151 |
882420 |
ctx->app_cont = true; |
|
4152 |
882420 |
return; |
|
4153 |
✓✓ | 180 |
} else if (n == TOKENIZER_NEED_MORE) { |
4154 |
12 |
goto retry_token; |
|
4155 |
✗✓ | 168 |
} else if (n <= TOKENIZER_STRING_ERROR) { |
4156 |
read_error_ctx(lbm_channel_row(chan), lbm_channel_column(chan)); |
||
4157 |
} |
||
4158 |
|||
4159 |
/* |
||
4160 |
* CHAR |
||
4161 |
*/ |
||
4162 |
char c_val; |
||
4163 |
168 |
n = tok_char(chan, &c_val); |
|
4164 |
✓✗ | 168 |
if(n > 0) { |
4165 |
168 |
lbm_channel_drop(chan,(unsigned int) n); |
|
4166 |
168 |
lbm_stack_drop(&ctx->K, 2); |
|
4167 |
168 |
ctx->r = lbm_enc_char((uint8_t)c_val); |
|
4168 |
168 |
ctx->app_cont = true; |
|
4169 |
168 |
return; |
|
4170 |
}else if (n < 0) goto retry_token; |
||
4171 |
|||
4172 |
read_error_ctx(lbm_channel_row(chan), lbm_channel_column(chan)); |
||
4173 |
|||
4174 |
16 |
retry_token: |
|
4175 |
✓✗ | 16 |
if (n == TOKENIZER_NEED_MORE) { |
4176 |
16 |
sptr[0] = stream; |
|
4177 |
16 |
sptr[1] = lbm_enc_u(0); |
|
4178 |
16 |
stack_reserve(ctx,1)[0] = READ_NEXT_TOKEN; |
|
4179 |
16 |
yield_ctx(EVAL_CPS_MIN_SLEEP); |
|
4180 |
16 |
return; |
|
4181 |
} |
||
4182 |
read_error_ctx(lbm_channel_row(chan), lbm_channel_column(chan)); |
||
4183 |
} |
||
4184 |
|||
4185 |
3304 |
static void cont_read_start_array(eval_context_t *ctx) { |
|
4186 |
3304 |
lbm_value *sptr = get_stack_ptr(ctx, 1); |
|
4187 |
3304 |
lbm_value stream = sptr[0]; |
|
4188 |
|||
4189 |
3304 |
lbm_char_channel_t *str = lbm_dec_channel(stream); |
|
4190 |
✓✗✗✓ |
3304 |
if (str == NULL || str->state == NULL) { |
4191 |
error_ctx(ENC_SYM_FATAL_ERROR); |
||
4192 |
return; // INFER does not understand that error_ctx longjmps out |
||
4193 |
// of this function here. |
||
4194 |
} |
||
4195 |
✓✓ | 3304 |
if (ctx->r == ENC_SYM_CLOSEBRACK) { |
4196 |
lbm_value array; |
||
4197 |
|||
4198 |
✗✓ | 56 |
if (!lbm_heap_allocate_array(&array, 0)) { |
4199 |
gc(); |
||
4200 |
if (!lbm_heap_allocate_array(&array, 0)) { |
||
4201 |
lbm_set_error_reason((char*)lbm_error_str_read_no_mem); |
||
4202 |
lbm_channel_reader_close(str); |
||
4203 |
error_ctx(ENC_SYM_FATAL_ERROR); // Terminates ctx |
||
4204 |
} |
||
4205 |
} |
||
4206 |
56 |
lbm_stack_drop(&ctx->K, 1); |
|
4207 |
56 |
ctx->r = array; |
|
4208 |
56 |
ctx->app_cont = true; |
|
4209 |
✓✗ | 3248 |
} else if (lbm_is_number(ctx->r)) { |
4210 |
#ifdef LBM_ALWAYS_GC |
||
4211 |
gc(); |
||
4212 |
#endif |
||
4213 |
3248 |
lbm_uint num_free = lbm_memory_longest_free(); |
|
4214 |
3248 |
lbm_uint initial_size = (lbm_uint)((float)num_free * 0.9); |
|
4215 |
✗✓ | 3248 |
if (initial_size == 0) { |
4216 |
gc(); |
||
4217 |
num_free = lbm_memory_longest_free(); |
||
4218 |
initial_size = (lbm_uint)((float)num_free * 0.9); |
||
4219 |
if (initial_size == 0) { |
||
4220 |
lbm_channel_reader_close(str); |
||
4221 |
error_ctx(ENC_SYM_MERROR); |
||
4222 |
} |
||
4223 |
} |
||
4224 |
lbm_value array; |
||
4225 |
3248 |
initial_size = sizeof(lbm_uint) * initial_size; |
|
4226 |
|||
4227 |
// Keep in mind that this allocation can fail for both |
||
4228 |
// lbm_memory and heap reasons. |
||
4229 |
✗✓ | 3248 |
if (!lbm_heap_allocate_array(&array, initial_size)) { |
4230 |
gc(); |
||
4231 |
if (!lbm_heap_allocate_array(&array, initial_size)) { |
||
4232 |
lbm_set_error_reason((char*)lbm_error_str_read_no_mem); |
||
4233 |
lbm_channel_reader_close(str); |
||
4234 |
error_ctx(ENC_SYM_FATAL_ERROR); |
||
4235 |
// NOTE: If array is not created evaluation ends here. |
||
4236 |
// Static analysis seems unaware. |
||
4237 |
} |
||
4238 |
} |
||
4239 |
|||
4240 |
3248 |
sptr[0] = array; |
|
4241 |
3248 |
lbm_value *rptr = stack_reserve(ctx, 4); |
|
4242 |
3248 |
rptr[0] = lbm_enc_u(initial_size); |
|
4243 |
3248 |
rptr[1] = lbm_enc_u(0); |
|
4244 |
3248 |
rptr[2] = stream; |
|
4245 |
3248 |
rptr[3] = READ_APPEND_ARRAY; |
|
4246 |
3248 |
ctx->app_cont = true; |
|
4247 |
} else { |
||
4248 |
lbm_channel_reader_close(str); |
||
4249 |
read_error_ctx(lbm_channel_row(str), lbm_channel_column(str)); |
||
4250 |
} |
||
4251 |
} |
||
4252 |
|||
4253 |
371000 |
static void cont_read_append_array(eval_context_t *ctx) { |
|
4254 |
371000 |
lbm_uint *sptr = get_stack_ptr(ctx, 4); |
|
4255 |
|||
4256 |
371000 |
lbm_value array = sptr[0]; |
|
4257 |
371000 |
lbm_value size = lbm_dec_as_u32(sptr[1]); |
|
4258 |
371000 |
lbm_value ix = lbm_dec_as_u32(sptr[2]); |
|
4259 |
371000 |
lbm_value stream = sptr[3]; |
|
4260 |
|||
4261 |
✗✓ | 371000 |
if (ix >= (size - 1)) { |
4262 |
error_ctx(ENC_SYM_MERROR); |
||
4263 |
} |
||
4264 |
|||
4265 |
// if sptr[0] is not an array something is very very wrong. |
||
4266 |
// Not robust against a garbage on stack. But how would garbage get onto stack? |
||
4267 |
371000 |
lbm_array_header_t *arr = assume_array(array); |
|
4268 |
✓✓ | 371000 |
if (lbm_is_number(ctx->r)) { |
4269 |
367752 |
((uint8_t*)arr->data)[ix] = (uint8_t)lbm_dec_as_u32(ctx->r); |
|
4270 |
|||
4271 |
367752 |
sptr[2] = lbm_enc_u(ix + 1); |
|
4272 |
367752 |
lbm_value *rptr = stack_reserve(ctx, 4); |
|
4273 |
367752 |
rptr[0] = READ_APPEND_ARRAY; |
|
4274 |
367752 |
rptr[1] = stream; |
|
4275 |
367752 |
rptr[2] = lbm_enc_u(0); |
|
4276 |
367752 |
rptr[3] = READ_NEXT_TOKEN; |
|
4277 |
367752 |
ctx->app_cont = true; |
|
4278 |
✓✗✓✗ |
3248 |
} else if (lbm_is_symbol(ctx->r) && ctx->r == ENC_SYM_CLOSEBRACK) { |
4279 |
3248 |
lbm_uint array_size = ix / sizeof(lbm_uint); |
|
4280 |
|||
4281 |
✓✓ | 3248 |
if (ix % sizeof(lbm_uint) != 0) { |
4282 |
2436 |
array_size = array_size + 1; |
|
4283 |
} |
||
4284 |
3248 |
lbm_memory_shrink((lbm_uint*)arr->data, array_size); |
|
4285 |
3248 |
arr->size = ix; |
|
4286 |
3248 |
lbm_stack_drop(&ctx->K, 4); |
|
4287 |
3248 |
ctx->r = array; |
|
4288 |
3248 |
ctx->app_cont = true; |
|
4289 |
} else { |
||
4290 |
error_ctx(ENC_SYM_TERROR); |
||
4291 |
} |
||
4292 |
371000 |
} |
|
4293 |
|||
4294 |
4880834 |
static void cont_read_append_continue(eval_context_t *ctx) { |
|
4295 |
4880834 |
lbm_value *sptr = get_stack_ptr(ctx, 3); |
|
4296 |
|||
4297 |
4880834 |
lbm_value first_cell = sptr[0]; |
|
4298 |
4880834 |
lbm_value last_cell = sptr[1]; |
|
4299 |
4880834 |
lbm_value stream = sptr[2]; |
|
4300 |
|||
4301 |
4880834 |
lbm_char_channel_t *str = lbm_dec_channel(stream); |
|
4302 |
✓✗✗✓ |
4880834 |
if (str == NULL || str->state == NULL) { |
4303 |
error_ctx(ENC_SYM_FATAL_ERROR); |
||
4304 |
return; // INFER does not understand that execution |
||
4305 |
// jumps out on error_ctx with a longjmp. |
||
4306 |
} |
||
4307 |
|||
4308 |
✓✓ | 4880834 |
if (lbm_type_of(ctx->r) == LBM_TYPE_SYMBOL) { |
4309 |
|||
4310 |
✓✓✓ | 1526994 |
switch(ctx->r) { |
4311 |
673498 |
case ENC_SYM_CLOSEPAR: |
|
4312 |
✓✓ | 673498 |
if (lbm_type_of(last_cell) == LBM_TYPE_CONS) { |
4313 |
670838 |
lbm_set_cdr(last_cell, ENC_SYM_NIL); // terminate the list |
|
4314 |
670838 |
ctx->r = first_cell; |
|
4315 |
} else { |
||
4316 |
2660 |
ctx->r = ENC_SYM_NIL; |
|
4317 |
} |
||
4318 |
673498 |
lbm_stack_drop(&ctx->K, 3); |
|
4319 |
/* Skip reading another token and apply the continuation */ |
||
4320 |
673498 |
ctx->app_cont = true; |
|
4321 |
673498 |
return; |
|
4322 |
6104 |
case ENC_SYM_DOT: { |
|
4323 |
6104 |
lbm_value *rptr = stack_reserve(ctx, 4); |
|
4324 |
6104 |
rptr[0] = READ_DOT_TERMINATE; |
|
4325 |
6104 |
rptr[1] = stream; |
|
4326 |
6104 |
rptr[2] = lbm_enc_u(0); |
|
4327 |
6104 |
rptr[3] = READ_NEXT_TOKEN; |
|
4328 |
6104 |
ctx->app_cont = true; |
|
4329 |
6104 |
} return; |
|
4330 |
} |
||
4331 |
} |
||
4332 |
4201232 |
lbm_value new_cell = cons_with_gc(ctx->r, ENC_SYM_NIL, ENC_SYM_NIL); |
|
4333 |
// Does not return if merror. So we cannot get a read-error here |
||
4334 |
// unless we write the a version of cons_with_gc here. |
||
4335 |
//if (lbm_is_symbol_merror(new_cell)) { |
||
4336 |
// lbm_channel_reader_close(str); |
||
4337 |
// read_error_ctx(lbm_channel_row(str), lbm_channel_column(str)); |
||
4338 |
// return; |
||
4339 |
//} |
||
4340 |
✓✓ | 4201232 |
if (lbm_type_of(last_cell) == LBM_TYPE_CONS) { |
4341 |
3524290 |
lbm_set_cdr(last_cell, new_cell); |
|
4342 |
3524290 |
last_cell = new_cell; |
|
4343 |
} else { |
||
4344 |
676942 |
first_cell = last_cell = new_cell; |
|
4345 |
} |
||
4346 |
4201232 |
sptr[0] = first_cell; |
|
4347 |
4201232 |
sptr[1] = last_cell; |
|
4348 |
//sptr[2] = stream; // unchanged. |
||
4349 |
4201232 |
lbm_value *rptr = stack_reserve(ctx, 4); |
|
4350 |
4201232 |
rptr[0] = READ_APPEND_CONTINUE; |
|
4351 |
4201232 |
rptr[1] = stream; |
|
4352 |
4201232 |
rptr[2] = lbm_enc_u(0); |
|
4353 |
4201232 |
rptr[3] = READ_NEXT_TOKEN; |
|
4354 |
4201232 |
ctx->app_cont = true; |
|
4355 |
} |
||
4356 |
|||
4357 |
69720 |
static void cont_read_eval_continue(eval_context_t *ctx) { |
|
4358 |
lbm_value env; |
||
4359 |
lbm_value stream; |
||
4360 |
69720 |
lbm_value *sptr = get_stack_ptr(ctx, 2); |
|
4361 |
69720 |
env = sptr[1]; |
|
4362 |
69720 |
stream = sptr[0]; |
|
4363 |
69720 |
lbm_char_channel_t *str = lbm_dec_channel(stream); |
|
4364 |
✓✗✓✗ |
69720 |
if (str && str->state) { |
4365 |
69720 |
ctx->row1 = (lbm_int)str->row(str); |
|
4366 |
✓✓ | 69720 |
if (lbm_type_of(ctx->r) == LBM_TYPE_SYMBOL) { |
4367 |
✗✗✓ | 5600 |
switch(ctx->r) { |
4368 |
case ENC_SYM_CLOSEPAR: |
||
4369 |
lbm_stack_drop(&ctx->K, 2); |
||
4370 |
ctx->app_cont = true; |
||
4371 |
return; |
||
4372 |
case ENC_SYM_DOT: |
||
4373 |
// A dot here is a syntax error. |
||
4374 |
lbm_set_error_reason((char*)lbm_error_str_parse_dot); |
||
4375 |
read_error_ctx(lbm_channel_row(str),lbm_channel_column(str)); |
||
4376 |
return; |
||
4377 |
} |
||
4378 |
} |
||
4379 |
69720 |
lbm_value *rptr = stack_reserve(ctx, 6); |
|
4380 |
69720 |
rptr[0] = READ_EVAL_CONTINUE; |
|
4381 |
69720 |
rptr[1] = stream; |
|
4382 |
69720 |
rptr[2] = lbm_enc_u(1); |
|
4383 |
69720 |
rptr[3] = READ_NEXT_TOKEN; |
|
4384 |
69720 |
rptr[4] = lbm_enc_u(ctx->flags); |
|
4385 |
69720 |
rptr[5] = POP_READER_FLAGS; |
|
4386 |
|||
4387 |
69720 |
ctx->curr_env = env; |
|
4388 |
69720 |
ctx->curr_exp = ctx->r; |
|
4389 |
} else { |
||
4390 |
error_ctx(ENC_SYM_FATAL_ERROR); |
||
4391 |
} |
||
4392 |
} |
||
4393 |
|||
4394 |
6104 |
static void cont_read_expect_closepar(eval_context_t *ctx) { |
|
4395 |
lbm_value res; |
||
4396 |
lbm_value stream; |
||
4397 |
|||
4398 |
6104 |
lbm_pop_2(&ctx->K, &res, &stream); |
|
4399 |
|||
4400 |
6104 |
lbm_char_channel_t *str = lbm_dec_channel(stream); |
|
4401 |
✓✗✗✓ |
6104 |
if (str == NULL || str->state == NULL) { // TODO: De Morgan these conditions. |
4402 |
error_ctx(ENC_SYM_FATAL_ERROR); |
||
4403 |
} else { |
||
4404 |
✓✗ | 6104 |
if (lbm_type_of(ctx->r) == LBM_TYPE_SYMBOL && |
4405 |
✓✗ | 6104 |
ctx->r == ENC_SYM_CLOSEPAR) { |
4406 |
6104 |
ctx->r = res; |
|
4407 |
6104 |
ctx->app_cont = true; |
|
4408 |
} else { |
||
4409 |
lbm_channel_reader_close(str); |
||
4410 |
lbm_set_error_reason((char*)lbm_error_str_parse_close); |
||
4411 |
read_error_ctx(lbm_channel_row(str), lbm_channel_column(str)); |
||
4412 |
} |
||
4413 |
} |
||
4414 |
6104 |
} |
|
4415 |
|||
4416 |
6104 |
static void cont_read_dot_terminate(eval_context_t *ctx) { |
|
4417 |
6104 |
lbm_value *sptr = get_stack_ptr(ctx, 3); |
|
4418 |
|||
4419 |
6104 |
lbm_value last_cell = sptr[1]; |
|
4420 |
6104 |
lbm_value stream = sptr[2]; |
|
4421 |
|||
4422 |
6104 |
lbm_char_channel_t *str = lbm_dec_channel(stream); |
|
4423 |
✓✗✗✓ |
6104 |
if (str == NULL || str->state == NULL) { |
4424 |
error_ctx(ENC_SYM_FATAL_ERROR); |
||
4425 |
✓✓ | 6104 |
} else if (lbm_type_of(ctx->r) == LBM_TYPE_SYMBOL && |
4426 |
✓✗ | 1680 |
(ctx->r == ENC_SYM_CLOSEPAR || |
4427 |
✗✓ | 1680 |
ctx->r == ENC_SYM_DOT)) { |
4428 |
lbm_channel_reader_close(str); |
||
4429 |
lbm_set_error_reason((char*)lbm_error_str_parse_dot); |
||
4430 |
read_error_ctx(lbm_channel_row(str), lbm_channel_column(str)); |
||
4431 |
✓✗ | 6104 |
} else if (lbm_is_cons(last_cell)) { |
4432 |
6104 |
lbm_set_cdr(last_cell, ctx->r); |
|
4433 |
6104 |
ctx->r = sptr[0]; // first cell |
|
4434 |
6104 |
lbm_value *rptr = stack_reserve(ctx, 3); |
|
4435 |
6104 |
sptr[0] = stream; |
|
4436 |
6104 |
sptr[1] = ctx->r; |
|
4437 |
6104 |
sptr[2] = READ_EXPECT_CLOSEPAR; |
|
4438 |
6104 |
rptr[0] = stream; |
|
4439 |
6104 |
rptr[1] = lbm_enc_u(0); |
|
4440 |
6104 |
rptr[2] = READ_NEXT_TOKEN; |
|
4441 |
6104 |
ctx->app_cont = true; |
|
4442 |
} else { |
||
4443 |
lbm_channel_reader_close(str); |
||
4444 |
lbm_set_error_reason((char*)lbm_error_str_parse_dot); |
||
4445 |
read_error_ctx(lbm_channel_row(str), lbm_channel_column(str)); |
||
4446 |
} |
||
4447 |
6104 |
} |
|
4448 |
|||
4449 |
330281 |
static void cont_read_done(eval_context_t *ctx) { |
|
4450 |
lbm_value stream; |
||
4451 |
lbm_value f_val; |
||
4452 |
lbm_value reader_mode; |
||
4453 |
330281 |
lbm_pop_3(&ctx->K, &reader_mode, &stream, &f_val); |
|
4454 |
|||
4455 |
330281 |
uint32_t flags = lbm_dec_as_u32(f_val); |
|
4456 |
330281 |
ctx->flags &= ~EVAL_CPS_CONTEXT_READER_FLAGS_MASK; |
|
4457 |
330281 |
ctx->flags |= (flags & EVAL_CPS_CONTEXT_READER_FLAGS_MASK); |
|
4458 |
|||
4459 |
330281 |
lbm_char_channel_t *str = lbm_dec_channel(stream); |
|
4460 |
✓✗✗✓ |
330281 |
if (str == NULL || str->state == NULL) { |
4461 |
error_ctx(ENC_SYM_FATAL_ERROR); |
||
4462 |
} else { |
||
4463 |
// the "else" is there to make INFER understand |
||
4464 |
// that this only happens if str is non-null. |
||
4465 |
// the "else" is unnecessary though as |
||
4466 |
// error_ctx longjmps out. |
||
4467 |
330281 |
lbm_channel_reader_close(str); |
|
4468 |
✓✓ | 330281 |
if (lbm_is_symbol(ctx->r)) { |
4469 |
22379 |
lbm_uint sym_val = lbm_dec_sym(ctx->r); |
|
4470 |
✓✓✗✓ |
22379 |
if (sym_val >= TOKENIZER_SYMBOLS_START && |
4471 |
sym_val <= TOKENIZER_SYMBOLS_END) { |
||
4472 |
read_error_ctx(lbm_channel_row(str), lbm_channel_column(str)); |
||
4473 |
} |
||
4474 |
} |
||
4475 |
330281 |
ctx->row0 = -1; |
|
4476 |
330281 |
ctx->row1 = -1; |
|
4477 |
330281 |
ctx->app_cont = true; |
|
4478 |
} |
||
4479 |
330281 |
} |
|
4480 |
|||
4481 |
41272 |
static void cont_wrap_result(eval_context_t *ctx) { |
|
4482 |
lbm_value cell; |
||
4483 |
lbm_value wrapper; |
||
4484 |
41272 |
lbm_pop(&ctx->K, &wrapper); |
|
4485 |
✗✓✗✗ |
41272 |
WITH_GC(cell, lbm_heap_allocate_list_init(2, |
4486 |
wrapper, |
||
4487 |
ctx->r)); |
||
4488 |
41272 |
ctx->r = cell; |
|
4489 |
41272 |
ctx->app_cont = true; |
|
4490 |
41272 |
} |
|
4491 |
|||
4492 |
105086201 |
static void cont_application_start(eval_context_t *ctx) { |
|
4493 |
|||
4494 |
/* sptr[0] = env |
||
4495 |
* sptr[1] = args |
||
4496 |
* ctx->r = function |
||
4497 |
*/ |
||
4498 |
|||
4499 |
✓✓ | 105086201 |
if (lbm_is_symbol(ctx->r)) { |
4500 |
77990383 |
stack_reserve(ctx,1)[0] = lbm_enc_u(0); |
|
4501 |
77990383 |
cont_application_args(ctx); |
|
4502 |
✓✗ | 27095818 |
} else if (lbm_is_cons(ctx->r)) { |
4503 |
27095818 |
lbm_uint *sptr = get_stack_ptr(ctx, 2); |
|
4504 |
27095818 |
lbm_value args = (lbm_value)sptr[1]; |
|
4505 |
✓✓✗✓ ✗ |
27095818 |
switch (get_car(ctx->r)) { |
4506 |
27089434 |
case ENC_SYM_CLOSURE: { |
|
4507 |
lbm_value cl[3]; |
||
4508 |
27089434 |
extract_n(get_cdr(ctx->r), cl, 3); |
|
4509 |
27089434 |
lbm_value arg_env = (lbm_value)sptr[0]; |
|
4510 |
lbm_value arg0, arg_rest; |
||
4511 |
27089434 |
get_car_and_cdr(args, &arg0, &arg_rest); |
|
4512 |
27089434 |
sptr[1] = cl[CLO_BODY]; |
|
4513 |
27089434 |
bool a_nil = lbm_is_symbol_nil(args); |
|
4514 |
27089434 |
bool p_nil = lbm_is_symbol_nil(cl[CLO_PARAMS]); |
|
4515 |
27089434 |
lbm_value *reserved = stack_reserve(ctx, 4); |
|
4516 |
|||
4517 |
✓✓✓✓ |
27089434 |
if (!a_nil && !p_nil) { |
4518 |
26211746 |
reserved[0] = cl[CLO_ENV]; |
|
4519 |
26211746 |
reserved[1] = cl[CLO_PARAMS]; |
|
4520 |
26211746 |
reserved[2] = arg_rest; |
|
4521 |
26211746 |
reserved[3] = CLOSURE_ARGS; |
|
4522 |
26211746 |
ctx->curr_exp = arg0; |
|
4523 |
26211746 |
ctx->curr_env = arg_env; |
|
4524 |
✓✓✓✗ |
877688 |
} else if (a_nil && p_nil) { |
4525 |
// No params, No args |
||
4526 |
317660 |
lbm_stack_drop(&ctx->K, 6); |
|
4527 |
317660 |
ctx->curr_exp = cl[CLO_BODY]; |
|
4528 |
317660 |
ctx->curr_env = cl[CLO_ENV]; |
|
4529 |
✓✗ | 560028 |
} else if (p_nil) { |
4530 |
560028 |
reserved[1] = get_cdr(args); // protect cdr(args) from allocate_binding |
|
4531 |
560028 |
ctx->curr_exp = get_car(args); // protect car(args) from allocate binding |
|
4532 |
560028 |
ctx->curr_env = arg_env; |
|
4533 |
560028 |
lbm_value rest_binder = allocate_binding(ENC_SYM_REST_ARGS, ENC_SYM_NIL, cl[CLO_ENV]); |
|
4534 |
560028 |
reserved[0] = rest_binder; |
|
4535 |
560028 |
reserved[2] = get_car(rest_binder); |
|
4536 |
560028 |
reserved[3] = CLOSURE_ARGS_REST; |
|
4537 |
} else { |
||
4538 |
lbm_set_error_reason((char*)lbm_error_str_num_args); |
||
4539 |
error_at_ctx(ENC_SYM_EERROR, ctx->r); |
||
4540 |
} |
||
4541 |
27089434 |
} break; |
|
4542 |
196 |
case ENC_SYM_CONT:{ |
|
4543 |
/* Continuation created using call-cc. |
||
4544 |
* ((SYM_CONT . cont-array) arg0 ) |
||
4545 |
*/ |
||
4546 |
196 |
lbm_value c = get_cdr(ctx->r); /* should be the continuation array*/ |
|
4547 |
|||
4548 |
✗✓ | 196 |
if (!lbm_is_lisp_array_r(c)) { |
4549 |
error_ctx(ENC_SYM_FATAL_ERROR); |
||
4550 |
} |
||
4551 |
|||
4552 |
196 |
lbm_uint arg_count = lbm_list_length(args); |
|
4553 |
✓✓✗ | 196 |
lbm_value arg = ENC_SYM_NIL; |
4554 |
switch (arg_count) { |
||
4555 |
56 |
case 0: |
|
4556 |
56 |
arg = ENC_SYM_NIL; |
|
4557 |
56 |
break; |
|
4558 |
140 |
case 1: |
|
4559 |
140 |
arg = get_car(args); |
|
4560 |
140 |
break; |
|
4561 |
default: |
||
4562 |
lbm_set_error_reason((char*)lbm_error_str_num_args); |
||
4563 |
error_ctx(ENC_SYM_EERROR); |
||
4564 |
} |
||
4565 |
196 |
lbm_stack_clear(&ctx->K); |
|
4566 |
|||
4567 |
196 |
lbm_array_header_t *arr = assume_array(c); |
|
4568 |
196 |
ctx->K.sp = arr->size / sizeof(lbm_uint); |
|
4569 |
196 |
memcpy(ctx->K.data, arr->data, arr->size); |
|
4570 |
|||
4571 |
lbm_value atomic; |
||
4572 |
196 |
lbm_pop(&ctx->K, &atomic); |
|
4573 |
196 |
is_atomic = atomic ? 1 : 0; |
|
4574 |
|||
4575 |
196 |
ctx->curr_exp = arg; |
|
4576 |
196 |
} break; |
|
4577 |
case ENC_SYM_CONT_SP: { |
||
4578 |
// continuation created using call-cc-unsafe |
||
4579 |
// ((SYM_CONT_SP . stack_ptr) arg0 ) |
||
4580 |
lbm_value c = get_cadr(ctx->r); /* should be the stack_ptr*/ |
||
4581 |
lbm_value atomic = get_cadr(get_cdr(ctx->r)); |
||
4582 |
|||
4583 |
if (!lbm_is_number(c)) { |
||
4584 |
error_ctx(ENC_SYM_FATAL_ERROR); |
||
4585 |
} |
||
4586 |
|||
4587 |
lbm_uint sp = (lbm_uint)lbm_dec_i(c); |
||
4588 |
|||
4589 |
lbm_uint arg_count = lbm_list_length(args); |
||
4590 |
lbm_value arg = ENC_SYM_NIL; |
||
4591 |
switch (arg_count) { |
||
4592 |
case 0: |
||
4593 |
arg = ENC_SYM_NIL; |
||
4594 |
break; |
||
4595 |
case 1: |
||
4596 |
arg = get_car(args); |
||
4597 |
break; |
||
4598 |
default: |
||
4599 |
lbm_set_error_reason((char*)lbm_error_str_num_args); |
||
4600 |
error_ctx(ENC_SYM_EERROR); |
||
4601 |
} |
||
4602 |
if (sp > 0 && sp <= ctx->K.sp && IS_CONTINUATION(ctx->K.data[sp-1])) { |
||
4603 |
is_atomic = atomic ? 1 : 0; // works fine with nil/true |
||
4604 |
ctx->K.sp = sp; |
||
4605 |
ctx->curr_exp = arg; |
||
4606 |
return; |
||
4607 |
} else { |
||
4608 |
error_ctx(ENC_SYM_FATAL_ERROR); |
||
4609 |
} |
||
4610 |
} break; |
||
4611 |
6188 |
case ENC_SYM_MACRO:{ |
|
4612 |
/* |
||
4613 |
* Perform macro expansion. |
||
4614 |
* Macro expansion is really just evaluation in an |
||
4615 |
* environment augmented with the unevaluated expressions passed |
||
4616 |
* as arguments. |
||
4617 |
*/ |
||
4618 |
6188 |
lbm_value env = (lbm_value)sptr[0]; |
|
4619 |
|||
4620 |
6188 |
lbm_value curr_param = get_cadr(ctx->r); |
|
4621 |
6188 |
lbm_value curr_arg = args; |
|
4622 |
6188 |
lbm_value expand_env = env; |
|
4623 |
✓✓✓✗ |
43316 |
while (lbm_is_cons(curr_param) && |
4624 |
18564 |
lbm_is_cons(curr_arg)) { |
|
4625 |
18564 |
lbm_cons_t *param_cell = lbm_ref_cell(curr_param); // already checked that cons. |
|
4626 |
18564 |
lbm_cons_t *arg_cell = lbm_ref_cell(curr_arg); |
|
4627 |
18564 |
lbm_value car_curr_param = param_cell->car; |
|
4628 |
18564 |
lbm_value cdr_curr_param = param_cell->cdr; |
|
4629 |
18564 |
lbm_value car_curr_arg = arg_cell->car; |
|
4630 |
18564 |
lbm_value cdr_curr_arg = arg_cell->cdr; |
|
4631 |
|||
4632 |
18564 |
lbm_value entry = cons_with_gc(car_curr_param, car_curr_arg, expand_env); |
|
4633 |
18564 |
lbm_value aug_env = cons_with_gc(entry, expand_env,ENC_SYM_NIL); |
|
4634 |
18564 |
expand_env = aug_env; |
|
4635 |
|||
4636 |
18564 |
curr_param = cdr_curr_param; |
|
4637 |
18564 |
curr_arg = cdr_curr_arg; |
|
4638 |
} |
||
4639 |
/* Two rounds of evaluation is performed. |
||
4640 |
* First to instantiate the arguments into the macro body. |
||
4641 |
* Second to evaluate the resulting program. |
||
4642 |
*/ |
||
4643 |
6188 |
sptr[1] = EVAL_R; |
|
4644 |
6188 |
lbm_value exp = get_cadr(get_cdr(ctx->r)); |
|
4645 |
6188 |
ctx->curr_exp = exp; |
|
4646 |
6188 |
ctx->curr_env = expand_env; |
|
4647 |
6188 |
} break; |
|
4648 |
default: |
||
4649 |
error_ctx(ENC_SYM_EERROR); |
||
4650 |
} |
||
4651 |
} else { |
||
4652 |
error_ctx(ENC_SYM_EERROR); |
||
4653 |
} |
||
4654 |
} |
||
4655 |
|||
4656 |
6188 |
static void cont_eval_r(eval_context_t* ctx) { |
|
4657 |
lbm_value env; |
||
4658 |
6188 |
lbm_pop(&ctx->K, &env); |
|
4659 |
6188 |
ctx->curr_exp = ctx->r; |
|
4660 |
6188 |
ctx->curr_env = env; |
|
4661 |
6188 |
} |
|
4662 |
|||
4663 |
643734 |
static void cont_progn_var(eval_context_t* ctx) { |
|
4664 |
|||
4665 |
lbm_value key; |
||
4666 |
lbm_value env; |
||
4667 |
|||
4668 |
643734 |
lbm_pop_2(&ctx->K, &key, &env); |
|
4669 |
|||
4670 |
✗✓ | 643734 |
if (fill_binding_location(key, ctx->r, env) < 0) { |
4671 |
lbm_set_error_reason("Incorrect type of name/key in let-binding"); |
||
4672 |
error_at_ctx(ENC_SYM_TERROR, key); |
||
4673 |
} |
||
4674 |
|||
4675 |
643734 |
ctx->app_cont = true; |
|
4676 |
643734 |
} |
|
4677 |
|||
4678 |
1775536 |
static void cont_setq(eval_context_t *ctx) { |
|
4679 |
lbm_value sym; |
||
4680 |
lbm_value env; |
||
4681 |
1775536 |
lbm_pop_2(&ctx->K, &sym, &env); |
|
4682 |
lbm_value res; |
||
4683 |
✗✓✗✗ |
1775536 |
WITH_GC(res, perform_setvar(sym, ctx->r, env)); |
4684 |
1775480 |
ctx->r = res; |
|
4685 |
1775480 |
ctx->app_cont = true; |
|
4686 |
1775480 |
} |
|
4687 |
|||
4688 |
2408 |
lbm_flash_status request_flash_storage_cell(lbm_value val, lbm_value *res) { |
|
4689 |
|||
4690 |
lbm_value flash_cell; |
||
4691 |
2408 |
lbm_flash_status s = lbm_allocate_const_cell(&flash_cell); |
|
4692 |
✗✓ | 2408 |
if (s != LBM_FLASH_WRITE_OK) |
4693 |
return s; |
||
4694 |
2408 |
lbm_value new_val = val; |
|
4695 |
2408 |
new_val &= ~LBM_PTR_VAL_MASK; // clear the value part of the ptr |
|
4696 |
2408 |
new_val |= (flash_cell & LBM_PTR_VAL_MASK); |
|
4697 |
2408 |
new_val |= LBM_PTR_TO_CONSTANT_BIT; |
|
4698 |
2408 |
*res = new_val; |
|
4699 |
2408 |
return s; |
|
4700 |
} |
||
4701 |
|||
4702 |
840 |
static void cont_move_to_flash(eval_context_t *ctx) { |
|
4703 |
|||
4704 |
lbm_value args; |
||
4705 |
840 |
lbm_pop(&ctx->K, &args); |
|
4706 |
|||
4707 |
✓✓ | 840 |
if (lbm_is_symbol_nil(args)) { |
4708 |
// Done looping over arguments. return true. |
||
4709 |
364 |
ctx->r = ENC_SYM_TRUE; |
|
4710 |
364 |
ctx->app_cont = true; |
|
4711 |
840 |
return; |
|
4712 |
} |
||
4713 |
|||
4714 |
lbm_value first_arg, rest; |
||
4715 |
476 |
get_car_and_cdr(args, &first_arg, &rest); |
|
4716 |
|||
4717 |
lbm_value val; |
||
4718 |
✓✗✓✗ |
476 |
if (lbm_is_symbol(first_arg) && lbm_global_env_lookup(&val, first_arg)) { |
4719 |
// Prepare to copy the rest of the arguments when done with first. |
||
4720 |
476 |
lbm_value *rptr = stack_reserve(ctx, 2); |
|
4721 |
476 |
rptr[0] = rest; |
|
4722 |
476 |
rptr[1] = MOVE_TO_FLASH; |
|
4723 |
✓✗ | 476 |
if (lbm_is_ptr(val) && |
4724 |
✓✗ | 476 |
(!(val & LBM_PTR_TO_CONSTANT_BIT))) { |
4725 |
476 |
lbm_value * rptr1 = stack_reserve(ctx, 3); |
|
4726 |
476 |
rptr1[0] = first_arg; |
|
4727 |
476 |
rptr1[1] = SET_GLOBAL_ENV; |
|
4728 |
476 |
rptr1[2] = MOVE_VAL_TO_FLASH_DISPATCH; |
|
4729 |
476 |
ctx->r = val; |
|
4730 |
} |
||
4731 |
476 |
ctx->app_cont = true; |
|
4732 |
476 |
return; |
|
4733 |
} |
||
4734 |
error_ctx(ENC_SYM_EERROR); |
||
4735 |
} |
||
4736 |
|||
4737 |
3388 |
static void cont_move_val_to_flash_dispatch(eval_context_t *ctx) { |
|
4738 |
|||
4739 |
3388 |
lbm_value val = ctx->r; |
|
4740 |
|||
4741 |
✓✓ | 3388 |
if (lbm_is_cons(val)) { // non-constant cons-cell |
4742 |
798 |
lbm_value *rptr = stack_reserve(ctx, 5); |
|
4743 |
798 |
rptr[0] = ENC_SYM_NIL; // fst cell of list |
|
4744 |
798 |
rptr[1] = ENC_SYM_NIL; // last cell of list |
|
4745 |
798 |
rptr[2] = get_cdr(val); |
|
4746 |
798 |
rptr[3] = MOVE_LIST_TO_FLASH; |
|
4747 |
798 |
rptr[4] = MOVE_VAL_TO_FLASH_DISPATCH; |
|
4748 |
798 |
ctx->r = get_car(val); |
|
4749 |
798 |
ctx->app_cont = true; |
|
4750 |
798 |
return; |
|
4751 |
} |
||
4752 |
|||
4753 |
✓✓✗✓ |
2590 |
if (lbm_is_ptr(val) && (val & LBM_PTR_TO_CONSTANT_BIT)) { // constant pointer cons or not. |
4754 |
//ctx->r unchanged |
||
4755 |
ctx->app_cont = true; |
||
4756 |
return; |
||
4757 |
} |
||
4758 |
|||
4759 |
✓✓ | 2590 |
if (lbm_is_ptr(val)) { // something that is not a cons but still a ptr type. |
4760 |
280 |
lbm_cons_t *ref = lbm_ref_cell(val); |
|
4761 |
✓✗ | 280 |
if (lbm_type_of(ref->cdr) == LBM_TYPE_SYMBOL) { |
4762 |
✓✓✓✓ ✗✗ |
280 |
switch (ref->cdr) { |
4763 |
140 |
case ENC_SYM_RAW_I_TYPE: /* fall through */ |
|
4764 |
case ENC_SYM_RAW_U_TYPE: |
||
4765 |
case ENC_SYM_RAW_F_TYPE: { |
||
4766 |
140 |
lbm_value flash_cell = ENC_SYM_NIL; |
|
4767 |
140 |
handle_flash_status(request_flash_storage_cell(val, &flash_cell)); |
|
4768 |
140 |
handle_flash_status(write_const_car(flash_cell, ref->car)); |
|
4769 |
140 |
handle_flash_status(write_const_cdr(flash_cell, ref->cdr)); |
|
4770 |
140 |
ctx->r = flash_cell; |
|
4771 |
140 |
} break; |
|
4772 |
56 |
case ENC_SYM_IND_I_TYPE: /* fall through */ |
|
4773 |
case ENC_SYM_IND_U_TYPE: |
||
4774 |
case ENC_SYM_IND_F_TYPE: { |
||
4775 |
#ifndef LBM64 |
||
4776 |
/* 64 bit values are in lbm mem on 32bit platforms. */ |
||
4777 |
56 |
lbm_uint *lbm_mem_ptr = (lbm_uint*)ref->car; |
|
4778 |
lbm_uint flash_ptr; |
||
4779 |
|||
4780 |
56 |
handle_flash_status(lbm_write_const_raw(lbm_mem_ptr, 2, &flash_ptr)); |
|
4781 |
56 |
lbm_value flash_cell = ENC_SYM_NIL; |
|
4782 |
56 |
handle_flash_status(request_flash_storage_cell(val, &flash_cell)); |
|
4783 |
56 |
handle_flash_status(write_const_car(flash_cell, flash_ptr)); |
|
4784 |
56 |
handle_flash_status(write_const_cdr(flash_cell, ref->cdr)); |
|
4785 |
56 |
ctx->r = flash_cell; |
|
4786 |
#else |
||
4787 |
// There are no indirect types in LBM64 |
||
4788 |
error_ctx(ENC_SYM_FATAL_ERROR); |
||
4789 |
#endif |
||
4790 |
56 |
} break; |
|
4791 |
28 |
case ENC_SYM_LISPARRAY_TYPE: { |
|
4792 |
28 |
lbm_array_header_t *arr = (lbm_array_header_t*)ref->car; |
|
4793 |
28 |
lbm_uint size = arr->size / sizeof(lbm_uint); |
|
4794 |
28 |
lbm_uint flash_addr = 0; |
|
4795 |
28 |
lbm_value *arrdata = (lbm_value *)arr->data; |
|
4796 |
28 |
lbm_value flash_cell = ENC_SYM_NIL; |
|
4797 |
28 |
handle_flash_status(request_flash_storage_cell(val, &flash_cell)); |
|
4798 |
28 |
handle_flash_status(lbm_allocate_const_raw(size, &flash_addr)); |
|
4799 |
28 |
lift_array_flash(flash_cell, |
|
4800 |
false, |
||
4801 |
(char *)flash_addr, |
||
4802 |
arr->size); |
||
4803 |
// Move array contents to flash recursively |
||
4804 |
28 |
lbm_value *rptr = stack_reserve(ctx, 5); |
|
4805 |
28 |
rptr[0] = flash_cell; |
|
4806 |
28 |
rptr[1] = lbm_enc_u(0); |
|
4807 |
28 |
rptr[2] = val; |
|
4808 |
28 |
rptr[3] = MOVE_ARRAY_ELTS_TO_FLASH; |
|
4809 |
28 |
rptr[4] = MOVE_VAL_TO_FLASH_DISPATCH; |
|
4810 |
28 |
ctx->r = arrdata[0]; |
|
4811 |
28 |
ctx->app_cont = true; |
|
4812 |
28 |
return; |
|
4813 |
} |
||
4814 |
56 |
case ENC_SYM_ARRAY_TYPE: { |
|
4815 |
56 |
lbm_array_header_t *arr = (lbm_array_header_t*)ref->car; |
|
4816 |
// arbitrary address: flash_arr. |
||
4817 |
56 |
lbm_uint flash_arr = 0; |
|
4818 |
56 |
handle_flash_status(lbm_write_const_array_padded((uint8_t*)arr->data, arr->size, &flash_arr)); |
|
4819 |
56 |
lbm_value flash_cell = ENC_SYM_NIL; |
|
4820 |
56 |
handle_flash_status(request_flash_storage_cell(val, &flash_cell)); |
|
4821 |
56 |
lift_array_flash(flash_cell, |
|
4822 |
true, |
||
4823 |
(char *)flash_arr, |
||
4824 |
arr->size); |
||
4825 |
56 |
ctx->r = flash_cell; |
|
4826 |
56 |
} break; |
|
4827 |
case ENC_SYM_CHANNEL_TYPE: /* fall through */ |
||
4828 |
case ENC_SYM_CUSTOM_TYPE: |
||
4829 |
lbm_set_error_reason((char *)lbm_error_str_flash_not_possible); |
||
4830 |
error_ctx(ENC_SYM_EERROR); |
||
4831 |
} |
||
4832 |
252 |
} else { |
|
4833 |
error_ctx(ENC_SYM_FATAL_ERROR); |
||
4834 |
} |
||
4835 |
252 |
ctx->app_cont = true; |
|
4836 |
252 |
return; |
|
4837 |
} |
||
4838 |
|||
4839 |
// if no condition matches, nothing happens (id). |
||
4840 |
2310 |
ctx->r = val; |
|
4841 |
2310 |
ctx->app_cont = true; |
|
4842 |
} |
||
4843 |
|||
4844 |
2016 |
static void cont_move_list_to_flash(eval_context_t *ctx) { |
|
4845 |
|||
4846 |
// ctx->r holds the value that should go in car |
||
4847 |
|||
4848 |
2016 |
lbm_value *sptr = get_stack_ptr(ctx, 3); |
|
4849 |
|||
4850 |
2016 |
lbm_value fst = sptr[0]; |
|
4851 |
2016 |
lbm_value lst = sptr[1]; |
|
4852 |
2016 |
lbm_value val = sptr[2]; |
|
4853 |
|||
4854 |
|||
4855 |
2016 |
lbm_value new_lst = ENC_SYM_NIL; |
|
4856 |
// Allocate element ptr storage after storing the element to flash. |
||
4857 |
2016 |
handle_flash_status(request_flash_storage_cell(lbm_enc_cons_ptr(LBM_PTR_NULL), &new_lst)); |
|
4858 |
|||
4859 |
✓✓ | 2016 |
if (lbm_is_symbol_nil(fst)) { |
4860 |
798 |
lst = new_lst; |
|
4861 |
798 |
fst = new_lst; |
|
4862 |
798 |
handle_flash_status(write_const_car(lst, ctx->r)); |
|
4863 |
} else { |
||
4864 |
1218 |
handle_flash_status(write_const_cdr(lst, new_lst)); // low before high |
|
4865 |
1218 |
handle_flash_status(write_const_car(new_lst, ctx->r)); |
|
4866 |
1218 |
lst = new_lst; |
|
4867 |
} |
||
4868 |
|||
4869 |
✓✓ | 2016 |
if (lbm_is_cons(val)) { |
4870 |
1218 |
sptr[0] = fst; |
|
4871 |
1218 |
sptr[1] = lst;//rest_cell; |
|
4872 |
1218 |
sptr[2] = get_cdr(val); |
|
4873 |
1218 |
lbm_value *rptr = stack_reserve(ctx, 2); |
|
4874 |
1218 |
rptr[0] = MOVE_LIST_TO_FLASH; |
|
4875 |
1218 |
rptr[1] = MOVE_VAL_TO_FLASH_DISPATCH; |
|
4876 |
1218 |
ctx->r = get_car(val); |
|
4877 |
} else { |
||
4878 |
798 |
sptr[0] = fst; |
|
4879 |
798 |
sptr[1] = lst; |
|
4880 |
798 |
sptr[2] = CLOSE_LIST_IN_FLASH; |
|
4881 |
798 |
stack_reserve(ctx, 1)[0] = MOVE_VAL_TO_FLASH_DISPATCH; |
|
4882 |
798 |
ctx->r = val; |
|
4883 |
} |
||
4884 |
2016 |
ctx->app_cont = true; |
|
4885 |
2016 |
} |
|
4886 |
|||
4887 |
798 |
static void cont_close_list_in_flash(eval_context_t *ctx) { |
|
4888 |
lbm_value fst; |
||
4889 |
lbm_value lst; |
||
4890 |
798 |
lbm_pop_2(&ctx->K, &lst, &fst); |
|
4891 |
798 |
lbm_value val = ctx->r; |
|
4892 |
798 |
handle_flash_status(write_const_cdr(lst, val)); |
|
4893 |
798 |
ctx->r = fst; |
|
4894 |
798 |
ctx->app_cont = true; |
|
4895 |
798 |
} |
|
4896 |
|||
4897 |
84 |
static void cont_move_array_elts_to_flash(eval_context_t *ctx) { |
|
4898 |
84 |
lbm_value *sptr = get_stack_ptr(ctx, 3); |
|
4899 |
// sptr[2] = source array in RAM |
||
4900 |
// sptr[1] = current index |
||
4901 |
// sptr[0] = target array in flash |
||
4902 |
84 |
lbm_array_header_t *src_arr = assume_array(sptr[2]); |
|
4903 |
84 |
lbm_uint size = src_arr->size / sizeof(lbm_uint); |
|
4904 |
84 |
lbm_value *srcdata = (lbm_value *)src_arr->data; |
|
4905 |
|||
4906 |
84 |
lbm_array_header_t *tgt_arr = assume_array(sptr[0]); |
|
4907 |
84 |
lbm_uint *tgtdata = (lbm_value *)tgt_arr->data; |
|
4908 |
84 |
lbm_uint ix = lbm_dec_as_u32(sptr[1]); |
|
4909 |
84 |
handle_flash_status(lbm_const_write(&tgtdata[ix], ctx->r)); |
|
4910 |
✓✓ | 84 |
if (ix >= size-1) { |
4911 |
28 |
ctx->r = sptr[0]; |
|
4912 |
28 |
lbm_stack_drop(&ctx->K, 3); |
|
4913 |
28 |
ctx->app_cont = true; |
|
4914 |
28 |
return; |
|
4915 |
} |
||
4916 |
56 |
sptr[1] = lbm_enc_u(ix + 1); |
|
4917 |
56 |
lbm_value *rptr = stack_reserve(ctx, 2); |
|
4918 |
56 |
rptr[0] = MOVE_ARRAY_ELTS_TO_FLASH; |
|
4919 |
56 |
rptr[1] = MOVE_VAL_TO_FLASH_DISPATCH; |
|
4920 |
56 |
ctx->r = srcdata[ix+1]; |
|
4921 |
56 |
ctx->app_cont = true; |
|
4922 |
56 |
return; |
|
4923 |
} |
||
4924 |
|||
4925 |
5068 |
static void cont_qq_expand_start(eval_context_t *ctx) { |
|
4926 |
5068 |
lbm_value *rptr = stack_reserve(ctx, 2); |
|
4927 |
5068 |
rptr[0] = ctx->r; |
|
4928 |
5068 |
rptr[1] = QQ_EXPAND; |
|
4929 |
5068 |
ctx->r = ENC_SYM_NIL; |
|
4930 |
5068 |
ctx->app_cont = true; |
|
4931 |
5068 |
} |
|
4932 |
|||
4933 |
10276 |
lbm_value quote_it(lbm_value qquoted) { |
|
4934 |
✓✓✓✗ |
20104 |
if (lbm_is_symbol(qquoted) && |
4935 |
19656 |
lbm_is_special(qquoted)) return qquoted; |
|
4936 |
|||
4937 |
448 |
lbm_value val = cons_with_gc(qquoted, ENC_SYM_NIL, ENC_SYM_NIL); |
|
4938 |
448 |
return cons_with_gc(ENC_SYM_QUOTE, val, ENC_SYM_NIL); |
|
4939 |
} |
||
4940 |
|||
4941 |
37968 |
bool is_append(lbm_value a) { |
|
4942 |
✓✗ | 75768 |
return (lbm_is_cons(a) && |
4943 |
✓✓✓✓ |
75768 |
lbm_is_symbol(get_car(a)) && |
4944 |
37800 |
(get_car(a) == ENC_SYM_APPEND)); |
|
4945 |
} |
||
4946 |
|||
4947 |
63840 |
lbm_value append(lbm_value front, lbm_value back) { |
|
4948 |
✓✓ | 63840 |
if (lbm_is_symbol_nil(front)) return back; |
4949 |
✓✓ | 29456 |
if (lbm_is_symbol_nil(back)) return front; |
4950 |
|||
4951 |
✓✓✓✓ |
30016 |
if (lbm_is_quoted_list(front) && |
4952 |
10388 |
lbm_is_quoted_list(back)) { |
|
4953 |
448 |
lbm_value f = get_cadr(front); |
|
4954 |
448 |
lbm_value b = get_cadr(back); |
|
4955 |
448 |
return quote_it(lbm_list_append(f, b)); |
|
4956 |
} |
||
4957 |
|||
4958 |
✓✓✓✓ |
28728 |
if (is_append(back) && |
4959 |
✓✗ | 9940 |
lbm_is_quoted_list(get_cadr(back)) && |
4960 |
392 |
lbm_is_quoted_list(front)) { |
|
4961 |
392 |
lbm_value ql = get_cadr(back); |
|
4962 |
392 |
lbm_value f = get_cadr(front); |
|
4963 |
392 |
lbm_value b = get_cadr(ql); |
|
4964 |
|||
4965 |
392 |
lbm_value v = lbm_list_append(f, b); |
|
4966 |
392 |
lbm_set_car(get_cdr(ql), v); |
|
4967 |
392 |
return back; |
|
4968 |
} |
||
4969 |
|||
4970 |
✓✓ | 18788 |
if (is_append(back)) { |
4971 |
9156 |
back = get_cdr(back); |
|
4972 |
9156 |
lbm_value new = cons_with_gc(front, back, ENC_SYM_NIL); |
|
4973 |
9156 |
return cons_with_gc(ENC_SYM_APPEND, new, ENC_SYM_NIL); |
|
4974 |
} |
||
4975 |
|||
4976 |
lbm_value t0, t1; |
||
4977 |
|||
4978 |
9632 |
t0 = cons_with_gc(back, ENC_SYM_NIL, front); |
|
4979 |
9632 |
t1 = cons_with_gc(front, t0, ENC_SYM_NIL); |
|
4980 |
9632 |
return cons_with_gc(ENC_SYM_APPEND, t1, ENC_SYM_NIL); |
|
4981 |
} |
||
4982 |
|||
4983 |
// //////////////////////////////////////////////////////////// |
||
4984 |
// Quasiquotation expansion that takes place at read time |
||
4985 |
// and is based on the paper by Bawden "Quasiquotation in lisp". |
||
4986 |
// Bawden, Alan. "Quasiquotation in Lisp." PEPM. 1999. |
||
4987 |
// |
||
4988 |
// cont_qq_expand and cont_qq_expand_list corresponds (mostly) to |
||
4989 |
// qq-expand and qq-expand-list in the paper. |
||
4990 |
// One difference is that the case where a backquote is nested |
||
4991 |
// inside of a backqoute is handled via the recursion through the |
||
4992 |
// reader. |
||
4993 |
|||
4994 |
/* Bawden's qq-expand implementation |
||
4995 |
(define (qq-expand x) |
||
4996 |
(cond ((tag-comma? x) |
||
4997 |
(tag-data x)) |
||
4998 |
((tag-comma-atsign? x) |
||
4999 |
(error "Illegal")) |
||
5000 |
((tag-backquote? x) |
||
5001 |
(qq-expand |
||
5002 |
(qq-expand (tag-data x)))) |
||
5003 |
((pair? x) |
||
5004 |
`(append |
||
5005 |
,(qq-expand-list (car x)) |
||
5006 |
,(qq-expand (cdr x)))) |
||
5007 |
(else `',x))) |
||
5008 |
*/ |
||
5009 |
34524 |
static void cont_qq_expand(eval_context_t *ctx) { |
|
5010 |
lbm_value qquoted; |
||
5011 |
34524 |
lbm_pop(&ctx->K, &qquoted); |
|
5012 |
|||
5013 |
✓✓ | 34524 |
switch(lbm_type_of(qquoted)) { |
5014 |
24696 |
case LBM_TYPE_CONS: { |
|
5015 |
24696 |
lbm_value car_val = get_car(qquoted); |
|
5016 |
24696 |
lbm_value cdr_val = get_cdr(qquoted); |
|
5017 |
✓✓✓✓ |
24696 |
if (lbm_type_of(car_val) == LBM_TYPE_SYMBOL && |
5018 |
car_val == ENC_SYM_COMMA) { |
||
5019 |
28 |
ctx->r = append(ctx->r, get_car(cdr_val)); |
|
5020 |
28 |
ctx->app_cont = true; |
|
5021 |
✓✓✗✓ |
24668 |
} else if (lbm_type_of(car_val) == LBM_TYPE_SYMBOL && |
5022 |
car_val == ENC_SYM_COMMAAT) { |
||
5023 |
lbm_set_error_reason((char*)lbm_error_str_qq_expand); |
||
5024 |
error_at_ctx(ENC_SYM_RERROR, qquoted); |
||
5025 |
} else { |
||
5026 |
24668 |
lbm_value *rptr = stack_reserve(ctx, 6); |
|
5027 |
24668 |
rptr[0] = ctx->r; |
|
5028 |
24668 |
rptr[1] = QQ_APPEND; |
|
5029 |
24668 |
rptr[2] = cdr_val; |
|
5030 |
24668 |
rptr[3] = QQ_EXPAND; |
|
5031 |
24668 |
rptr[4] = car_val; |
|
5032 |
24668 |
rptr[5] = QQ_EXPAND_LIST; |
|
5033 |
24668 |
ctx->app_cont = true; |
|
5034 |
24668 |
ctx->r = ENC_SYM_NIL; |
|
5035 |
} |
||
5036 |
|||
5037 |
24696 |
} break; |
|
5038 |
9828 |
default: { |
|
5039 |
9828 |
lbm_value res = quote_it(qquoted); |
|
5040 |
9828 |
ctx->r = append(ctx->r, res); |
|
5041 |
9828 |
ctx->app_cont = true; |
|
5042 |
} |
||
5043 |
} |
||
5044 |
34524 |
} |
|
5045 |
|||
5046 |
29456 |
static void cont_qq_append(eval_context_t *ctx) { |
|
5047 |
lbm_value head; |
||
5048 |
29456 |
lbm_pop(&ctx->K, &head); |
|
5049 |
29456 |
ctx->r = append(head, ctx->r); |
|
5050 |
29456 |
ctx->app_cont = true; |
|
5051 |
29456 |
} |
|
5052 |
|||
5053 |
/* Bawden's qq-expand-list implementation |
||
5054 |
(define (qq-expand-list x) |
||
5055 |
(cond ((tag-comma? x) |
||
5056 |
`(list ,(tag-data x))) |
||
5057 |
((tag-comma-atsign? x) |
||
5058 |
(tag-data x)) |
||
5059 |
((tag-backquote? x) |
||
5060 |
(qq-expand-list |
||
5061 |
(qq-expand (tag-data x)))) |
||
5062 |
((pair? x) |
||
5063 |
`(list |
||
5064 |
(append |
||
5065 |
,(qq-expand-list (car x)) |
||
5066 |
,(qq-expand (cdr x))))) |
||
5067 |
(else `'(,x)))) |
||
5068 |
*/ |
||
5069 |
|||
5070 |
29456 |
static void cont_qq_expand_list(eval_context_t* ctx) { |
|
5071 |
lbm_value l; |
||
5072 |
29456 |
lbm_pop(&ctx->K, &l); |
|
5073 |
|||
5074 |
29456 |
ctx->app_cont = true; |
|
5075 |
✓✓ | 29456 |
switch(lbm_type_of(l)) { |
5076 |
18844 |
case LBM_TYPE_CONS: { |
|
5077 |
18844 |
lbm_value car_val = get_car(l); |
|
5078 |
18844 |
lbm_value cdr_val = get_cdr(l); |
|
5079 |
✓✗✓✓ |
18844 |
if (lbm_type_of(car_val) == LBM_TYPE_SYMBOL && |
5080 |
car_val == ENC_SYM_COMMA) { |
||
5081 |
13916 |
lbm_value tl = cons_with_gc(get_car(cdr_val), ENC_SYM_NIL, ENC_SYM_NIL); |
|
5082 |
13916 |
lbm_value tmp = cons_with_gc(ENC_SYM_LIST, tl, ENC_SYM_NIL); |
|
5083 |
13916 |
ctx->r = append(ctx->r, tmp); |
|
5084 |
14056 |
return; |
|
5085 |
✓✗✓✓ |
4928 |
} else if (lbm_type_of(car_val) == LBM_TYPE_SYMBOL && |
5086 |
car_val == ENC_SYM_COMMAAT) { |
||
5087 |
140 |
lbm_value cadr_val = lbm_car(cdr_val); |
|
5088 |
140 |
ctx->r = cadr_val; |
|
5089 |
140 |
return; |
|
5090 |
} else { |
||
5091 |
4788 |
lbm_value *rptr = stack_reserve(ctx, 7); |
|
5092 |
4788 |
rptr[0] = QQ_LIST; |
|
5093 |
4788 |
rptr[1] = ctx->r; |
|
5094 |
4788 |
rptr[2] = QQ_APPEND; |
|
5095 |
4788 |
rptr[3] = cdr_val; |
|
5096 |
4788 |
rptr[4] = QQ_EXPAND; |
|
5097 |
4788 |
rptr[5] = car_val; |
|
5098 |
4788 |
rptr[6] = QQ_EXPAND_LIST; |
|
5099 |
4788 |
ctx->r = ENC_SYM_NIL; |
|
5100 |
} |
||
5101 |
|||
5102 |
4788 |
} break; |
|
5103 |
10612 |
default: { |
|
5104 |
10612 |
lbm_value a_list = cons_with_gc(l, ENC_SYM_NIL, ENC_SYM_NIL); |
|
5105 |
10612 |
lbm_value tl = cons_with_gc(a_list, ENC_SYM_NIL, ENC_SYM_NIL); |
|
5106 |
10612 |
lbm_value tmp = cons_with_gc(ENC_SYM_QUOTE, tl, ENC_SYM_NIL); |
|
5107 |
10612 |
ctx->r = append(ctx->r, tmp); |
|
5108 |
} |
||
5109 |
} |
||
5110 |
} |
||
5111 |
|||
5112 |
4788 |
static void cont_qq_list(eval_context_t *ctx) { |
|
5113 |
4788 |
lbm_value val = ctx->r; |
|
5114 |
4788 |
lbm_value apnd_app = cons_with_gc(val, ENC_SYM_NIL, ENC_SYM_NIL); |
|
5115 |
4788 |
lbm_value tmp = cons_with_gc(ENC_SYM_LIST, apnd_app, ENC_SYM_NIL); |
|
5116 |
4788 |
ctx->r = tmp; |
|
5117 |
4788 |
ctx->app_cont = true; |
|
5118 |
4788 |
} |
|
5119 |
|||
5120 |
84 |
static void cont_kill(eval_context_t *ctx) { |
|
5121 |
(void) ctx; |
||
5122 |
84 |
ok_ctx(); |
|
5123 |
84 |
} |
|
5124 |
|||
5125 |
69716 |
static void cont_pop_reader_flags(eval_context_t *ctx) { |
|
5126 |
lbm_value flags; |
||
5127 |
69716 |
lbm_pop(&ctx->K, &flags); |
|
5128 |
69716 |
ctx->flags = ctx->flags & ~EVAL_CPS_CONTEXT_READER_FLAGS_MASK; |
|
5129 |
69716 |
ctx->flags |= (lbm_dec_as_u32(flags) & EVAL_CPS_CONTEXT_READER_FLAGS_MASK); |
|
5130 |
// r is unchanged. |
||
5131 |
69716 |
ctx->app_cont = true; |
|
5132 |
69716 |
} |
|
5133 |
|||
5134 |
8120 |
static void cont_exception_handler(eval_context_t *ctx) { |
|
5135 |
8120 |
lbm_value *sptr = pop_stack_ptr(ctx, 2); |
|
5136 |
8120 |
lbm_value retval = sptr[0]; |
|
5137 |
8120 |
lbm_value flags = sptr[1]; |
|
5138 |
8120 |
lbm_set_car(get_cdr(retval), ctx->r); |
|
5139 |
8120 |
ctx->flags = (uint32_t)flags; |
|
5140 |
8120 |
ctx->r = retval; |
|
5141 |
8120 |
ctx->app_cont = true; |
|
5142 |
8120 |
} |
|
5143 |
|||
5144 |
// cont_recv_to: |
||
5145 |
// |
||
5146 |
// s[sp-1] = patterns |
||
5147 |
// |
||
5148 |
// ctx->r = timeout value |
||
5149 |
196 |
static void cont_recv_to(eval_context_t *ctx) { |
|
5150 |
✓✗ | 196 |
if (lbm_is_number(ctx->r)) { |
5151 |
196 |
lbm_value *sptr = get_stack_ptr(ctx, 1); // patterns at sptr[0] |
|
5152 |
196 |
float timeout_time = lbm_dec_as_float(ctx->r); |
|
5153 |
✓✓ | 196 |
if (timeout_time < 0.0) timeout_time = 0.0; // clamp. |
5154 |
✓✓ | 196 |
if (ctx->num_mail > 0) { |
5155 |
lbm_value e; |
||
5156 |
56 |
lbm_value new_env = ctx->curr_env; |
|
5157 |
#ifdef LBM_ALWAYS_GC |
||
5158 |
gc(); |
||
5159 |
#endif |
||
5160 |
56 |
int n = find_match(sptr[0], ctx->mailbox, ctx->num_mail, &e, &new_env); |
|
5161 |
✗✓ | 56 |
if (n == FM_NEED_GC) { |
5162 |
gc(); |
||
5163 |
new_env = ctx->curr_env; |
||
5164 |
n = find_match(sptr[0], ctx->mailbox, ctx->num_mail, &e, &new_env); |
||
5165 |
if (n == FM_NEED_GC) error_ctx(ENC_SYM_MERROR); |
||
5166 |
} |
||
5167 |
✗✓ | 56 |
if (n == FM_PATTERN_ERROR) { |
5168 |
lbm_set_error_reason("Incorrect pattern format for recv"); |
||
5169 |
error_at_ctx(ENC_SYM_EERROR, sptr[0]); |
||
5170 |
✓✗ | 56 |
} else if (n >= 0) { // match |
5171 |
56 |
mailbox_remove_mail(ctx, (lbm_uint)n); |
|
5172 |
56 |
ctx->curr_env = new_env; |
|
5173 |
56 |
ctx->curr_exp = e; |
|
5174 |
56 |
lbm_stack_drop(&ctx->K, 1); |
|
5175 |
56 |
return; |
|
5176 |
} |
||
5177 |
} |
||
5178 |
// If no mail or no match, go to sleep |
||
5179 |
140 |
lbm_uint *rptr = stack_reserve(ctx,2); |
|
5180 |
140 |
rptr[0] = ctx->r; |
|
5181 |
140 |
rptr[1] = RECV_TO_RETRY; |
|
5182 |
140 |
block_current_ctx(LBM_THREAD_STATE_RECV_TO,S_TO_US(timeout_time),true); |
|
5183 |
} else { |
||
5184 |
error_ctx(ENC_SYM_TERROR); |
||
5185 |
} |
||
5186 |
} |
||
5187 |
|||
5188 |
// cont_recv_to_retry |
||
5189 |
// |
||
5190 |
// s[sp-2] = patterns |
||
5191 |
// s[sp-1] = timeout value |
||
5192 |
// |
||
5193 |
// ctx->r = nonsense | timeout symbol |
||
5194 |
140 |
static void cont_recv_to_retry(eval_context_t *ctx) { |
|
5195 |
140 |
lbm_value *sptr = get_stack_ptr(ctx, 2); //sptr[0] = patterns, sptr[1] = timeout |
|
5196 |
|||
5197 |
✓✗ | 140 |
if (ctx->num_mail > 0) { |
5198 |
lbm_value e; |
||
5199 |
140 |
lbm_value new_env = ctx->curr_env; |
|
5200 |
#ifdef LBM_ALWAYS_GC |
||
5201 |
gc(); |
||
5202 |
#endif |
||
5203 |
140 |
int n = find_match(sptr[0], ctx->mailbox, ctx->num_mail, &e, &new_env); |
|
5204 |
✗✓ | 140 |
if (n == FM_NEED_GC) { |
5205 |
gc(); |
||
5206 |
new_env = ctx->curr_env; |
||
5207 |
n = find_match(sptr[0], ctx->mailbox, ctx->num_mail, &e, &new_env); |
||
5208 |
if (n == FM_NEED_GC) error_ctx(ENC_SYM_MERROR); |
||
5209 |
} |
||
5210 |
✗✓ | 140 |
if (n == FM_PATTERN_ERROR) { |
5211 |
lbm_set_error_reason("Incorrect pattern format for recv"); |
||
5212 |
error_at_ctx(ENC_SYM_EERROR, sptr[0]); |
||
5213 |
✓✓ | 140 |
} else if (n >= 0) { // match |
5214 |
56 |
mailbox_remove_mail(ctx, (lbm_uint)n); |
|
5215 |
56 |
ctx->curr_env = new_env; |
|
5216 |
56 |
ctx->curr_exp = e; |
|
5217 |
56 |
lbm_stack_drop(&ctx->K, 2); |
|
5218 |
56 |
return; |
|
5219 |
} |
||
5220 |
} |
||
5221 |
|||
5222 |
// No message matched but the timeout was reached. |
||
5223 |
// This is like having a recv-to with no case that matches |
||
5224 |
// the timeout symbol. |
||
5225 |
✓✗ | 84 |
if (ctx->r == ENC_SYM_TIMEOUT) { |
5226 |
84 |
lbm_stack_drop(&ctx->K, 2); |
|
5227 |
84 |
ctx->app_cont = true; |
|
5228 |
84 |
return; |
|
5229 |
} |
||
5230 |
|||
5231 |
stack_reserve(ctx,1)[0] = RECV_TO_RETRY; |
||
5232 |
reblock_current_ctx(LBM_THREAD_STATE_RECV_TO,true); |
||
5233 |
} |
||
5234 |
|||
5235 |
|||
5236 |
/*********************************************************/ |
||
5237 |
/* Continuations table */ |
||
5238 |
typedef void (*cont_fun)(eval_context_t *); |
||
5239 |
|||
5240 |
static const cont_fun continuations[NUM_CONTINUATIONS] = |
||
5241 |
{ advance_ctx, // CONT_DONE |
||
5242 |
cont_set_global_env, |
||
5243 |
cont_bind_to_key_rest, |
||
5244 |
cont_if, |
||
5245 |
cont_progn_rest, |
||
5246 |
cont_application_args, |
||
5247 |
cont_and, |
||
5248 |
cont_or, |
||
5249 |
cont_wait, |
||
5250 |
cont_match, |
||
5251 |
cont_application_start, |
||
5252 |
cont_eval_r, |
||
5253 |
cont_resume, |
||
5254 |
cont_closure_application_args, |
||
5255 |
cont_exit_atomic, |
||
5256 |
cont_read_next_token, |
||
5257 |
cont_read_append_continue, |
||
5258 |
cont_read_eval_continue, |
||
5259 |
cont_read_expect_closepar, |
||
5260 |
cont_read_dot_terminate, |
||
5261 |
cont_read_done, |
||
5262 |
cont_read_start_array, |
||
5263 |
cont_read_append_array, |
||
5264 |
cont_map, |
||
5265 |
cont_match_guard, |
||
5266 |
cont_terminate, |
||
5267 |
cont_progn_var, |
||
5268 |
cont_setq, |
||
5269 |
cont_move_to_flash, |
||
5270 |
cont_move_val_to_flash_dispatch, |
||
5271 |
cont_move_list_to_flash, |
||
5272 |
cont_close_list_in_flash, |
||
5273 |
cont_qq_expand_start, |
||
5274 |
cont_qq_expand, |
||
5275 |
cont_qq_append, |
||
5276 |
cont_qq_expand_list, |
||
5277 |
cont_qq_list, |
||
5278 |
cont_kill, |
||
5279 |
cont_loop, |
||
5280 |
cont_loop_condition, |
||
5281 |
cont_merge_rest, |
||
5282 |
cont_merge_layer, |
||
5283 |
cont_closure_args_rest, |
||
5284 |
cont_move_array_elts_to_flash, |
||
5285 |
cont_pop_reader_flags, |
||
5286 |
cont_exception_handler, |
||
5287 |
cont_recv_to, |
||
5288 |
cont_wrap_result, |
||
5289 |
cont_recv_to_retry, |
||
5290 |
}; |
||
5291 |
|||
5292 |
/*********************************************************/ |
||
5293 |
/* Evaluators lookup table (special forms) */ |
||
5294 |
typedef void (*evaluator_fun)(eval_context_t *); |
||
5295 |
|||
5296 |
static const evaluator_fun evaluators[] = |
||
5297 |
{ |
||
5298 |
eval_quote, |
||
5299 |
eval_define, |
||
5300 |
eval_progn, |
||
5301 |
eval_lambda, |
||
5302 |
eval_if, |
||
5303 |
eval_let, |
||
5304 |
eval_and, |
||
5305 |
eval_or, |
||
5306 |
eval_match, |
||
5307 |
eval_receive, |
||
5308 |
eval_receive_timeout, |
||
5309 |
eval_callcc, |
||
5310 |
eval_atomic, |
||
5311 |
eval_selfevaluating, // macro |
||
5312 |
eval_selfevaluating, // cont |
||
5313 |
eval_selfevaluating, // closure |
||
5314 |
eval_cond, |
||
5315 |
eval_app_cont, |
||
5316 |
eval_var, |
||
5317 |
eval_setq, |
||
5318 |
eval_move_to_flash, |
||
5319 |
eval_loop, |
||
5320 |
eval_trap, |
||
5321 |
eval_call_cc_unsafe, |
||
5322 |
eval_selfevaluating, // cont_sp |
||
5323 |
}; |
||
5324 |
|||
5325 |
|||
5326 |
/*********************************************************/ |
||
5327 |
/* Evaluator step function */ |
||
5328 |
|||
5329 |
912105056 |
static void evaluation_step(void){ |
|
5330 |
912105056 |
eval_context_t *ctx = ctx_running; |
|
5331 |
#ifdef VISUALIZE_HEAP |
||
5332 |
heap_vis_gen_image(); |
||
5333 |
#endif |
||
5334 |
|||
5335 |
✓✓ | 912105056 |
if (ctx->app_cont) { |
5336 |
lbm_value k; |
||
5337 |
424259771 |
lbm_pop(&ctx->K, &k); |
|
5338 |
424259771 |
ctx->app_cont = false; |
|
5339 |
|||
5340 |
424259771 |
lbm_uint decoded_k = DEC_CONTINUATION(k); |
|
5341 |
// If app_cont is true, then top of stack must be a valid continuation! |
||
5342 |
✓✗ | 424259771 |
if (decoded_k < NUM_CONTINUATIONS) { |
5343 |
424259771 |
continuations[decoded_k](ctx); |
|
5344 |
} else { |
||
5345 |
error_ctx(ENC_SYM_FATAL_ERROR); |
||
5346 |
} |
||
5347 |
424251783 |
return; |
|
5348 |
} |
||
5349 |
|||
5350 |
✓✓ | 487845285 |
if (lbm_is_symbol(ctx->curr_exp)) { |
5351 |
224402016 |
eval_symbol(ctx); |
|
5352 |
224401932 |
return; |
|
5353 |
} |
||
5354 |
✓✓ | 263443269 |
if (lbm_is_cons(ctx->curr_exp)) { |
5355 |
168914971 |
lbm_cons_t *cell = lbm_ref_cell(ctx->curr_exp); |
|
5356 |
168914971 |
lbm_value h = cell->car; |
|
5357 |
✓✓✓✓ |
168914971 |
if (lbm_is_symbol(h) && ((h & ENC_SPECIAL_FORMS_MASK) == ENC_SPECIAL_FORMS_BIT)) { |
5358 |
63829050 |
lbm_uint eval_index = lbm_dec_sym(h) & SPECIAL_FORMS_INDEX_MASK; |
|
5359 |
63829050 |
evaluators[eval_index](ctx); |
|
5360 |
63828966 |
return; |
|
5361 |
} |
||
5362 |
/* |
||
5363 |
* At this point head can be anything. It should evaluate |
||
5364 |
* into a form that can be applied (closure, symbol, ...) though. |
||
5365 |
*/ |
||
5366 |
105085921 |
lbm_value *reserved = stack_reserve(ctx, 3); |
|
5367 |
105085921 |
reserved[0] = ctx->curr_env; // INFER: stack_reserve aborts context if error. |
|
5368 |
105085921 |
reserved[1] = cell->cdr; |
|
5369 |
105085921 |
reserved[2] = APPLICATION_START; |
|
5370 |
105085921 |
ctx->curr_exp = h; // evaluate the function |
|
5371 |
105085921 |
return; |
|
5372 |
} |
||
5373 |
|||
5374 |
94528298 |
eval_selfevaluating(ctx); |
|
5375 |
94528298 |
return; |
|
5376 |
} |
||
5377 |
|||
5378 |
|||
5379 |
// Reset has a built in pause. |
||
5380 |
// so after reset, continue. |
||
5381 |
void lbm_reset_eval(void) { |
||
5382 |
eval_cps_next_state_arg = 0; |
||
5383 |
eval_cps_next_state = EVAL_CPS_STATE_RESET; |
||
5384 |
if (eval_cps_next_state != eval_cps_run_state) eval_cps_state_changed = true; |
||
5385 |
} |
||
5386 |
|||
5387 |
21580 |
void lbm_pause_eval(void ) { |
|
5388 |
21580 |
eval_cps_next_state_arg = 0; |
|
5389 |
21580 |
eval_cps_next_state = EVAL_CPS_STATE_PAUSED; |
|
5390 |
✓✗ | 21580 |
if (eval_cps_next_state != eval_cps_run_state) eval_cps_state_changed = true; |
5391 |
21580 |
} |
|
5392 |
|||
5393 |
21588 |
void lbm_pause_eval_with_gc(uint32_t num_free) { |
|
5394 |
21588 |
eval_cps_next_state_arg = num_free; |
|
5395 |
21588 |
eval_cps_next_state = EVAL_CPS_STATE_PAUSED; |
|
5396 |
✓✗ | 21588 |
if (eval_cps_next_state != eval_cps_run_state) eval_cps_state_changed = true; |
5397 |
21588 |
} |
|
5398 |
|||
5399 |
21588 |
void lbm_continue_eval(void) { |
|
5400 |
21588 |
eval_cps_next_state = EVAL_CPS_STATE_RUNNING; |
|
5401 |
✓✗ | 21588 |
if (eval_cps_next_state != eval_cps_run_state) eval_cps_state_changed = true; |
5402 |
21588 |
} |
|
5403 |
|||
5404 |
void lbm_kill_eval(void) { |
||
5405 |
eval_cps_next_state = EVAL_CPS_STATE_KILL; |
||
5406 |
if (eval_cps_next_state != eval_cps_run_state) eval_cps_state_changed = true; |
||
5407 |
} |
||
5408 |
|||
5409 |
146089 |
uint32_t lbm_get_eval_state(void) { |
|
5410 |
146089 |
return eval_cps_run_state; |
|
5411 |
} |
||
5412 |
|||
5413 |
// Only unblocks threads that are unblockable. |
||
5414 |
// A sleeping thread is not unblockable. |
||
5415 |
84 |
static void handle_event_unblock_ctx(lbm_cid cid, lbm_value v) { |
|
5416 |
84 |
eval_context_t *found = NULL; |
|
5417 |
84 |
mutex_lock(&qmutex); |
|
5418 |
|||
5419 |
84 |
found = lookup_ctx_nm(&blocked, cid); |
|
5420 |
✓✗✓✗ |
84 |
if (found && LBM_IS_STATE_UNBLOCKABLE(found->state)){ |
5421 |
84 |
drop_ctx_nm(&blocked,found); |
|
5422 |
✓✓ | 84 |
if (lbm_is_error(v)) { |
5423 |
28 |
get_stack_ptr(found, 1)[0] = TERMINATE; // replace TOS |
|
5424 |
28 |
found->app_cont = true; |
|
5425 |
} |
||
5426 |
84 |
found->r = v; |
|
5427 |
84 |
found->state = LBM_THREAD_STATE_READY; |
|
5428 |
84 |
enqueue_ctx_nm(&queue,found); |
|
5429 |
} |
||
5430 |
84 |
mutex_unlock(&qmutex); |
|
5431 |
84 |
} |
|
5432 |
|||
5433 |
static void handle_event_define(lbm_value key, lbm_value val) { |
||
5434 |
lbm_uint dec_key = lbm_dec_sym(key); |
||
5435 |
lbm_uint ix_key = dec_key & GLOBAL_ENV_MASK; |
||
5436 |
lbm_value *global_env = lbm_get_global_env(); |
||
5437 |
lbm_uint orig_env = global_env[ix_key]; |
||
5438 |
lbm_value new_env; |
||
5439 |
// A key is a symbol and should not need to be remembered. |
||
5440 |
WITH_GC(new_env, lbm_env_set(orig_env,key,val)); |
||
5441 |
|||
5442 |
global_env[ix_key] = new_env; |
||
5443 |
} |
||
5444 |
|||
5445 |
7017 |
static lbm_value get_event_value(lbm_event_t *e) { |
|
5446 |
lbm_value v; |
||
5447 |
✓✗ | 7017 |
if (e->buf_len > 0) { |
5448 |
lbm_flat_value_t fv; |
||
5449 |
7017 |
fv.buf = (uint8_t*)e->buf_ptr; |
|
5450 |
7017 |
fv.buf_size = e->buf_len; |
|
5451 |
7017 |
fv.buf_pos = 0; |
|
5452 |
✗✓ | 7017 |
if (!lbm_unflatten_value(&fv, &v)) { |
5453 |
lbm_set_flags(LBM_FLAG_HANDLER_EVENT_DELIVERY_FAILED); |
||
5454 |
v = ENC_SYM_EERROR; |
||
5455 |
} |
||
5456 |
// Free the flat value buffer. GC is unaware of its existence. |
||
5457 |
7017 |
lbm_free(fv.buf); |
|
5458 |
} else { |
||
5459 |
v = (lbm_value)e->buf_ptr; |
||
5460 |
} |
||
5461 |
7017 |
return v; |
|
5462 |
} |
||
5463 |
|||
5464 |
93328586 |
static void process_events(void) { |
|
5465 |
|||
5466 |
✗✓ | 93328586 |
if (!lbm_events) { |
5467 |
return; |
||
5468 |
} |
||
5469 |
|||
5470 |
lbm_event_t e; |
||
5471 |
✓✓ | 186664189 |
while (lbm_event_pop(&e)) { |
5472 |
7017 |
lbm_value event_val = get_event_value(&e); |
|
5473 |
✓✗✓✗ ✗ |
7017 |
switch(e.type) { |
5474 |
84 |
case LBM_EVENT_UNBLOCK_CTX: |
|
5475 |
84 |
handle_event_unblock_ctx((lbm_cid)e.parameter, event_val); |
|
5476 |
84 |
break; |
|
5477 |
case LBM_EVENT_DEFINE: |
||
5478 |
handle_event_define((lbm_value)e.parameter, event_val); |
||
5479 |
break; |
||
5480 |
6933 |
case LBM_EVENT_FOR_HANDLER: |
|
5481 |
✓✗ | 6933 |
if (lbm_event_handler_pid >= 0) { |
5482 |
6933 |
lbm_find_receiver_and_send(lbm_event_handler_pid, event_val); |
|
5483 |
} |
||
5484 |
6933 |
break; |
|
5485 |
case LBM_EVENT_RUN_USER_CALLBACK: |
||
5486 |
user_callback((void*)e.parameter); |
||
5487 |
break; |
||
5488 |
} |
||
5489 |
93335603 |
} |
|
5490 |
} |
||
5491 |
|||
5492 |
/* eval_cps_run can be paused |
||
5493 |
I think it would be better use a mailbox for |
||
5494 |
communication between other threads and the run_eval |
||
5495 |
but for now a set of variables will be used. */ |
||
5496 |
21588 |
void lbm_run_eval(void){ |
|
5497 |
|||
5498 |
✗✓ | 21588 |
if (setjmp(critical_error_jmp_buf) > 0) { |
5499 |
printf_callback("GC stack overflow!\n"); |
||
5500 |
critical_error_callback(); |
||
5501 |
// terminate evaluation thread. |
||
5502 |
return; |
||
5503 |
} |
||
5504 |
|||
5505 |
21588 |
setjmp(error_jmp_buf); |
|
5506 |
|||
5507 |
✓✗ | 113668 |
while (eval_running) { |
5508 |
✓✓✓✗ |
128493 |
if (eval_cps_state_changed || eval_cps_run_state == EVAL_CPS_STATE_PAUSED) { |
5509 |
98787 |
eval_cps_state_changed = false; |
|
5510 |
✗✓✗✓ |
98787 |
switch (eval_cps_next_state) { |
5511 |
case EVAL_CPS_STATE_RESET: |
||
5512 |
if (eval_cps_run_state != EVAL_CPS_STATE_RESET) { |
||
5513 |
is_atomic = false; |
||
5514 |
blocked.first = NULL; |
||
5515 |
blocked.last = NULL; |
||
5516 |
queue.first = NULL; |
||
5517 |
queue.last = NULL; |
||
5518 |
ctx_running = NULL; |
||
5519 |
eval_steps_quota = eval_steps_refill; |
||
5520 |
eval_cps_run_state = EVAL_CPS_STATE_RESET; |
||
5521 |
if (blocking_extension) { |
||
5522 |
blocking_extension = false; |
||
5523 |
mutex_unlock(&blocking_extension_mutex); |
||
5524 |
} |
||
5525 |
} |
||
5526 |
usleep_callback(EVAL_CPS_MIN_SLEEP); |
||
5527 |
continue; |
||
5528 |
77199 |
case EVAL_CPS_STATE_PAUSED: |
|
5529 |
✓✓ | 77199 |
if (eval_cps_run_state != EVAL_CPS_STATE_PAUSED) { |
5530 |
✗✓ | 43168 |
if (lbm_heap_num_free() < eval_cps_next_state_arg) { |
5531 |
gc(); |
||
5532 |
} |
||
5533 |
43168 |
eval_cps_next_state_arg = 0; |
|
5534 |
43168 |
eval_cps_run_state = EVAL_CPS_STATE_PAUSED; |
|
5535 |
} |
||
5536 |
77199 |
usleep_callback(EVAL_CPS_MIN_SLEEP); |
|
5537 |
40794 |
continue; |
|
5538 |
case EVAL_CPS_STATE_KILL: |
||
5539 |
eval_cps_run_state = EVAL_CPS_STATE_DEAD; |
||
5540 |
eval_running = false; |
||
5541 |
continue; |
||
5542 |
21588 |
default: // running state |
|
5543 |
21588 |
eval_cps_run_state = eval_cps_next_state; |
|
5544 |
21588 |
break; |
|
5545 |
} |
||
5546 |
29706 |
} |
|
5547 |
while (true) { |
||
5548 |
✓✓✓✓ |
1005477472 |
if (eval_steps_quota && ctx_running) { |
5549 |
912105056 |
eval_steps_quota--; |
|
5550 |
912105056 |
evaluation_step(); |
|
5551 |
} else { |
||
5552 |
✓✓ | 93372416 |
if (eval_cps_state_changed) break; |
5553 |
93329286 |
eval_steps_quota = eval_steps_refill; |
|
5554 |
✓✓ | 93329286 |
if (!is_atomic) { |
5555 |
✓✓ | 93328586 |
if (gc_requested) { |
5556 |
96 |
gc(); |
|
5557 |
} |
||
5558 |
93328586 |
process_events(); |
|
5559 |
93328586 |
mutex_lock(&qmutex); |
|
5560 |
✓✓ | 93328586 |
if (ctx_running) { |
5561 |
91165199 |
enqueue_ctx_nm(&queue, ctx_running); |
|
5562 |
91165199 |
ctx_running = NULL; |
|
5563 |
} |
||
5564 |
93328586 |
wake_up_ctxs_nm(); |
|
5565 |
93328586 |
ctx_running = dequeue_ctx_nm(&queue); |
|
5566 |
93328586 |
mutex_unlock(&qmutex); |
|
5567 |
✓✓ | 93328586 |
if (!ctx_running) { |
5568 |
2106353 |
lbm_system_sleeping = true; |
|
5569 |
//Fixed sleep interval to poll events regularly. |
||
5570 |
2106353 |
usleep_callback(EVAL_CPS_MIN_SLEEP); |
|
5571 |
2106345 |
lbm_system_sleeping = false; |
|
5572 |
} |
||
5573 |
} |
||
5574 |
} |
||
5575 |
} |
||
5576 |
} |
||
5577 |
} |
||
5578 |
|||
5579 |
lbm_cid lbm_eval_program(lbm_value lisp) { |
||
5580 |
return lbm_create_ctx(lisp, ENC_SYM_NIL, 256, NULL); |
||
5581 |
} |
||
5582 |
|||
5583 |
lbm_cid lbm_eval_program_ext(lbm_value lisp, unsigned int stack_size) { |
||
5584 |
return lbm_create_ctx(lisp, ENC_SYM_NIL, stack_size, NULL); |
||
5585 |
} |
||
5586 |
|||
5587 |
21588 |
int lbm_eval_init() { |
|
5588 |
✓✗ | 21588 |
if (!qmutex_initialized) { |
5589 |
21588 |
mutex_init(&qmutex); |
|
5590 |
21588 |
qmutex_initialized = true; |
|
5591 |
} |
||
5592 |
✓✗ | 21588 |
if (!lbm_events_mutex_initialized) { |
5593 |
21588 |
mutex_init(&lbm_events_mutex); |
|
5594 |
21588 |
lbm_events_mutex_initialized = true; |
|
5595 |
} |
||
5596 |
✓✗ | 21588 |
if (!blocking_extension_mutex_initialized) { |
5597 |
21588 |
mutex_init(&blocking_extension_mutex); |
|
5598 |
21588 |
blocking_extension_mutex_initialized = true; |
|
5599 |
} |
||
5600 |
|||
5601 |
21588 |
mutex_lock(&qmutex); |
|
5602 |
21588 |
mutex_lock(&lbm_events_mutex); |
|
5603 |
|||
5604 |
21588 |
blocked.first = NULL; |
|
5605 |
21588 |
blocked.last = NULL; |
|
5606 |
21588 |
queue.first = NULL; |
|
5607 |
21588 |
queue.last = NULL; |
|
5608 |
21588 |
ctx_running = NULL; |
|
5609 |
|||
5610 |
21588 |
eval_cps_run_state = EVAL_CPS_STATE_RUNNING; |
|
5611 |
|||
5612 |
21588 |
mutex_unlock(&lbm_events_mutex); |
|
5613 |
21588 |
mutex_unlock(&qmutex); |
|
5614 |
|||
5615 |
21588 |
reset_infer_canary(); |
|
5616 |
|||
5617 |
✗✓ | 21588 |
if (!lbm_init_env()) return 0; |
5618 |
21588 |
eval_running = true; |
|
5619 |
21588 |
return 1; |
|
5620 |
} |
||
5621 |
|||
5622 |
21588 |
bool lbm_eval_init_events(unsigned int num_events) { |
|
5623 |
|||
5624 |
21588 |
mutex_lock(&lbm_events_mutex); |
|
5625 |
21588 |
lbm_events = (lbm_event_t*)lbm_malloc(num_events * sizeof(lbm_event_t)); |
|
5626 |
21588 |
bool r = false; |
|
5627 |
✓✗ | 21588 |
if (lbm_events) { |
5628 |
21588 |
lbm_events_max = num_events; |
|
5629 |
21588 |
lbm_events_head = 0; |
|
5630 |
21588 |
lbm_events_tail = 0; |
|
5631 |
21588 |
lbm_events_full = false; |
|
5632 |
21588 |
lbm_event_handler_pid = -1; |
|
5633 |
21588 |
r = true; |
|
5634 |
} |
||
5635 |
21588 |
mutex_unlock(&lbm_events_mutex); |
|
5636 |
21588 |
return r; |
|
5637 |
} |
Generated by: GCOVR (Version 4.2) |