GCC Code Coverage Report
Directory: ../src/ Exec Total Coverage
File: /home/joels/Current/lispbm/src/eval_cps.c Lines: 2868 3326 86.2 %
Date: 2025-04-14 11:29:35 Branches: 892 1344 66.4 %

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
}