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