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