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