GCC Code Coverage Report


Directory: ../src/
File: /home/joels/Current/lispbm/src/eval_cps.c
Date: 2025-08-08 18:10:24
Exec Total Coverage
Lines: 3186 3441 92.6%
Functions: 212 223 95.1%
Branches: 1069 1394 76.7%

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