GCC Code Coverage Report
Directory: ../src/ Exec Total Coverage
File: /home/joels/Current/lispbm/src/eval_cps.c Lines: 2820 3291 85.7 %
Date: 2025-01-19 11:10:47 Branches: 887 1348 65.8 %

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_ARRAY      CONTINUATION(21)
72
#define READ_APPEND_ARRAY     CONTINUATION(22)
73
#define MAP                   CONTINUATION(23)
74
#define MATCH_GUARD           CONTINUATION(24)
75
#define TERMINATE             CONTINUATION(25)
76
#define PROGN_VAR             CONTINUATION(26)
77
#define SETQ                  CONTINUATION(27)
78
#define MOVE_TO_FLASH         CONTINUATION(28)
79
#define MOVE_VAL_TO_FLASH_DISPATCH CONTINUATION(29)
80
#define MOVE_LIST_TO_FLASH    CONTINUATION(30)
81
#define CLOSE_LIST_IN_FLASH   CONTINUATION(31)
82
#define QQ_EXPAND_START       CONTINUATION(32)
83
#define QQ_EXPAND             CONTINUATION(33)
84
#define QQ_APPEND             CONTINUATION(34)
85
#define QQ_EXPAND_LIST        CONTINUATION(35)
86
#define QQ_LIST               CONTINUATION(36)
87
#define KILL                  CONTINUATION(37)
88
#define LOOP                  CONTINUATION(38)
89
#define LOOP_CONDITION        CONTINUATION(39)
90
#define MERGE_REST            CONTINUATION(40)
91
#define MERGE_LAYER           CONTINUATION(41)
92
#define CLOSURE_ARGS_REST     CONTINUATION(42)
93
#define MOVE_ARRAY_ELTS_TO_FLASH CONTINUATION(43)
94
#define POP_READER_FLAGS      CONTINUATION(44)
95
#define EXCEPTION_HANDLER     CONTINUATION(45)
96
#define RECV_TO               CONTINUATION(46)
97
#define WRAP_RESULT           CONTINUATION(47)
98
#define RECV_TO_RETRY         CONTINUATION(48)
99
#define NUM_CONTINUATIONS     49
100
101
#define FM_NEED_GC       -1
102
#define FM_NO_MATCH      -2
103
#define FM_PATTERN_ERROR -3
104
105
typedef enum {
106
  BL_OK = 0,
107
  BL_NO_MEMORY,
108
  BL_INCORRECT_KEY
109
} binding_location_status;
110
111
#define FB_OK             0
112
#define FB_TYPE_ERROR    -1
113
114
// Infer canarie
115
//
116
// In some cases Infer incorrectly complains about null pointer
117
// derefences that cannot happen. In these cases the longjmp
118
// error system aborts execution before the potential null
119
// pointer dereference can occur.
120
//
121
// Functions such as stack_reserve does not return NULL,
122
// instead it executes a longjmp and does not return at all.
123
// Infer does not seem to understand this abrubt code flow.
124
#ifdef LBM64
125
#define INFER_CANARY_BITS (lbm_uint)0xAAAAAAAAAAAAAAAA
126
#else
127
#define INFER_CANARY_BITS 0xAAAAAAAAu
128
#endif
129
lbm_uint INFER_CANARY[1];
130
131
8156
bool check_infer_canary(void) {
132
8156
  return INFER_CANARY[0] == INFER_CANARY_BITS;
133
}
134
135
21588
void reset_infer_canary(void) {
136
21588
  INFER_CANARY[0] = INFER_CANARY_BITS;
137
21588
}
138
139
const char* lbm_error_str_parse_eof = "End of parse stream.";
140
const char* lbm_error_str_parse_dot = "Incorrect usage of '.'.";
141
const char* lbm_error_str_parse_close = "Expected closing parenthesis.";
142
const char* lbm_error_str_num_args = "Incorrect number of arguments.";
143
const char* lbm_error_str_forbidden_in_atomic = "Operation is forbidden in an atomic block.";
144
const char* lbm_error_str_no_number = "Argument(s) must be a number.";
145
const char* lbm_error_str_not_a_boolean = "Argument must be t or nil (true or false).";
146
const char* lbm_error_str_incorrect_arg = "Incorrect argument.";
147
const char* lbm_error_str_var_outside_progn = "Usage of var outside of progn.";
148
const char* lbm_error_str_flash_not_possible = "Value cannot be written to flash.";
149
const char* lbm_error_str_flash_error = "Error writing to flash.";
150
const char* lbm_error_str_flash_full = "Flash memory is full.";
151
const char* lbm_error_str_variable_not_bound = "Variable not bound.";
152
const char* lbm_error_str_read_no_mem = "Out of memory while reading.";
153
const char* lbm_error_str_qq_expand = "Quasiquotation expansion error.";
154
155
static lbm_value lbm_error_suspect;
156
static bool lbm_error_has_suspect = false;
157
#ifdef LBM_ALWAYS_GC
158
159
#define WITH_GC(y, x)                           \
160
  gc();                                         \
161
  (y) = (x);                                    \
162
  if (lbm_is_symbol_merror((y))) {              \
163
    error_ctx(ENC_SYM_MERROR);                  \
164
  }
165
166
#define WITH_GC_RMBR_1(y, x, r)                 \
167
  lbm_gc_mark_phase(r);                         \
168
  gc();                                         \
169
  (y) = (x);                                    \
170
  if (lbm_is_symbol_merror((y))) {              \
171
    error_ctx(ENC_SYM_MERROR);                  \
172
  }
173
174
#else
175
176
#define WITH_GC(y, x)                           \
177
  (y) = (x);                                    \
178
  if (lbm_is_symbol_merror((y))) {              \
179
    gc();                                       \
180
    (y) = (x);                                  \
181
    if (lbm_is_symbol_merror((y))) {            \
182
      error_ctx(ENC_SYM_MERROR);                \
183
    }                                           \
184
    /* continue executing statements below */   \
185
  }
186
#define WITH_GC_RMBR_1(y, x, r)                 \
187
  (y) = (x);                                    \
188
  if (lbm_is_symbol_merror((y))) {              \
189
    lbm_gc_mark_phase(r);                       \
190
    gc();                                       \
191
    (y) = (x);                                  \
192
    if (lbm_is_symbol_merror((y))) {            \
193
      error_ctx(ENC_SYM_MERROR);                \
194
    }                                           \
195
    /* continue executing statements below */   \
196
  }
197
198
#endif
199
200
/**************************************************************/
201
/* */
202
typedef struct {
203
  eval_context_t *first;
204
  eval_context_t *last;
205
} eval_context_queue_t;
206
207
#ifdef CLEAN_UP_CLOSURES
208
static lbm_value clean_cl_env_symbol = ENC_SYM_NIL;
209
#endif
210
211
static int gc(void);
212
static void error_ctx(lbm_value);
213
static void error_at_ctx(lbm_value err_val, lbm_value at);
214
static void enqueue_ctx(eval_context_queue_t *q, eval_context_t *ctx);
215
static bool mailbox_add_mail(eval_context_t *ctx, lbm_value mail);
216
217
// The currently executing context.
218
eval_context_t *ctx_running = NULL;
219
volatile bool  lbm_system_sleeping = false;
220
221
static volatile bool gc_requested = false;
222
4368
void lbm_request_gc(void) {
223
4368
  gc_requested = true;
224
4368
}
225
226
/*
227
   On ChibiOs the CH_CFG_ST_FREQUENCY setting in chconf.h sets the
228
   resolution of the timer used for sleep operations.  If this is set
229
   to 10KHz the resolution is 100us.
230
231
   The CH_CFG_ST_TIMEDELTA specifies the minimum number of ticks that
232
   can be safely specified in a timeout directive (wonder if that
233
   means sleep-period). The timedelta is set to 2.
234
235
   If I have understood these correctly it means that the minimum
236
   sleep duration possible is 2 * 100us = 200us.
237
*/
238
239
#define EVAL_CPS_DEFAULT_STACK_SIZE 256
240
#define EVAL_CPS_MIN_SLEEP 200
241
#define EVAL_STEPS_QUOTA   10
242
243
static volatile uint32_t eval_steps_refill = EVAL_STEPS_QUOTA;
244
static uint32_t eval_steps_quota = EVAL_STEPS_QUOTA;
245
246
28
void lbm_set_eval_step_quota(uint32_t quota) {
247
28
  eval_steps_refill = quota;
248
28
}
249
250
static uint32_t          eval_cps_run_state = EVAL_CPS_STATE_DEAD;
251
static volatile uint32_t eval_cps_next_state = EVAL_CPS_STATE_NONE;
252
static volatile uint32_t eval_cps_next_state_arg = 0;
253
static volatile bool     eval_cps_state_changed = false;
254
255
static void usleep_nonsense(uint32_t us) {
256
  (void) us;
257
}
258
259
static bool dynamic_load_nonsense(const char *sym, const char **code) {
260
  (void) sym;
261
  (void) code;
262
  return false;
263
}
264
265
static uint32_t timestamp_nonsense(void) {
266
  return 0;
267
}
268
269
static int printf_nonsense(const char *fmt, ...) {
270
  (void) fmt;
271
  return 0;
272
}
273
274
static void ctx_done_nonsense(eval_context_t *ctx) {
275
  (void) ctx;
276
}
277
278
static void critical_nonsense(void) {
279
  return;
280
}
281
282
static void user_callback_nonsense(void *arg) {
283
  (void) arg;
284
  return;
285
}
286
287
static void (*critical_error_callback)(void) = critical_nonsense;
288
static void (*usleep_callback)(uint32_t) = usleep_nonsense;
289
static uint32_t (*timestamp_us_callback)(void) = timestamp_nonsense;
290
static void (*ctx_done_callback)(eval_context_t *) = ctx_done_nonsense;
291
static int (*printf_callback)(const char *, ...) = printf_nonsense;
292
static bool (*dynamic_load_callback)(const char *, const char **) = dynamic_load_nonsense;
293
static void (*user_callback)(void *) = user_callback_nonsense;
294
295
void lbm_set_user_callback(void (*fptr)(void *)) {
296
  if (fptr == NULL) user_callback = user_callback_nonsense;
297
  else user_callback = fptr;
298
}
299
300
21588
void lbm_set_critical_error_callback(void (*fptr)(void)) {
301
21588
  if (fptr == NULL) critical_error_callback = critical_nonsense;
302
21588
  else critical_error_callback = fptr;
303
21588
}
304
305
21588
void lbm_set_usleep_callback(void (*fptr)(uint32_t)) {
306
21588
  if (fptr == NULL) usleep_callback = usleep_nonsense;
307
21588
  else usleep_callback = fptr;
308
21588
}
309
310
21588
void lbm_set_timestamp_us_callback(uint32_t (*fptr)(void)) {
311
21588
  if (fptr == NULL) timestamp_us_callback = timestamp_nonsense;
312
21588
  else timestamp_us_callback = fptr;
313
21588
}
314
315
21588
void lbm_set_ctx_done_callback(void (*fptr)(eval_context_t *)) {
316
21588
  if (fptr == NULL) ctx_done_callback = ctx_done_nonsense;
317
21588
  else ctx_done_callback = fptr;
318
21588
}
319
320
21588
void lbm_set_printf_callback(int (*fptr)(const char*, ...)){
321
21588
  if (fptr == NULL) printf_callback = printf_nonsense;
322
21588
  else printf_callback = fptr;
323
21588
}
324
325
21588
void lbm_set_dynamic_load_callback(bool (*fptr)(const char *, const char **)) {
326
21588
  if (fptr == NULL) dynamic_load_callback = dynamic_load_nonsense;
327
21588
  else  dynamic_load_callback = fptr;
328
21588
}
329
330
static volatile lbm_event_t *lbm_events = NULL;
331
static unsigned int lbm_events_head = 0;
332
static unsigned int lbm_events_tail = 0;
333
static unsigned int lbm_events_max  = 0;
334
static bool         lbm_events_full = false;
335
static mutex_t      lbm_events_mutex;
336
static bool         lbm_events_mutex_initialized = false;
337
static volatile lbm_cid  lbm_event_handler_pid = -1;
338
339
lbm_cid lbm_get_event_handler_pid(void) {
340
  return lbm_event_handler_pid;
341
}
342
343
224
void lbm_set_event_handler_pid(lbm_cid pid) {
344
224
  lbm_event_handler_pid = pid;
345
224
}
346
347
bool lbm_event_handler_exists(void) {
348
  return(lbm_event_handler_pid > 0);
349
}
350
351
352
7024
static bool event_internal(lbm_event_type_t event_type, lbm_uint parameter, lbm_uint buf_ptr, lbm_uint buf_len) {
353
7024
  bool r = false;
354
7024
  if (lbm_events) {
355
7024
    mutex_lock(&lbm_events_mutex);
356
7024
    if (!lbm_events_full) {
357
      lbm_event_t event;
358
7024
      event.type = event_type;
359
7024
      event.parameter = parameter;
360
7024
      event.buf_ptr = buf_ptr;
361
7024
      event.buf_len = buf_len;
362
7024
      lbm_events[lbm_events_head] = event;
363
7024
      lbm_events_head = (lbm_events_head + 1) % lbm_events_max;
364
7024
      lbm_events_full = lbm_events_head == lbm_events_tail;
365
7024
      r = true;
366
    }
367
7024
    mutex_unlock(&lbm_events_mutex);
368
  }
369
7024
  return r;
370
}
371
372
bool lbm_event_define(lbm_value key, lbm_flat_value_t *fv) {
373
  return event_internal(LBM_EVENT_DEFINE, key, (lbm_uint)fv->buf, fv->buf_size);
374
}
375
376
bool lbm_event_run_user_callback(void *arg) {
377
  return event_internal(LBM_EVENT_RUN_USER_CALLBACK, (lbm_uint)arg, 0, 0);
378
}
379
380
bool lbm_event_unboxed(lbm_value unboxed) {
381
  lbm_uint t = lbm_type_of(unboxed);
382
  if (t == LBM_TYPE_SYMBOL ||
383
      t == LBM_TYPE_I ||
384
      t == LBM_TYPE_U ||
385
      t == LBM_TYPE_CHAR) {
386
    if (lbm_event_handler_pid > 0) {
387
      return event_internal(LBM_EVENT_FOR_HANDLER, 0, (lbm_uint)unboxed, 0);
388
    }
389
  }
390
  return false;
391
}
392
393
6940
bool lbm_event(lbm_flat_value_t *fv) {
394
6940
  if (lbm_event_handler_pid > 0) {
395
6940
    return event_internal(LBM_EVENT_FOR_HANDLER, 0, (lbm_uint)fv->buf, fv->buf_size);
396
  }
397
  return false;
398
}
399
400
93335603
static bool lbm_event_pop(lbm_event_t *event) {
401
93335603
  mutex_lock(&lbm_events_mutex);
402

93335603
  if (lbm_events_head == lbm_events_tail && !lbm_events_full) {
403
93328586
    mutex_unlock(&lbm_events_mutex);
404
93328586
    return false;
405
  }
406
7017
  *event = lbm_events[lbm_events_tail];
407
7017
  lbm_events_tail = (lbm_events_tail + 1) % lbm_events_max;
408
7017
  lbm_events_full = false;
409
7017
  mutex_unlock(&lbm_events_mutex);
410
7017
  return true;
411
}
412
413
bool lbm_event_queue_is_empty(void) {
414
  mutex_lock(&lbm_events_mutex);
415
  bool empty = false;
416
  if (lbm_events_head == lbm_events_tail && !lbm_events_full) {
417
    empty = true;
418
  }
419
  mutex_unlock(&lbm_events_mutex);
420
  return empty;
421
}
422
423
static bool              eval_running = false;
424
static volatile bool     blocking_extension = false;
425
static mutex_t           blocking_extension_mutex;
426
static bool              blocking_extension_mutex_initialized = false;
427
static lbm_uint          blocking_extension_timeout_us = 0;
428
static bool              blocking_extension_timeout = false;
429
430
static bool              is_atomic = false;
431
432
/* Process queues */
433
static eval_context_queue_t blocked  = {NULL, NULL};
434
static eval_context_queue_t queue    = {NULL, NULL};
435
436
/* one mutex for all queue operations */
437
mutex_t qmutex;
438
bool    qmutex_initialized = false;
439
440
441
// MODES
442
static volatile bool lbm_verbose = false;
443
static volatile bool lbm_hide_trapped_error = false;
444
445
void lbm_toggle_verbose(void) {
446
  lbm_verbose = !lbm_verbose;
447
}
448
449
21588
void lbm_set_verbose(bool verbose) {
450
21588
  lbm_verbose = verbose;
451
21588
}
452
453
void lbm_set_hide_trapped_error(bool hide) {
454
  lbm_hide_trapped_error = hide;
455
}
456
457
1120
lbm_cid lbm_get_current_cid(void) {
458
1120
  if (ctx_running)
459
1120
    return ctx_running->id;
460
  else
461
    return -1;
462
}
463
464
eval_context_t *lbm_get_current_context(void) {
465
  return ctx_running;
466
}
467
468
void lbm_surrender_quota(void) {
469
  eval_steps_quota = 0;
470
}
471
472
473
/****************************************************/
474
/* Utilities used locally in this file              */
475
476
380968
static inline lbm_array_header_t *assume_array(lbm_value a){
477
380968
  return (lbm_array_header_t*)lbm_ref_cell(a)->car;
478
}
479
480
4388468
static lbm_value cons_with_gc(lbm_value head, lbm_value tail, lbm_value remember) {
481
#ifdef LBM_ALWAYS_GC
482
  lbm_value always_gc_roots[3] = {head, tail, remember};
483
  lbm_gc_mark_roots(always_gc_roots,3);
484
  gc();
485
#endif
486
4388468
  lbm_value res = lbm_heap_state.freelist;
487
4388468
  if (lbm_is_symbol_nil(res)) {
488
1136
    lbm_value roots[3] = {head, tail, remember};
489
1136
    lbm_gc_mark_roots(roots,3);
490
1136
    gc();
491
1136
    res = lbm_heap_state.freelist;
492
1136
    if (lbm_is_symbol_nil(res)) {
493
      error_ctx(ENC_SYM_MERROR);
494
    }
495
  }
496
4388468
  lbm_uint heap_ix = lbm_dec_ptr(res);
497
4388468
  lbm_heap_state.freelist = lbm_heap_state.heap[heap_ix].cdr;
498
4388468
  lbm_heap_state.num_alloc++;
499
4388468
  lbm_heap_state.heap[heap_ix].car = head;
500
4388468
  lbm_heap_state.heap[heap_ix].cdr = tail;
501
4388468
  res = lbm_set_ptr_type(res, LBM_TYPE_CONS);
502
4388468
  return res;
503
}
504
505
469496525
static lbm_uint *get_stack_ptr(eval_context_t *ctx, unsigned int n) {
506
469496525
  if (n <= ctx->K.sp) {
507
469496525
    lbm_uint index = ctx->K.sp - n;
508
469496525
    return &ctx->K.data[index];
509
  }
510
  error_ctx(ENC_SYM_STACK_ERROR);
511
  return (lbm_uint*)INFER_CANARY; // dead code cannot be reached, but C compiler doesn't realise.
512
}
513
514
// pop_stack_ptr is safe when no GC is performed and
515
// the values of the stack will be dropped.
516
21771007
static lbm_uint *pop_stack_ptr(eval_context_t *ctx, unsigned int n) {
517
21771007
  if (n <= ctx->K.sp) {
518
21771007
    ctx->K.sp -= n;
519
21771007
    return &ctx->K.data[ctx->K.sp];
520
  }
521
  error_ctx(ENC_SYM_STACK_ERROR);
522
  return (lbm_uint*)INFER_CANARY; // dead code cannot be reached, but C compiler doesn't realise.
523
}
524
525
493959640
static inline lbm_uint *stack_reserve(eval_context_t *ctx, unsigned int n) {
526
493959640
  if (ctx->K.sp + n < ctx->K.size) {
527
493959640
    lbm_uint *ptr = &ctx->K.data[ctx->K.sp];
528
493959640
    ctx->K.sp += n;
529
493959640
    return ptr;
530
  }
531
  error_ctx(ENC_SYM_STACK_ERROR);
532
  return (lbm_uint*)INFER_CANARY; // dead code cannot be reached, but C compiler doesn't realise.
533
}
534
535
7196
static void handle_flash_status(lbm_flash_status s) {
536
7196
  if ( s == LBM_FLASH_FULL) {
537
    lbm_set_error_reason((char*)lbm_error_str_flash_full);
538
    error_ctx(ENC_SYM_EERROR);
539
  }
540
7196
  if (s == LBM_FLASH_WRITE_ERROR) {
541
    lbm_set_error_reason((char*)lbm_error_str_flash_error);
542
    error_ctx(ENC_SYM_FATAL_ERROR);
543
  }
544
7196
}
545
546
84
static void lift_array_flash(lbm_value flash_cell, bool bytearray,  char *data, lbm_uint num_elt) {
547
548
  lbm_array_header_t flash_array_header;
549
84
  flash_array_header.size = num_elt;
550
84
  flash_array_header.data = (lbm_uint*)data;
551
84
  lbm_uint flash_array_header_ptr = 0;
552
84
  handle_flash_status(lbm_write_const_raw((lbm_uint*)&flash_array_header,
553
                                          sizeof(lbm_array_header_t) / sizeof(lbm_uint),
554
                                          &flash_array_header_ptr));
555
84
  handle_flash_status(write_const_car(flash_cell, flash_array_header_ptr));
556
84
  lbm_uint t = bytearray ? ENC_SYM_ARRAY_TYPE : ENC_SYM_LISPARRAY_TYPE;
557
84
  handle_flash_status(write_const_cdr(flash_cell, t));
558
84
}
559
560
119716056
static void get_car_and_cdr(lbm_value a, lbm_value *a_car, lbm_value *a_cdr) {
561
119716056
  if (lbm_is_ptr(a)) {
562
119398396
    lbm_cons_t *cell = lbm_ref_cell(a);
563
119398396
    *a_car = cell->car;
564
119398396
    *a_cdr = cell->cdr;
565
317660
  } else if (lbm_is_symbol_nil(a)) {
566
317660
    *a_car = *a_cdr = ENC_SYM_NIL;
567
  } else {
568
    *a_car = *a_cdr = ENC_SYM_NIL;
569
    error_ctx(ENC_SYM_TERROR);
570
  }
571
119716056
}
572
573
/* car cdr caar cadr replacements that are evaluator safe. */
574
114759837
static lbm_value get_car(lbm_value a) {
575
114759837
  if (lbm_is_ptr(a)) {
576
114759837
    lbm_cons_t *cell = lbm_ref_cell(a);
577
114759837
    return cell->car;
578
  } else if (lbm_is_symbol_nil(a)) {
579
    return a;
580
  }
581
  error_ctx(ENC_SYM_TERROR);
582
  return(ENC_SYM_TERROR);
583
}
584
585
139610947
static lbm_value get_cdr(lbm_value a) {
586
139610947
  if (lbm_is_ptr(a)) {
587
139610919
    lbm_cons_t *cell = lbm_ref_cell(a);
588
139610919
    return cell->cdr;
589
28
  } else if (lbm_is_symbol_nil(a)) {
590
28
    return a;
591
  }
592
  error_ctx(ENC_SYM_TERROR);
593
  return(ENC_SYM_TERROR);
594
}
595
596
27381821
static lbm_value get_cadr(lbm_value a) {
597
27381821
  if (lbm_is_ptr(a)) {
598
27381821
    lbm_cons_t *cell = lbm_ref_cell(a);
599
27381821
    lbm_value tmp = cell->cdr;
600
27381821
    if (lbm_is_ptr(tmp)) {
601
27371937
      return lbm_ref_cell(tmp)->car;
602
9884
    } else if (lbm_is_symbol_nil(tmp)) {
603
9884
      return tmp;
604
    }
605
  } else if (lbm_is_symbol_nil(a)) {
606
    return a;
607
  }
608
  error_ctx(ENC_SYM_TERROR);
609
  return(ENC_SYM_TERROR);
610
}
611
612
// Allocate a binding and attach it to a list (if so desired)
613
60007154
static lbm_value allocate_binding(lbm_value key, lbm_value val, lbm_value the_cdr) {
614
#ifdef LBM_ALWAYS_GC
615
  lbm_gc_mark_phase(key);
616
  lbm_gc_mark_phase(val);
617
  lbm_gc_mark_phase(the_cdr);
618
  gc();
619
  if (lbm_heap_num_free() < 2) {
620
    error_ctx(ENC_SYM_MERROR);
621
  }
622
#else
623
60007154
  if (lbm_heap_num_free() < 2) {
624
83306
    lbm_gc_mark_phase(key);
625
83306
    lbm_gc_mark_phase(val);
626
83306
    lbm_gc_mark_phase(the_cdr);
627
83306
    gc();
628
83306
    if (lbm_heap_num_free() < 2) {
629
28
      error_ctx(ENC_SYM_MERROR);
630
    }
631
  }
632
#endif
633
  // If num_free is calculated correctly, freelist is definitely a cons-cell.
634
60007126
  lbm_cons_t* heap = lbm_heap_state.heap;
635
60007126
  lbm_value binding_cell = lbm_heap_state.freelist;
636
60007126
  lbm_uint binding_cell_ix = lbm_dec_ptr(binding_cell);
637
60007126
  lbm_value list_cell = heap[binding_cell_ix].cdr;
638
60007126
  lbm_uint list_cell_ix = lbm_dec_ptr(list_cell);
639
60007126
  lbm_heap_state.freelist = heap[list_cell_ix].cdr;
640
60007126
  lbm_heap_state.num_alloc += 2;
641
60007126
  heap[binding_cell_ix].car = key;
642
60007126
  heap[binding_cell_ix].cdr = val;
643
60007126
  heap[list_cell_ix].car = binding_cell;
644
60007126
  heap[list_cell_ix].cdr = the_cdr;
645
60007126
  return list_cell;
646
}
647
648
#define CLO_PARAMS 0
649
#define CLO_BODY   1
650
#define CLO_ENV    2
651
#define LOOP_BINDS 0
652
#define LOOP_COND  1
653
#define LOOP_BODY  2
654
655
// TODO: extract_n could be a good place to do some error checking.
656
//       extract_n is often used to extract components of a list that
657
//       makes up a special form application. If there are not n items
658
//       present that could be an indication of a syntax error in the
659
//       special form application.
660
// (a b c) -> [a b c]
661
57802816
static lbm_value extract_n(lbm_value curr, lbm_value *res, unsigned int n) {
662
218965912
  for (unsigned int i = 0; i < n; i ++) {
663
161163096
    if (lbm_is_ptr(curr)) {
664
161163068
      lbm_cons_t *cell = lbm_ref_cell(curr);
665
161163068
      res[i] = cell->car;
666
161163068
      curr = cell->cdr;
667
    } else {
668
28
      res[i] = ENC_SYM_NIL;
669
    }
670
  }
671
57802816
  return curr; // Rest of list is returned here.
672
}
673
674
73276474
static void call_fundamental(lbm_uint fundamental, lbm_value *args, lbm_uint arg_count, eval_context_t *ctx) {
675
  lbm_value res;
676
#ifdef LBM_ALWAYS_GC
677
  gc();
678
#endif
679
73276474
  res = fundamental_table[fundamental](args, arg_count, ctx);
680
73276474
  if (lbm_is_error(res)) {
681
216067
    if (lbm_is_symbol_merror(res)) {
682
211475
      gc();
683
211475
      res = fundamental_table[fundamental](args, arg_count, ctx);
684
    }
685
216067
    if (lbm_is_error(res)) {
686
4656
      error_at_ctx(res, lbm_enc_sym(FUNDAMENTAL_SYMBOLS_START | fundamental));
687
    }
688
  }
689
73271818
  lbm_stack_drop(&ctx->K, arg_count+1);
690
73271818
  ctx->app_cont = true;
691
73271818
  ctx->r = res;
692
73271818
}
693
694
28
static void atomic_error(void) {
695
28
  is_atomic = false;
696
28
  lbm_set_error_reason((char*)lbm_error_str_forbidden_in_atomic);
697
28
  error_ctx(ENC_SYM_EERROR);
698
}
699
700
// block_current_ctx blocks a context until it is
701
// woken up externally or a timeout period of time passes.
702
// Blocking while in an atomic block would have bad consequences.
703
3323
static void block_current_ctx(uint32_t state, lbm_uint sleep_us,  bool do_cont) {
704
3323
  if (is_atomic) atomic_error();
705
3323
  ctx_running->timestamp = timestamp_us_callback();
706
3323
  ctx_running->sleep_us = sleep_us;
707
3323
  ctx_running->state  = state;
708
3323
  ctx_running->app_cont = do_cont;
709
3323
  enqueue_ctx(&blocked, ctx_running);
710
3323
  ctx_running = NULL;
711
3323
}
712
713
// reblock an essentially already blocked context.
714
// Same as block but sets no new timestamp or sleep_us.
715
static void reblock_current_ctx(uint32_t state, bool do_cont) {
716
  if (is_atomic) atomic_error();
717
  ctx_running->state  = state;
718
  ctx_running->app_cont = do_cont;
719
  enqueue_ctx(&blocked, ctx_running);
720
  ctx_running = NULL;
721
}
722
723
724
126
lbm_flash_status lbm_write_const_array_padded(uint8_t *data, lbm_uint n, lbm_uint *res) {
725
126
  lbm_uint full_words = n / sizeof(lbm_uint);
726
126
  lbm_uint n_mod = n % sizeof(lbm_uint);
727
728
126
  if (n_mod == 0) { // perfect fit.
729
56
    return lbm_write_const_raw((lbm_uint*)data, full_words, res);
730
  } else {
731
70
    lbm_uint last_word = 0;
732
70
    memcpy(&last_word, &data[full_words * sizeof(lbm_uint)], n_mod);
733
70
    if (full_words >= 1) {
734
14
      lbm_flash_status s = lbm_write_const_raw((lbm_uint*)data, full_words, res);
735
14
      if ( s == LBM_FLASH_WRITE_OK) {
736
        lbm_uint dummy;
737
14
        s = lbm_write_const_raw(&last_word, 1, &dummy);
738
      }
739
14
      return s;
740
    } else {
741
56
      return lbm_write_const_raw(&last_word, 1, res);
742
    }
743
  }
744
}
745
746
/****************************************************/
747
/* Error message creation                           */
748
749
#define ERROR_MESSAGE_BUFFER_SIZE_BYTES 256
750
751
8156
void print_environments(char *buf, unsigned int size) {
752
753
8156
  lbm_value curr_l = ctx_running->curr_env;
754
8156
  printf_callback("\tCurrent local environment:\n");
755
8384
  while (lbm_type_of(curr_l) == LBM_TYPE_CONS) {
756
228
    lbm_print_value(buf, (size/2) - 1, lbm_caar(curr_l));
757
228
    lbm_print_value(buf + (size/2),size/2, lbm_cdr(lbm_car(curr_l)));
758
228
    printf_callback("\t%s = %s\n", buf, buf+(size/2));
759
228
    curr_l = lbm_cdr(curr_l);
760
  }
761
8156
  printf_callback("\n\n");
762
8156
  printf_callback("\tCurrent global environment:\n");
763
8156
  lbm_value *glob_env = lbm_get_global_env();
764
765
269148
  for (int i = 0; i < GLOBAL_ENV_ROOTS; i ++) {
766
260992
    lbm_value curr_g = glob_env[i];;
767
306696
    while (lbm_type_of(curr_g) == LBM_TYPE_CONS) {
768
769
45704
      lbm_print_value(buf, (size/2) - 1, lbm_caar(curr_g));
770
45704
      lbm_print_value(buf + (size/2),size/2, lbm_cdr(lbm_car(curr_g)));
771
45704
      printf_callback("\t%s = %s\n", buf, buf+(size/2));
772
45704
      curr_g = lbm_cdr(curr_g);
773
    }
774
  }
775
8156
}
776
777
24216
void print_error_value(char *buf, lbm_uint bufsize, char *pre, lbm_value v, bool lookup) {
778
779
24216
  lbm_print_value(buf, bufsize, v);
780
24216
  printf_callback("%s %s\n",pre, buf);
781
24216
  if (lookup) {
782
16060
    if (lbm_is_symbol(v)) {
783
9196
      if (lbm_dec_sym(v) >= RUNTIME_SYMBOLS_START) {
784
1152
        lbm_value res = ENC_SYM_NIL;
785

2244
        if (lbm_env_lookup_b(&res, v, ctx_running->curr_env) ||
786
1092
            lbm_global_env_lookup(&res, v)) {
787
760
          lbm_print_value(buf, bufsize, res);
788
760
          printf_callback("      bound to: %s\n", buf);
789
        } else {
790
392
          printf_callback("      UNDEFINED\n");
791
        }
792
      }
793
    }
794
  }
795
24216
}
796
797
8156
void print_error_message(lbm_value error,
798
                         bool has_at,
799
                         lbm_value at,
800
                         unsigned int row,
801
                         unsigned int col,
802
                         lbm_int row0,
803
                         lbm_int row1,
804
                         lbm_int cid,
805
                         char *name) {
806
  /* try to allocate a lbm_print_value buffer on the lbm_memory */
807
8156
  char *buf = lbm_malloc_reserve(ERROR_MESSAGE_BUFFER_SIZE_BYTES);
808
8156
  if (!buf) {
809
    printf_callback("Error: Not enough free memory to create a human readable error message\n");
810
    return;
811
  }
812
813
8156
  print_error_value(buf, ERROR_MESSAGE_BUFFER_SIZE_BYTES,"   Error:", error, false);
814
8156
  if (name) {
815
    printf_callback(  "   CTX: %d \"%s\"\n", cid, name);
816
  } else {
817
8156
    printf_callback(  "   CTX: %d\n", cid);
818
  }
819
8156
  print_error_value(buf, ERROR_MESSAGE_BUFFER_SIZE_BYTES,"   Current:", ctx_running->curr_exp, true);
820
8156
  if (lbm_error_has_suspect) {
821
1232
      print_error_value(buf, ERROR_MESSAGE_BUFFER_SIZE_BYTES,"   At:", lbm_error_suspect, true);
822
1232
      lbm_error_has_suspect = false;
823
6924
  } else if (has_at) {
824
6672
    print_error_value(buf, ERROR_MESSAGE_BUFFER_SIZE_BYTES,"   In:", at, true);
825
  }
826
827
8156
  printf_callback("\n");
828
829

8156
  if (lbm_is_symbol(error) &&
830
      error == ENC_SYM_RERROR) {
831
    printf_callback("   Line:   %u\n", row);
832
    printf_callback("   Column: %u\n", col);
833
8156
  } else if (row0 >= 0) {
834
3976
    if (row1 < 0) printf_callback("   Starting at row: %d\n", row0);
835
3976
    else printf_callback("   Between row %d and %d\n", row0, row1);
836
  }
837
838
8156
  printf_callback("\n");
839
840
8156
  if (ctx_running->error_reason) {
841
1736
    printf_callback("   Reason: %s\n\n", ctx_running->error_reason);
842
  }
843
8156
  if (lbm_verbose) {
844
8156
    lbm_print_value(buf, ERROR_MESSAGE_BUFFER_SIZE_BYTES, ctx_running->r);
845
8156
    printf_callback("   Current intermediate result: %s\n\n", buf);
846
847
8156
    print_environments(buf, ERROR_MESSAGE_BUFFER_SIZE_BYTES);
848
8156
    printf_callback("\n\n");
849
850
8156
    printf_callback("   Stack:\n");
851
166932
    for (unsigned int i = 0; i < ctx_running->K.sp; i ++) {
852
158776
      lbm_print_value(buf, ERROR_MESSAGE_BUFFER_SIZE_BYTES, ctx_running->K.data[i]);
853
158776
      printf_callback("     %s\n", buf);
854
    }
855
  }
856
8156
  lbm_free(buf);
857
}
858
859
/****************************************************/
860
/* Tokenizing and parsing                           */
861
862
309988
bool create_string_channel(char *str, lbm_value *res, lbm_value dep) {
863
864
309988
  lbm_char_channel_t *chan = NULL;
865
309988
  lbm_string_channel_state_t *st = NULL;
866
867
309988
  st = (lbm_string_channel_state_t*)lbm_malloc(sizeof(lbm_string_channel_state_t));
868
309988
  if (st == NULL) {
869
1018
    return false;
870
  }
871
308970
  chan = (lbm_char_channel_t*)lbm_malloc(sizeof(lbm_char_channel_t));
872
308970
  if (chan == NULL) {
873
270
    lbm_free(st);
874
270
    return false;
875
  }
876
877
308700
  lbm_create_string_char_channel(st, chan, str);
878
308700
  lbm_value cell = lbm_heap_allocate_cell(LBM_TYPE_CHANNEL, (lbm_uint) chan, ENC_SYM_CHANNEL_TYPE);
879
308700
  if (cell == ENC_SYM_MERROR) {
880
    lbm_free(st);
881
    lbm_free(chan);
882
    return false;
883
  }
884
885
308700
  lbm_char_channel_set_dependency(chan, dep);
886
887
308700
  *res = cell;
888
308700
  return true;
889
}
890
891
21588
bool lift_char_channel(lbm_char_channel_t *chan , lbm_value *res) {
892
21588
  lbm_value cell = lbm_heap_allocate_cell(LBM_TYPE_CHANNEL, (lbm_uint) chan, ENC_SYM_CHANNEL_TYPE);
893
21588
  if (cell == ENC_SYM_MERROR) {
894
    return false;
895
  }
896
21588
  *res = cell;
897
21588
  return true;
898
}
899
900
901
/****************************************************/
902
/* Queue functions                                  */
903
904
695634
static void queue_iterator_nm(eval_context_queue_t *q, ctx_fun f, void *arg1, void *arg2) {
905
  eval_context_t *curr;
906
695634
  curr = q->first;
907
908
708960
  while (curr != NULL) {
909
13326
    f(curr, arg1, arg2);
910
13326
    curr = curr->next;
911
  }
912
695634
}
913
914
void lbm_all_ctxs_iterator(ctx_fun f, void *arg1, void *arg2) {
915
  mutex_lock(&qmutex);
916
  queue_iterator_nm(&blocked, f, arg1, arg2);
917
  queue_iterator_nm(&queue, f, arg1, arg2);
918
  if (ctx_running) f(ctx_running, arg1, arg2);
919
  mutex_unlock(&qmutex);
920
}
921
922
84
void lbm_running_iterator(ctx_fun f, void *arg1, void *arg2){
923
84
  mutex_lock(&qmutex);
924
84
  queue_iterator_nm(&queue, f, arg1, arg2);
925
84
  mutex_unlock(&qmutex);
926
84
}
927
928
84
void lbm_blocked_iterator(ctx_fun f, void *arg1, void *arg2){
929
84
  mutex_lock(&qmutex);
930
84
  queue_iterator_nm(&blocked, f, arg1, arg2);
931
84
  mutex_unlock(&qmutex);
932
84
}
933
934
91256870
static void enqueue_ctx_nm(eval_context_queue_t *q, eval_context_t *ctx) {
935
91256870
  if (q->last == NULL) {
936
88252634
    ctx->prev = NULL;
937
88252634
    ctx->next = NULL;
938
88252634
    q->first = ctx;
939
88252634
    q->last  = ctx;
940
  } else {
941
3004236
    ctx->prev = q->last;
942
3004236
    ctx->next = NULL;
943
3004236
    q->last->next = ctx;
944
3004236
    q->last = ctx;
945
  }
946
91256870
}
947
948
57177
static void enqueue_ctx(eval_context_queue_t *q, eval_context_t *ctx) {
949
57177
  mutex_lock(&qmutex);
950
57177
  enqueue_ctx_nm(q,ctx);
951
57177
  mutex_unlock(&qmutex);
952
57177
}
953
954
18481
static eval_context_t *lookup_ctx_nm(eval_context_queue_t *q, lbm_cid cid) {
955
  eval_context_t *curr;
956
18481
  curr = q->first;
957
18481
  while (curr != NULL) {
958
4200
    if (curr->id == cid) {
959
4200
      return curr;
960
    }
961
    curr = curr->next;
962
  }
963
14281
  return NULL;
964
}
965
966
3183
static bool drop_ctx_nm(eval_context_queue_t *q, eval_context_t *ctx) {
967
968
3183
  bool res = false;
969

3183
  if (q->first == NULL || q->last == NULL) {
970
    if (!(q->last == NULL && q->first == NULL)) {
971
      /* error state that should not happen */
972
      return res;
973
    }
974
    /* Queue is empty */
975
    return res;
976
  }
977
978
3183
  eval_context_t *curr = q->first;
979
3183
  while (curr) {
980
3183
    if (curr->id == ctx->id) {
981
3183
      res = true;
982
3183
      eval_context_t *tmp = curr->next;
983
3183
      if (curr->prev == NULL) {
984
3183
        if (curr->next == NULL) {
985
3169
          q->last = NULL;
986
3169
          q->first = NULL;
987
        } else {
988
14
          q->first = tmp;
989
14
          tmp->prev = NULL;
990
        }
991
      } else { /* curr->prev != NULL */
992
        if (curr->next == NULL) {
993
          q->last = curr->prev;
994
          q->last->next = NULL;
995
        } else {
996
          curr->prev->next = tmp;
997
          tmp->prev = curr->prev;
998
        }
999
      }
1000
3183
      break;
1001
    }
1002
    curr = curr->next;
1003
  }
1004
3183
  return res;
1005
}
1006
1007
/* End execution of the running context. */
1008
22399
static void finish_ctx(void) {
1009
1010
22399
  if (!ctx_running) {
1011
    return;
1012
  }
1013
  /* Drop the continuation stack immediately to free up lbm_memory */
1014
22399
  lbm_stack_free(&ctx_running->K);
1015
22399
  ctx_done_callback(ctx_running);
1016
1017
22399
  lbm_free(ctx_running->name); //free name if in LBM_MEM
1018
22399
  lbm_memory_free((lbm_uint*)ctx_running->error_reason); //free error_reason if in LBM_MEM
1019
1020
22399
  lbm_memory_free((lbm_uint*)ctx_running->mailbox);
1021
22399
  lbm_memory_free((lbm_uint*)ctx_running);
1022
22399
  ctx_running = NULL;
1023
}
1024
1025
140
static void context_exists(eval_context_t *ctx, void *cid, void *b) {
1026
140
  if (ctx->id == *(lbm_cid*)cid) {
1027
28
    *(bool*)b = true;
1028
  }
1029
140
}
1030
1031
1232
void lbm_set_error_suspect(lbm_value suspect) {
1032
1232
  lbm_error_suspect = suspect;
1033
1232
  lbm_error_has_suspect = true;
1034
1232
}
1035
1036
1316
void lbm_set_error_reason(char *error_str) {
1037
1316
  if (ctx_running != NULL) {
1038
1316
    ctx_running->error_reason = error_str;
1039
  }
1040
1316
}
1041
1042
// Not possible to CONS_WITH_GC in error_ctx_base (potential loop)
1043
8156
static void error_ctx_base(lbm_value err_val, bool has_at, lbm_value at, unsigned int row, unsigned int column) {
1044
1045
8156
  if (!check_infer_canary()) {
1046
    // If this happens the Runtime system is likely corrupt and
1047
    // a crash is imminent.
1048
    // A critical error is issues so that the crash can be handled.
1049
    // At a minimum the lbm runtime should be restarted.
1050
    lbm_critical_error();
1051
  }
1052
1053
8156
  if (!(lbm_hide_trapped_error &&
1054
	(ctx_running->flags & EVAL_CPS_CONTEXT_FLAG_TRAP_UNROLL_RETURN))) {
1055
8156
    print_error_message(err_val,
1056
			has_at,
1057
			at,
1058
			row,
1059
			column,
1060
8156
			ctx_running->row0,
1061
8156
			ctx_running->row1,
1062
8156
			ctx_running->id,
1063
8156
			ctx_running->name);
1064
  }
1065
1066
8156
  if (ctx_running->flags & EVAL_CPS_CONTEXT_FLAG_TRAP) {
1067
196
    if (lbm_heap_num_free() < 3) {
1068
      gc();
1069
    }
1070
1071
196
    if (lbm_heap_num_free() >= 3) {
1072
196
      lbm_value msg = lbm_cons(err_val, ENC_SYM_NIL);
1073
196
      msg = lbm_cons(lbm_enc_i(ctx_running->id), msg);
1074
196
      msg = lbm_cons(ENC_SYM_EXIT_ERROR, msg);
1075
196
      if (!lbm_is_symbol_merror(msg)) {
1076
196
        lbm_find_receiver_and_send(ctx_running->parent, msg);
1077
      }
1078
    }
1079
    // context dies.
1080

7960
  } else if ((ctx_running->flags & EVAL_CPS_CONTEXT_FLAG_TRAP_UNROLL_RETURN) &&
1081
      (err_val != ENC_SYM_FATAL_ERROR)) {
1082
    lbm_uint v;
1083
28336
    while (ctx_running->K.sp > 0) {
1084
28336
      lbm_pop(&ctx_running->K, &v);
1085
28336
      if (v == EXCEPTION_HANDLER) { // context continues executing.
1086
7952
        lbm_value *sptr = get_stack_ptr(ctx_running, 2);
1087
7952
        lbm_set_car(sptr[0], ENC_SYM_EXIT_ERROR);
1088
7952
        stack_reserve(ctx_running, 1)[0] = EXCEPTION_HANDLER;
1089
7952
        ctx_running->app_cont = true;
1090
7952
        ctx_running->r = err_val;
1091
7952
        longjmp(error_jmp_buf, 1);
1092
      }
1093
    }
1094
    err_val = ENC_SYM_FATAL_ERROR;
1095
  }
1096
204
  ctx_running->r = err_val;
1097
204
  finish_ctx();
1098
204
  longjmp(error_jmp_buf, 1);
1099
}
1100
1101
7904
static void error_at_ctx(lbm_value err_val, lbm_value at) {
1102
7904
  error_ctx_base(err_val, true, at, 0, 0);
1103
}
1104
1105
252
static void error_ctx(lbm_value err_val) {
1106
252
  error_ctx_base(err_val, false, 0, 0, 0);
1107
}
1108
1109
static void read_error_ctx(unsigned int row, unsigned int column) {
1110
  error_ctx_base(ENC_SYM_RERROR, false, 0, row, column);
1111
}
1112
1113
void lbm_critical_error(void) {
1114
  longjmp(critical_error_jmp_buf, 1);
1115
}
1116
1117
// successfully finish a context
1118
22195
static void ok_ctx(void) {
1119
22195
  if (ctx_running->flags & EVAL_CPS_CONTEXT_FLAG_TRAP) {
1120
    lbm_value msg;
1121

140
    WITH_GC(msg, lbm_heap_allocate_list_init(3,
1122
                                             ENC_SYM_EXIT_OK,
1123
                                             lbm_enc_i(ctx_running->id),
1124
                                             ctx_running->r));
1125
140
    lbm_find_receiver_and_send(ctx_running->parent, msg);
1126
  }
1127
22195
  finish_ctx();
1128
22195
}
1129
1130
93328586
static eval_context_t *dequeue_ctx_nm(eval_context_queue_t *q) {
1131
93328586
  if (q->last == NULL) {
1132
2106353
    return NULL;
1133
  }
1134
  // q->first should only be NULL if q->last is.
1135
91222233
  eval_context_t *res = q->first;
1136
1137
91222233
  if (q->first == q->last) { // One thing in queue
1138
88220339
    q->first = NULL;
1139
88220339
    q->last  = NULL;
1140
   } else {
1141
3001894
    q->first = q->first->next;
1142
3001894
    q->first->prev = NULL;
1143
  }
1144
91222233
  res->prev = NULL;
1145
91222233
  res->next = NULL;
1146
91222233
  return res;
1147
}
1148
1149
93328586
static void wake_up_ctxs_nm(void) {
1150
  lbm_uint t_now;
1151
1152
93328586
  if (timestamp_us_callback) {
1153
93328586
    t_now = timestamp_us_callback();
1154
  } else {
1155
    t_now = 0;
1156
  }
1157
1158
93328586
  eval_context_queue_t *q = &blocked;
1159
93328586
  eval_context_t *curr = q->first;
1160
1161
96684327
  while (curr != NULL) {
1162
    lbm_uint t_diff;
1163
3355741
    eval_context_t *next = curr->next;
1164
3355741
    if (LBM_IS_STATE_WAKE_UP_WAKABLE(curr->state)) {
1165
3124229
      if ( curr->timestamp > t_now) {
1166
        /* There was an overflow on the counter */
1167
#ifndef LBM64
1168
        t_diff = (0xFFFFFFFF - curr->timestamp) + t_now;
1169
#else
1170
        t_diff = (0xFFFFFFFFFFFFFFFF - curr->timestamp) + t_now;
1171
#endif
1172
      } else {
1173
3124229
        t_diff = t_now - curr->timestamp;
1174
      }
1175
1176
3124229
      if (t_diff >= curr->sleep_us) {
1177
31311
        eval_context_t *wake_ctx = curr;
1178
31311
        if (curr == q->last) {
1179
31270
          if (curr->prev) {
1180
2287
            q->last = curr->prev;
1181
2287
            q->last->next = NULL;
1182
          } else {
1183
28983
            q->first = NULL;
1184
28983
            q->last = NULL;
1185
          }
1186
41
        } else if (curr->prev == NULL) {
1187
41
          q->first = curr->next;
1188
41
          q->first->prev = NULL;
1189
        } else {
1190
          curr->prev->next = curr->next;
1191
          if (curr->next) {
1192
            curr->next->prev = curr->prev;
1193
          }
1194
        }
1195
31311
        wake_ctx->next = NULL;
1196
31311
        wake_ctx->prev = NULL;
1197
31311
        if (LBM_IS_STATE_TIMEOUT(curr->state)) {
1198
140
          mailbox_add_mail(wake_ctx, ENC_SYM_TIMEOUT);
1199
140
          wake_ctx->r = ENC_SYM_TIMEOUT;
1200
        }
1201
31311
        wake_ctx->state = LBM_THREAD_STATE_READY;
1202
31311
        enqueue_ctx_nm(&queue, wake_ctx);
1203
      }
1204
    }
1205
3355741
    curr = next;
1206
  }
1207
93328586
}
1208
1209
31258
static void yield_ctx(lbm_uint sleep_us) {
1210
31258
  if (is_atomic) atomic_error();
1211
31230
  if (timestamp_us_callback) {
1212
31230
    ctx_running->timestamp = timestamp_us_callback();
1213
31230
    ctx_running->sleep_us = sleep_us;
1214
31230
    ctx_running->state = LBM_THREAD_STATE_SLEEPING;
1215
  } else {
1216
    ctx_running->timestamp = 0;
1217
    ctx_running->sleep_us = 0;
1218
    ctx_running->state = LBM_THREAD_STATE_SLEEPING;
1219
  }
1220
31230
  ctx_running->r = ENC_SYM_TRUE;
1221
31230
  ctx_running->app_cont = true;
1222
31230
  enqueue_ctx(&blocked,ctx_running);
1223
31230
  ctx_running = NULL;
1224
31230
}
1225
1226
22652
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) {
1227
1228
22652
  if (!lbm_is_cons(program)) return -1;
1229
1230
22652
  eval_context_t *ctx = NULL;
1231
#ifdef LBM_ALWAYS_GC
1232
  {
1233
    lbm_uint roots[2] = {program, env};
1234
    lbm_gc_mark_roots(roots, 2);
1235
    gc();
1236
  }
1237
#endif
1238
22652
  ctx = (eval_context_t*)lbm_malloc(sizeof(eval_context_t));
1239
22652
  if (ctx == NULL) {
1240
    lbm_uint roots[2] = {program, env};
1241
    lbm_gc_mark_roots(roots, 2);
1242
    gc();
1243
    ctx = (eval_context_t*)lbm_malloc(sizeof(eval_context_t));
1244
  }
1245
22652
  if (ctx == NULL) return -1;
1246
#ifdef LBM_ALWAYS_GC
1247
  {
1248
    lbm_uint roots[2] = {program, env};
1249
    lbm_gc_mark_roots(roots, 2);
1250
    gc();
1251
  }
1252
#endif
1253
22652
  if (!lbm_stack_allocate(&ctx->K, stack_size)) {
1254
28
    lbm_uint roots[2] = {program, env};
1255
28
    lbm_gc_mark_roots(roots, 2);
1256
28
    gc();
1257
28
    if (!lbm_stack_allocate(&ctx->K, stack_size)) {
1258
28
      lbm_memory_free((lbm_uint*)ctx);
1259
28
      return -1;
1260
    }
1261
  }
1262
1263
22624
  lbm_value *mailbox = NULL;
1264
#ifdef LBM_ALWAYS_GC
1265
  {
1266
    lbm_uint roots[2] = {program, env};
1267
    lbm_gc_mark_roots(roots, 2);
1268
    gc();
1269
  }
1270
#endif
1271
22624
  mailbox = (lbm_value*)lbm_memory_allocate(EVAL_CPS_DEFAULT_MAILBOX_SIZE);
1272
22624
  if (mailbox == NULL) {
1273
    lbm_value roots[2] = {program, env};
1274
    lbm_gc_mark_roots(roots,2);
1275
    gc();
1276
    mailbox = (lbm_value *)lbm_memory_allocate(EVAL_CPS_DEFAULT_MAILBOX_SIZE);
1277
  }
1278
22624
  if (mailbox == NULL) {
1279
    lbm_stack_free(&ctx->K);
1280
    lbm_memory_free((lbm_uint*)ctx);
1281
    return -1;
1282
  }
1283
1284
  // TODO: Limit names to 19 chars + 1 char for 0? (or something similar).
1285
22624
  if (name) {
1286
140
    lbm_uint name_len = strlen(name) + 1;
1287
#ifdef LBM_ALWAYS_GC
1288
    {
1289
      lbm_uint roots[2] = {program, env};
1290
      lbm_gc_mark_roots(roots, 2);
1291
      gc();
1292
    }
1293
#endif
1294
140
    ctx->name = lbm_malloc(name_len);
1295
140
    if (ctx->name == NULL) {
1296
      lbm_value roots[2] = {program, env};
1297
      lbm_gc_mark_roots(roots, 2);
1298
      gc();
1299
      ctx->name = lbm_malloc(name_len);
1300
    }
1301
140
    if (ctx->name == NULL) {
1302
      lbm_stack_free(&ctx->K);
1303
      lbm_memory_free((lbm_uint*)mailbox);
1304
      lbm_memory_free((lbm_uint*)ctx);
1305
      return -1;
1306
    }
1307
140
    memcpy(ctx->name, name, name_len);
1308
  } else {
1309
22484
     ctx->name = NULL;
1310
  }
1311
1312
22624
  lbm_int cid = lbm_memory_address_to_ix((lbm_uint*)ctx);
1313
1314
22624
  ctx->program = lbm_cdr(program);
1315
22624
  ctx->curr_exp = lbm_car(program);
1316
22624
  ctx->curr_env = env;
1317
22624
  ctx->r = ENC_SYM_NIL;
1318
22624
  ctx->error_reason = NULL;
1319
22624
  ctx->mailbox = mailbox;
1320
22624
  ctx->mailbox_size = EVAL_CPS_DEFAULT_MAILBOX_SIZE;
1321
22624
  ctx->flags = context_flags;
1322
22624
  ctx->num_mail = 0;
1323
22624
  ctx->app_cont = false;
1324
22624
  ctx->timestamp = 0;
1325
22624
  ctx->sleep_us = 0;
1326
22624
  ctx->state = LBM_THREAD_STATE_READY;
1327
22624
  ctx->prev = NULL;
1328
22624
  ctx->next = NULL;
1329
1330
22624
  ctx->row0 = -1;
1331
22624
  ctx->row1 = -1;
1332
1333
22624
  ctx->id = cid;
1334
22624
  ctx->parent = parent;
1335
1336
22624
  if (!lbm_push(&ctx->K, DONE)) {
1337
    lbm_memory_free((lbm_uint*)ctx->mailbox);
1338
    lbm_stack_free(&ctx->K);
1339
    lbm_memory_free((lbm_uint*)ctx);
1340
    return -1;
1341
  }
1342
1343
22624
  enqueue_ctx(&queue,ctx);
1344
1345
22624
  return ctx->id;
1346
}
1347
1348
21588
lbm_cid lbm_create_ctx(lbm_value program, lbm_value env, lbm_uint stack_size, char *name) {
1349
  // Creates a parentless context.
1350
21588
  return lbm_create_ctx_parent(program,
1351
                               env,
1352
                               stack_size,
1353
                               -1,
1354
                               EVAL_CPS_CONTEXT_FLAG_NOTHING,
1355
                               name);
1356
}
1357
1358
140
bool lbm_mailbox_change_size(eval_context_t *ctx, lbm_uint new_size) {
1359
1360
140
  lbm_value *mailbox = NULL;
1361
#ifdef LBM_ALWAYS_GC
1362
  gc();
1363
#endif
1364
140
  mailbox = (lbm_value*)lbm_memory_allocate(new_size);
1365
140
  if (mailbox == NULL) {
1366
28
    gc();
1367
28
    mailbox = (lbm_value *)lbm_memory_allocate(new_size);
1368
  }
1369
140
  if (mailbox == NULL) {
1370
28
    return false;
1371
  }
1372
1373
112
  for (lbm_uint i = 0; i < ctx->num_mail; i ++ ) {
1374
    mailbox[i] = ctx->mailbox[i];
1375
  }
1376
112
  lbm_memory_free(ctx->mailbox);
1377
112
  ctx->mailbox = mailbox;
1378
112
  ctx->mailbox_size = (uint32_t)new_size;
1379
112
  return true;
1380
}
1381
1382
6188
static void mailbox_remove_mail(eval_context_t *ctx, lbm_uint ix) {
1383
1384
22121
  for (lbm_uint i = ix; i < ctx->num_mail-1; i ++) {
1385
15933
    ctx->mailbox[i] = ctx->mailbox[i+1];
1386
  }
1387
6188
  ctx->num_mail --;
1388
6188
}
1389
1390
7140
static bool mailbox_add_mail(eval_context_t *ctx, lbm_value mail) {
1391
1392
7140
  if (ctx->num_mail >= ctx->mailbox_size) {
1393
588
    mailbox_remove_mail(ctx, 0);
1394
  }
1395
1396
7140
  ctx->mailbox[ctx->num_mail] = mail;
1397
7140
  ctx->num_mail ++;
1398
7140
  return true;
1399
}
1400
1401
/**************************************************************
1402
 * Advance execution to the next expression in the program.
1403
 * Assumes programs are not malformed. Apply_eval_program
1404
 * ensures programs are lists ending in nil. The reader
1405
 * ensures this likewise.
1406
 *************************************************************/
1407
64611
static void advance_ctx(eval_context_t *ctx) {
1408
64611
  if (ctx->program) { // fast not-nil check,  assume cons if not nil.
1409
42500
    stack_reserve(ctx, 1)[0] = DONE;
1410
42500
    lbm_cons_t *cell = lbm_ref_cell(ctx->program);
1411
42500
    ctx->curr_exp = cell->car;
1412
42500
    ctx->program = cell->cdr;
1413
42500
    ctx->curr_env = ENC_SYM_NIL;
1414
  } else {
1415
22111
    if (ctx_running == ctx) {  // This should always be the case because of odd historical reasons.
1416
22111
      ok_ctx();
1417
    }
1418
  }
1419
64611
}
1420
1421
84
bool lbm_unblock_ctx(lbm_cid cid, lbm_flat_value_t *fv) {
1422
84
  return event_internal(LBM_EVENT_UNBLOCK_CTX, (lbm_uint)cid, (lbm_uint)fv->buf, fv->buf_size);
1423
}
1424
1425
28
bool lbm_unblock_ctx_r(lbm_cid cid) {
1426
28
  mutex_lock(&blocking_extension_mutex);
1427
28
  bool r = false;
1428
28
  eval_context_t *found = NULL;
1429
28
  mutex_lock(&qmutex);
1430
28
  found = lookup_ctx_nm(&blocked, cid);
1431

28
  if (found && (LBM_IS_STATE_UNBLOCKABLE(found->state))) {
1432
28
    drop_ctx_nm(&blocked,found);
1433
28
    found->state = LBM_THREAD_STATE_READY;
1434
28
    enqueue_ctx_nm(&queue,found);
1435
28
    r = true;
1436
  }
1437
28
  mutex_unlock(&qmutex);
1438
28
  mutex_unlock(&blocking_extension_mutex);
1439
28
  return r;
1440
}
1441
1442
// unblock unboxed is also safe for rmbr:ed things.
1443
bool lbm_unblock_ctx_unboxed(lbm_cid cid, lbm_value unboxed) {
1444
  mutex_lock(&blocking_extension_mutex);
1445
  bool r = false;
1446
  eval_context_t *found = NULL;
1447
  mutex_lock(&qmutex);
1448
  found = lookup_ctx_nm(&blocked, cid);
1449
  if (found && (LBM_IS_STATE_UNBLOCKABLE(found->state))) {
1450
    drop_ctx_nm(&blocked,found);
1451
    found->r = unboxed;
1452
    if (lbm_is_error(unboxed)) {
1453
      get_stack_ptr(found, 1)[0] = TERMINATE; // replace TOS
1454
      found->app_cont = true;
1455
    }
1456
    found->state = LBM_THREAD_STATE_READY;
1457
    enqueue_ctx_nm(&queue,found);
1458
    r = true;
1459
  }
1460
  mutex_unlock(&qmutex);
1461
  mutex_unlock(&blocking_extension_mutex);
1462
  return r;
1463
}
1464
1465
112
static bool lbm_block_ctx_base(bool timeout, float t_s) {
1466
112
  mutex_lock(&blocking_extension_mutex);
1467
112
  blocking_extension = true;
1468
112
  if (timeout) {
1469
    blocking_extension_timeout_us = S_TO_US(t_s);
1470
    blocking_extension_timeout = true;
1471
  } else {
1472
112
    blocking_extension_timeout = false;
1473
  }
1474
112
  return true;
1475
}
1476
1477
void lbm_block_ctx_from_extension_timeout(float s) {
1478
  lbm_block_ctx_base(true, s);
1479
}
1480
1481
112
void lbm_block_ctx_from_extension(void) {
1482
112
  lbm_block_ctx_base(false, 0);
1483
112
}
1484
1485
// todo: May need to pop rmbrs from stack, if present.
1486
// Suspect that the letting the discard cont run is really not a problem.
1487
// Either way will be quite confusing what happens to allocated things when undoing block.
1488
void lbm_undo_block_ctx_from_extension(void) {
1489
  blocking_extension = false;
1490
  blocking_extension_timeout_us = 0;
1491
  blocking_extension_timeout = false;
1492
  mutex_unlock(&blocking_extension_mutex);
1493
}
1494
1495
#define LBM_RECEIVER_FOUND 0
1496
#define LBM_RECEIVER_FOUND_MAIL_DELIVERY_FAILED -1
1497
#define LBM_RECEIVER_NOT_FOUND -2
1498
1499
10601
int lbm_find_receiver_and_send(lbm_cid cid, lbm_value msg) {
1500
10601
  mutex_lock(&qmutex);
1501
10601
  eval_context_t *found = NULL;
1502
1503
10601
  found = lookup_ctx_nm(&blocked, cid);
1504
10601
  if (found) {
1505
3001
    if (LBM_IS_STATE_RECV(found->state)) { // only if unblock receivers here.
1506
2987
      drop_ctx_nm(&blocked,found);
1507
2987
      found->state = LBM_THREAD_STATE_READY;
1508
2987
      enqueue_ctx_nm(&queue,found);
1509
    }
1510
3001
    if (!mailbox_add_mail(found, msg)) {
1511
      mutex_unlock(&qmutex);
1512
      return LBM_RECEIVER_FOUND_MAIL_DELIVERY_FAILED;
1513
    }
1514
3001
    mutex_unlock(&qmutex);
1515
3001
    return LBM_RECEIVER_FOUND;
1516
  }
1517
1518
7600
  found = lookup_ctx_nm(&queue, cid);
1519
7600
  if (found) {
1520
1003
    if (!mailbox_add_mail(found, msg)) {
1521
      mutex_unlock(&qmutex);
1522
      return LBM_RECEIVER_FOUND_MAIL_DELIVERY_FAILED;
1523
    }
1524
1003
    mutex_unlock(&qmutex);
1525
1003
    return LBM_RECEIVER_FOUND;
1526
  }
1527
1528
  /* check the current context */
1529

6597
  if (ctx_running && ctx_running->id == cid) {
1530
2996
    if (!mailbox_add_mail(ctx_running, msg)) {
1531
      mutex_unlock(&qmutex);
1532
      return LBM_RECEIVER_FOUND_MAIL_DELIVERY_FAILED;
1533
    }
1534
2996
    mutex_unlock(&qmutex);
1535
2996
    return LBM_RECEIVER_FOUND;
1536
  }
1537
3601
  mutex_unlock(&qmutex);
1538
3601
  return LBM_RECEIVER_NOT_FOUND;
1539
}
1540
1541
// a match binder looks like (? x) or (? _) for example.
1542
// It is a list of two elements where the first is a ? and the second is a symbol.
1543
22356
static inline lbm_value get_match_binder_variable(lbm_value exp) {
1544
22356
  lbm_value var = ENC_SYM_NIL; // 0 false
1545
22356
  if (lbm_is_cons(exp)) {
1546
14908
    lbm_cons_t *e_cell = lbm_ref_cell(exp);
1547
14908
    lbm_value bt = e_cell->car;
1548

14908
    if (bt == ENC_SYM_MATCH_ANY && lbm_is_cons(e_cell->cdr)) {
1549
8468
      var = lbm_ref_cell(e_cell->cdr)->car;
1550
    }
1551
  }
1552
22356
  return var;
1553
}
1554
1555
/* Pattern matching is currently implemented as a recursive
1556
   function and make use of stack relative to the size of
1557
   expressions that are being matched. */
1558
22356
static bool match(lbm_value p, lbm_value e, lbm_value *env, bool *gc) {
1559
22356
  bool r = false;
1560
22356
  lbm_value var = get_match_binder_variable(p);
1561
22356
  if (var) {
1562
8468
    lbm_value binding = lbm_cons(var, e);
1563
8468
    if (lbm_is_cons(binding)) {
1564
8456
      lbm_value new_env = lbm_cons(binding, *env);
1565
8456
      if (lbm_is_cons(new_env)) {
1566
8456
        *env = new_env;
1567
8456
        r = true;
1568
      }
1569
    }
1570
8468
    *gc = !r;
1571
13888
  } else  if (lbm_is_symbol(p)) {
1572
5768
    if (p == ENC_SYM_DONTCARE) r = true;
1573
4508
    else r = (p == e);
1574

8120
  } else if (lbm_is_cons(p) && lbm_is_cons(e) ) {
1575
5264
    lbm_cons_t *p_cell = lbm_ref_cell(p);
1576
5264
    lbm_cons_t *e_cell = lbm_ref_cell(e);
1577
5264
    lbm_value headp = p_cell->car;
1578
5264
    lbm_value tailp = p_cell->cdr;
1579
5264
    lbm_value heade = e_cell->car;
1580
5264
    lbm_value taile = e_cell->cdr;
1581
5264
    r = match(headp, heade, env, gc);
1582

5264
    r = r && match (tailp, taile, env, gc);
1583
  } else {
1584
2856
    r = struct_eq(p, e);
1585
  }
1586
22356
  return r;
1587
}
1588
1589
// Find match is not very picky about syntax.
1590
// A completely malformed recv form is most likely to
1591
// just return no_match.
1592
5686
static int find_match(lbm_value plist, lbm_value *earr, lbm_uint num, lbm_value *e, lbm_value *env) {
1593
1594
  // A pattern list is a list of pattern, expression lists.
1595
  // ( (p1 e1) (p2 e2) ... (pn en))
1596
5686
  lbm_value curr_p = plist;
1597
5686
  int n = 0;
1598
5686
  bool need_gc = false;
1599
6286
  for (int i = 0; i < (int)num; i ++ ) {
1600
6200
    lbm_value curr_e = earr[i];
1601
7528
    while (!lbm_is_symbol_nil(curr_p)) {
1602
6928
      lbm_value me = get_car(curr_p);
1603
6928
      if (match(get_car(me), curr_e, env, &need_gc)) {
1604
5600
        if (need_gc) return FM_NEED_GC;
1605
5600
        *e = get_cadr(me);
1606
1607
5600
        if (!lbm_is_symbol_nil(get_cadr(get_cdr(me)))) {
1608
          return FM_PATTERN_ERROR;
1609
        }
1610
5600
        return n;
1611
      }
1612
1328
      curr_p = get_cdr(curr_p);
1613
    }
1614
600
    curr_p = plist;       /* search all patterns against next exp */
1615
600
    n ++;
1616
  }
1617
1618
86
  return FM_NO_MATCH;
1619
}
1620
1621
/****************************************************/
1622
/* Garbage collection                               */
1623
1624
360891
static void mark_context(eval_context_t *ctx, void *arg1, void *arg2) {
1625
  (void) arg1;
1626
  (void) arg2;
1627
360891
  lbm_value roots[3] = {ctx->curr_exp, ctx->program, ctx->r};
1628
360891
  lbm_gc_mark_env(ctx->curr_env);
1629
360891
  lbm_gc_mark_roots(roots, 3);
1630
360891
  lbm_gc_mark_roots(ctx->mailbox, ctx->num_mail);
1631
360891
  lbm_gc_mark_aux(ctx->K.data, ctx->K.sp);
1632
360891
}
1633
1634
347733
static int gc(void) {
1635
347733
  if (ctx_running) {
1636
347705
    ctx_running->state = ctx_running->state | LBM_THREAD_STATE_GC_BIT;
1637
  }
1638
1639
347733
  gc_requested = false;
1640
347733
  lbm_gc_state_inc();
1641
1642
  // The freelist should generally be NIL when GC runs.
1643
347733
  lbm_nil_freelist();
1644
347733
  lbm_value *env = lbm_get_global_env();
1645
11475189
  for (int i = 0; i < GLOBAL_ENV_ROOTS; i ++) {
1646
11127456
    lbm_gc_mark_env(env[i]);
1647
  }
1648
1649
347733
  mutex_lock(&qmutex); // Lock the queues.
1650
                       // Any concurrent messing with the queues
1651
                       // while doing GC cannot possibly be good.
1652
347733
  queue_iterator_nm(&queue, mark_context, NULL, NULL);
1653
347733
  queue_iterator_nm(&blocked, mark_context, NULL, NULL);
1654
1655
347733
  if (ctx_running) {
1656
347705
    mark_context(ctx_running, NULL, NULL);
1657
  }
1658
347733
  mutex_unlock(&qmutex);
1659
1660
#ifdef VISUALIZE_HEAP
1661
  heap_vis_gen_image();
1662
#endif
1663
1664
347733
  int r = lbm_gc_sweep_phase();
1665
347733
  lbm_heap_new_freelist_length();
1666
347733
  lbm_memory_update_min_free();
1667
1668
347733
  if (ctx_running) {
1669
347705
    ctx_running->state = ctx_running->state & ~LBM_THREAD_STATE_GC_BIT;
1670
  }
1671
347733
  return r;
1672
}
1673
1674
13812
int lbm_perform_gc(void) {
1675
13812
  return gc();
1676
}
1677
1678
/****************************************************/
1679
/* Evaluation functions                             */
1680
1681
1682
224402016
static void eval_symbol(eval_context_t *ctx) {
1683
224402016
  lbm_uint s = lbm_dec_sym(ctx->curr_exp);
1684
224402016
  if (s >= RUNTIME_SYMBOLS_START) {
1685
145524450
    lbm_value res = ENC_SYM_NIL;
1686

171569526
    if (lbm_env_lookup_b(&res, ctx->curr_exp, ctx->curr_env) ||
1687
26045076
        lbm_global_env_lookup(&res, ctx->curr_exp)) {
1688
145519606
      ctx->r =  res;
1689
145519606
      ctx->app_cont = true;
1690
145519606
      return;
1691
    }
1692
    // Dynamic load attempt
1693
    // Only symbols of kind RUNTIME can be dynamically loaded.
1694
4844
    const char *sym_str = lbm_get_name_by_symbol(s);
1695
4844
    const char *code_str = NULL;
1696
4844
    if (!dynamic_load_callback(sym_str, &code_str)) {
1697
84
      error_at_ctx(ENC_SYM_NOT_FOUND, ctx->curr_exp);
1698
    }
1699
4760
    lbm_value *sptr = stack_reserve(ctx, 3);
1700
4760
    sptr[0] = ctx->curr_exp;
1701
4760
    sptr[1] = ctx->curr_env;
1702
4760
    sptr[2] = RESUME;
1703
1704
4760
    lbm_value chan = ENC_SYM_NIL;
1705
#ifdef LBM_ALWAYS_GC
1706
    gc();
1707
#endif
1708
4760
    if (!create_string_channel((char *)code_str, &chan, ENC_SYM_NIL)) {
1709
      gc();
1710
      if (!create_string_channel((char *)code_str, &chan, ENC_SYM_NIL)) {
1711
        error_ctx(ENC_SYM_MERROR);
1712
      }
1713
    }
1714
1715
    // Here, chan has either been assigned or execution has terminated.
1716
1717
    lbm_value loader;
1718

4760
    WITH_GC_RMBR_1(loader, lbm_heap_allocate_list_init(2,
1719
                                                       ENC_SYM_READ,
1720
                                                       chan), chan);
1721
    lbm_value evaluator;
1722

4760
    WITH_GC_RMBR_1(evaluator, lbm_heap_allocate_list_init(2,
1723
                                                          ENC_SYM_EVAL,
1724
                                                          loader), loader);
1725
4760
    ctx->curr_exp = evaluator;
1726
4760
    ctx->curr_env = ENC_SYM_NIL; // dynamics should be evaluable in empty local env
1727
  } else {
1728
    //special symbols and extensions can be handled the same way.
1729
78877566
    ctx->r = ctx->curr_exp;
1730
78877566
    ctx->app_cont = true;
1731
  }
1732
}
1733
1734
// (quote e) => e
1735
4660259
static void eval_quote(eval_context_t *ctx) {
1736
4660259
  ctx->r = get_cadr(ctx->curr_exp);
1737
4660259
  ctx->app_cont = true;
1738
4660259
}
1739
1740
// a => a
1741
96775550
static void eval_selfevaluating(eval_context_t *ctx) {
1742
96775550
  ctx->r = ctx->curr_exp;
1743
96775550
  ctx->app_cont = true;
1744
96775550
}
1745
1746
// (progn e1 ... en)
1747
14310180
static void eval_progn(eval_context_t *ctx) {
1748
14310180
  lbm_value exps = get_cdr(ctx->curr_exp);
1749
1750
14310180
  if (lbm_is_cons(exps)) {
1751
14310152
    lbm_cons_t *cell = lbm_ref_cell(exps); // already checked that it's cons.
1752
14310152
    ctx->curr_exp = cell->car;
1753
14310152
    if (lbm_is_cons(cell->cdr)) { // malformed progn not ending in nil is tolerated
1754
11507968
      lbm_uint *sptr = stack_reserve(ctx, 4);
1755
11507968
      sptr[0] = ctx->curr_env; // env to restore between expressions in progn
1756
11507968
      sptr[1] = lbm_enc_u(0);  // Has env been copied (needed for progn local bindings)
1757
11507968
      sptr[2] = cell->cdr;     // Requirement: sptr[2] is a cons.
1758
11507968
      sptr[3] = PROGN_REST;
1759
    }
1760
28
  } else if (lbm_is_symbol_nil(exps)) { // Empty progn is nil
1761
28
    ctx->r = ENC_SYM_NIL;
1762
28
    ctx->app_cont = true;
1763
  } else {
1764
    error_ctx(ENC_SYM_EERROR);
1765
  }
1766
14310180
}
1767
1768
// (atomic e1 ... en)
1769
252
static void eval_atomic(eval_context_t *ctx) {
1770
252
  if (is_atomic) atomic_error();
1771
252
  stack_reserve(ctx, 1)[0] = EXIT_ATOMIC;
1772
252
  is_atomic = true;
1773
252
  eval_progn(ctx);
1774
252
}
1775
1776
// (call-cc (lambda (k) .... ))
1777
224
static void eval_callcc(eval_context_t *ctx) {
1778
  lbm_value cont_array;
1779
224
  lbm_uint *sptr0 = stack_reserve(ctx, 1);
1780
224
  sptr0[0] = is_atomic ? ENC_SYM_TRUE : ENC_SYM_NIL;
1781
#ifdef LBM_ALWAYS_GC
1782
  gc();
1783
#endif
1784
224
  if (!lbm_heap_allocate_lisp_array(&cont_array, ctx->K.sp)) {
1785
    gc();
1786
    lbm_heap_allocate_lisp_array(&cont_array, ctx->K.sp);
1787
  }
1788
224
  if (lbm_is_ptr(cont_array)) {
1789
224
    lbm_array_header_t *arr = assume_array(cont_array);
1790
224
    memcpy(arr->data, ctx->K.data, ctx->K.sp * sizeof(lbm_uint));
1791
    // The stored stack contains the is_atomic flag.
1792
    // This flag is overwritten in the following execution path.
1793
1794
224
    lbm_value acont = cons_with_gc(ENC_SYM_CONT, cont_array, ENC_SYM_NIL);
1795
224
    lbm_value arg_list = cons_with_gc(acont, ENC_SYM_NIL, ENC_SYM_NIL);
1796
    // Go directly into application evaluation without passing go
1797
224
    lbm_uint *sptr = stack_reserve(ctx, 2);
1798
224
    sptr0[0] = ctx->curr_env;
1799
224
    sptr[0] = arg_list;
1800
224
    sptr[1] = APPLICATION_START;
1801
224
    ctx->curr_exp = get_cadr(ctx->curr_exp);
1802
  } else {
1803
    // failed to create continuation array.
1804
    error_ctx(ENC_SYM_MERROR);
1805
  }
1806
224
}
1807
1808
// (call-cc-unsafe (lambda (k) ... ))
1809
// cc-unsafe: continuation should not be bound to any global directly or indirectly.
1810
// invoking the continuation must check that target SP holds a continuation that
1811
// can be applied using app_cont, otherwise error. The continuation need not be correct
1812
// in case user globally bound the continuation, but it may rule out disastrous failure.
1813
140
static void eval_call_cc_unsafe(eval_context_t *ctx) {
1814
140
  lbm_uint sp = ctx->K.sp;
1815
  // The stored stack contains the is_atomic flag.
1816
  // This flag is overwritten in the following execution path.
1817
140
  lbm_value acont = lbm_heap_allocate_list_init(3,
1818
                                                ENC_SYM_CONT_SP,
1819
                                                lbm_enc_i((int32_t)sp),
1820
140
                                                is_atomic ? ENC_SYM_TRUE : ENC_SYM_NIL, ENC_SYM_NIL);
1821
140
  lbm_value arg_list = cons_with_gc(acont, ENC_SYM_NIL, ENC_SYM_NIL);
1822
  // Go directly into application evaluation without passing go
1823
140
  lbm_uint *sptr = stack_reserve(ctx, 3);
1824
140
  sptr[0] = ctx->curr_env;
1825
140
  sptr[1] = arg_list;
1826
140
  sptr[2] = APPLICATION_START;
1827
140
  ctx->curr_exp = get_cadr(ctx->curr_exp);
1828
140
}
1829
1830
// (define sym exp)
1831
#define KEY 1
1832
#define VAL 2
1833
4267004
static void eval_define(eval_context_t *ctx) {
1834
  lbm_value parts[3];
1835
4267004
  lbm_value rest = extract_n(ctx->curr_exp, parts, 3);
1836
4267004
  lbm_uint *sptr = stack_reserve(ctx, 2);
1837

4267004
  if (lbm_is_symbol(parts[KEY]) && lbm_is_symbol_nil(rest)) {
1838
4267004
    lbm_uint sym_val = lbm_dec_sym(parts[KEY]);
1839
4267004
    sptr[0] = parts[KEY];
1840
4267004
    if (sym_val >= RUNTIME_SYMBOLS_START) {
1841
4267004
      sptr[1] = SET_GLOBAL_ENV;
1842
4267004
      if (ctx->flags & EVAL_CPS_CONTEXT_FLAG_CONST) {
1843
14
        stack_reserve(ctx, 1)[0] = MOVE_VAL_TO_FLASH_DISPATCH;
1844
      }
1845
4267004
      ctx->curr_exp = parts[VAL];
1846
4267004
      return;
1847
    }
1848
  }
1849
  error_at_ctx(ENC_SYM_EERROR, ctx->curr_exp);
1850
}
1851
1852
/* Allocate closure is only used in eval_lambda currently.
1853
   Inlining it should use no extra storage.
1854
 */
1855
12152
static inline lbm_value allocate_closure(lbm_value params, lbm_value body, lbm_value env) {
1856
1857
#ifdef LBM_ALWAYS_GC
1858
  gc();
1859
  if (lbm_heap_num_free() < 4) {
1860
    error_ctx(ENC_SYM_MERROR);
1861
  }
1862
#else
1863
12152
  if (lbm_heap_num_free() < 4) {
1864
    gc();
1865
    if (lbm_heap_num_free() < 4) {
1866
      error_ctx(ENC_SYM_MERROR);
1867
    }
1868
  }
1869
#endif
1870
  // The freelist will always contain just plain heap-cells.
1871
  // So dec_ptr is sufficient.
1872
12152
  lbm_value res = lbm_heap_state.freelist;
1873
  // CONS check is not needed. If num_free is correct, then freelist is a cons-cell.
1874
12152
  lbm_cons_t *heap = lbm_heap_state.heap;
1875
12152
  lbm_uint ix = lbm_dec_ptr(res);
1876
12152
  heap[ix].car = ENC_SYM_CLOSURE;
1877
12152
  ix = lbm_dec_ptr(heap[ix].cdr);
1878
12152
  heap[ix].car = params;
1879
12152
  ix = lbm_dec_ptr(heap[ix].cdr);
1880
12152
  heap[ix].car = body;
1881
12152
  ix = lbm_dec_ptr(heap[ix].cdr);
1882
12152
  heap[ix].car = env;
1883
12152
  lbm_heap_state.freelist = heap[ix].cdr;
1884
12152
  heap[ix].cdr = ENC_SYM_NIL;
1885
12152
  lbm_heap_state.num_alloc+=4;
1886
12152
  return res;
1887
}
1888
1889
/* Eval lambda is cheating, a lot! It does this
1890
   for performance reasons. The cheats are that
1891
   1. When  closure is created, a reference to the local env
1892
   in which the lambda was evaluated is added to the closure.
1893
   Ideally it should have created a list of free variables in the function
1894
   and then looked up the values of these creating a new environment.
1895
   2. The global env is considered global constant. As there is no copying
1896
   of environment bindings into the closure, undefine may break closures.
1897
1898
   some obscure programs such as test_setq_local_closure.lisp does not
1899
   work properly due to this cheating.
1900
 */
1901
// (lambda param-list body-exp) -> (closure param-list body-exp env)
1902
12152
static void eval_lambda(eval_context_t *ctx) {
1903
  lbm_value vals[3];
1904
12152
  extract_n(ctx->curr_exp, vals, 3);
1905
12152
  ctx->r = allocate_closure(vals[1],vals[2], ctx->curr_env);
1906
#ifdef CLEAN_UP_CLOSURES
1907
  lbm_uint sym_id  = 0;
1908
  if (clean_cl_env_symbol) {
1909
    lbm_value tail = cons_with_gc(ctx->r, ENC_SYM_NIL, ENC_SYM_NIL);
1910
    lbm_value app = cons_with_gc(clean_cl_env_symbol, tail, tail);
1911
    ctx->curr_exp = app;
1912
  } else if (lbm_get_symbol_by_name("clean-cl-env", &sym_id)) {
1913
    clean_cl_env_symbol = lbm_enc_sym(sym_id);
1914
    lbm_value tail = cons_with_gc(ctx->r, ENC_SYM_NIL, ENC_SYM_NIL);
1915
    lbm_value app = cons_with_gc(clean_cl_env_symbol, tail, tail);
1916
    ctx->curr_exp = app;
1917
  } else {
1918
    ctx->app_cont = true;
1919
  }
1920
#else
1921
12152
  ctx->app_cont = true;
1922
#endif
1923
12152
}
1924
1925
// (if cond-expr then-expr else-expr)
1926
21761606
static void eval_if(eval_context_t *ctx) {
1927
21761606
  lbm_value cdr = get_cdr(ctx->curr_exp);
1928
21761606
  lbm_value *sptr = stack_reserve(ctx, 3);
1929
21761606
  sptr[0] = get_cdr(cdr);
1930
21761606
  sptr[1] = ctx->curr_env;
1931
21761606
  sptr[2] = IF;
1932
21761606
  ctx->curr_exp = get_car(cdr);
1933
21761606
}
1934
1935
// (cond (cond-expr-1 expr-1)
1936
//         ...
1937
//       (cond-expr-N expr-N))
1938
1316
static void eval_cond(eval_context_t *ctx) {
1939
  lbm_value cond1[2];
1940
1316
  lbm_value rest_conds = extract_n(ctx->curr_exp, cond1, 2);
1941
1942
  // end recursion at (cond )
1943
1316
  if (lbm_is_symbol_nil(cond1[1])) {
1944
28
    ctx->r = ENC_SYM_NIL;
1945
28
    ctx->app_cont = true;
1946
  } else {
1947
    // Cond is one of the few places where a bit of syntax checking takes place at runtime..
1948
    // Maybe dont bother?
1949
1288
    lbm_uint len = lbm_list_length(cond1[1]);
1950
1288
    if (len != 2) {
1951
      lbm_set_error_reason("Incorrect syntax in cond");
1952
      error_ctx(ENC_SYM_EERROR);
1953
    }
1954
    lbm_value cond_expr[2];
1955
1288
    extract_n(cond1[1], cond_expr, 2);
1956
    lbm_value rest;
1957

1288
    WITH_GC(rest, lbm_heap_allocate_list_init(2,
1958
                                              cond_expr[1], // Then branch
1959
                                              cons_with_gc(ENC_SYM_COND, rest_conds , ENC_SYM_NIL)));
1960
1288
    lbm_value *sptr = stack_reserve(ctx, 3);
1961
1288
    sptr[0] = rest;
1962
1288
    sptr[1] = ctx->curr_env;
1963
1288
    sptr[2] = IF;
1964
1288
    ctx->curr_exp = cond_expr[0]; //condition;
1965
  }
1966
1316
}
1967
1968
11406
static void eval_app_cont(eval_context_t *ctx) {
1969
11406
  lbm_stack_drop(&ctx->K, 1);
1970
11406
  ctx->app_cont = true;
1971
11406
}
1972
1973
// Create a named location in an environment to later receive a value.
1974
40969702
static binding_location_status create_binding_location_internal(lbm_value key, lbm_value *env) {
1975
40969702
  if (lbm_type_of(key) == LBM_TYPE_SYMBOL) { // default case
1976

26935608
    if (key == ENC_SYM_NIL || key == ENC_SYM_DONTCARE) return BL_OK;
1977
    lbm_value binding;
1978
    lbm_value new_env_tmp;
1979
21325500
    binding = lbm_cons(key, ENC_SYM_PLACEHOLDER);
1980
21325500
    new_env_tmp = lbm_cons(binding, *env);
1981

21325500
    if (lbm_is_symbol(binding) || lbm_is_symbol(new_env_tmp)) {
1982
21656
      return BL_NO_MEMORY;
1983
    }
1984
21303844
    *env = new_env_tmp;
1985
14034094
  } else if (lbm_is_cons(key)) { // deconstruct case
1986
14034094
    int r = create_binding_location_internal(get_car(key), env);
1987
14034094
    if (r == BL_OK) {
1988
14027470
      r = create_binding_location_internal(get_cdr(key), env);
1989
    }
1990
14034094
    return r;
1991
  }
1992
21303844
  return BL_OK;
1993
}
1994
1995
12886482
static void create_binding_location(lbm_value key, lbm_value *env) {
1996
1997
12886482
  lbm_value env_tmp = *env;
1998
#ifdef LBM_ALWAYS_GC
1999
  lbm_gc_mark_phase(env_tmp);
2000
  gc();
2001
#endif
2002
12886482
  binding_location_status r = create_binding_location_internal(key, &env_tmp);
2003
12886482
  if (r != BL_OK) {
2004
21656
    if (r == BL_NO_MEMORY) {
2005
21656
      env_tmp = *env;
2006
21656
      lbm_gc_mark_phase(env_tmp);
2007
21656
      gc();
2008
21656
      r = create_binding_location_internal(key, &env_tmp);
2009
    }
2010

21656
    switch(r) {
2011
21656
    case BL_OK:
2012
21656
      break;
2013
    case BL_NO_MEMORY:
2014
      error_ctx(ENC_SYM_MERROR);
2015
      break;
2016
    case BL_INCORRECT_KEY:
2017
      error_ctx(ENC_SYM_TERROR);
2018
      break;
2019
    }
2020
12864826
  }
2021
12886482
  *env = env_tmp;
2022
12886482
}
2023
2024
12128844
static void let_bind_values_eval(lbm_value binds, lbm_value exp, lbm_value env, eval_context_t *ctx) {
2025
12128844
  if (lbm_is_cons(binds)) {
2026
      // Preallocate binding locations.
2027
12128844
      lbm_value curr = binds;
2028
24371592
      while (lbm_is_cons(curr)) {
2029
12242748
        lbm_value new_env_tmp = env;
2030
12242748
        lbm_cons_t *cell = lbm_ref_cell(curr); // already checked that cons.
2031
12242748
        lbm_value car_curr = cell->car;
2032
12242748
        lbm_value cdr_curr = cell->cdr;
2033
12242748
        lbm_value key = get_car(car_curr);
2034
12242748
        create_binding_location(key, &new_env_tmp);
2035
12242748
        env = new_env_tmp;
2036
12242748
        curr = cdr_curr;
2037
      }
2038
2039
12128844
      lbm_cons_t *cell = lbm_ref_cell(binds); // already checked that cons.
2040
12128844
      lbm_value car_binds = cell->car;
2041
12128844
      lbm_value cdr_binds = cell->cdr;
2042
      lbm_value key_val[2];
2043
12128844
      extract_n(car_binds, key_val, 2);
2044
2045
12128844
      lbm_uint *sptr = stack_reserve(ctx, 5);
2046
12128844
      sptr[0] = exp;
2047
12128844
      sptr[1] = cdr_binds;
2048
12128844
      sptr[2] = env;
2049
12128844
      sptr[3] = key_val[0];
2050
12128844
      sptr[4] = BIND_TO_KEY_REST;
2051
12128844
      ctx->curr_exp = key_val[1];
2052
12128844
      ctx->curr_env = env;
2053
    } else {
2054
      ctx->curr_exp = exp;
2055
    }
2056
12128844
}
2057
2058
// (var x (...)) - local binding inside of an progn
2059
// var has to take, place root-level nesting within progn.
2060
// (progn ... (var a 10) ...) OK!
2061
// (progn ... (something (var a 10)) ... ) NOT OK!
2062
/* progn stack
2063
   sp-4 : env
2064
   sp-3 : 0
2065
   sp-2 : rest
2066
   sp-1 : PROGN_REST
2067
*/
2068
643734
static void eval_var(eval_context_t *ctx) {
2069
643734
  if (ctx->K.sp >= 4) { // Possibly in progn
2070
643734
    lbm_value sv = ctx->K.data[ctx->K.sp - 1];
2071

643734
    if (IS_CONTINUATION(sv) && (sv == PROGN_REST)) {
2072
643734
      lbm_uint sp = ctx->K.sp;
2073
643734
      uint32_t is_copied = lbm_dec_as_u32(ctx->K.data[sp-3]);
2074
643734
      if (is_copied == 0) {
2075
        lbm_value env;
2076

632002
        WITH_GC(env, lbm_env_copy_spine(ctx->K.data[sp-4]));
2077
632002
        ctx->K.data[sp-3] = lbm_enc_u(1);
2078
632002
        ctx->K.data[sp-4] = env;
2079
      }
2080
643734
      lbm_value new_env = ctx->K.data[sp-4];
2081
643734
      lbm_value args = get_cdr(ctx->curr_exp);
2082
643734
      lbm_value key = get_car(args);
2083
2084
643734
      create_binding_location(key, &new_env);
2085
2086
643734
      ctx->K.data[sp-4] = new_env;
2087
2088
643734
      lbm_value v_exp = get_cadr(args);
2089
643734
      lbm_value *sptr = stack_reserve(ctx, 3);
2090
643734
      sptr[0] = new_env;
2091
643734
      sptr[1] = key;
2092
643734
      sptr[2] = PROGN_VAR;
2093
      // Activating the new environment before the evaluation of the value to be bound.
2094
      // This would normally shadow the existing value, but create_binding_location sets
2095
      // the binding to be $placeholder, which is ignored when looking up the value.
2096
      // The way closures work, the var-variable needs to be in scope during val
2097
      // evaluation for a recursive closure to be possible.
2098
643734
      ctx->curr_env = new_env;
2099
643734
      ctx->curr_exp = v_exp;
2100
643734
      return;
2101
    }
2102
  }
2103
  lbm_set_error_reason((char*)lbm_error_str_var_outside_progn);
2104
  error_ctx(ENC_SYM_EERROR);
2105
}
2106
2107
// (setq x (...)) - same as (set 'x (...)) or (setvar 'x (...))
2108
// does not error when given incorrect number of arguments.
2109
1775550
static void eval_setq(eval_context_t *ctx) {
2110
  lbm_value parts[3];
2111
1775550
  extract_n(ctx->curr_exp, parts, 3);
2112
1775550
  lbm_value *sptr = stack_reserve(ctx, 3);
2113
1775550
  sptr[0] = ctx->curr_env;
2114
1775550
  sptr[1] = parts[1];
2115
1775550
  sptr[2] = SETQ;
2116
1775550
  ctx->curr_exp = parts[2];
2117
1775550
}
2118
2119
364
static void eval_move_to_flash(eval_context_t *ctx) {
2120
364
  lbm_value args = get_cdr(ctx->curr_exp);
2121
364
  lbm_value *sptr = stack_reserve(ctx,2);
2122
364
  sptr[0] = args;
2123
364
  sptr[1] = MOVE_TO_FLASH;
2124
364
  ctx->app_cont = true;
2125
364
}
2126
2127
// (loop list-of-local-bindings
2128
//       condition-exp
2129
//       body-exp)
2130
280
static void eval_loop(eval_context_t *ctx) {
2131
280
  lbm_value env              = ctx->curr_env;
2132
  lbm_value parts[3];
2133
280
  extract_n(get_cdr(ctx->curr_exp), parts, 3);
2134
280
  lbm_value *sptr = stack_reserve(ctx, 3);
2135
280
  sptr[0] = parts[LOOP_BODY];
2136
280
  sptr[1] = parts[LOOP_COND];
2137
280
  sptr[2] = LOOP_CONDITION;
2138
280
  let_bind_values_eval(parts[LOOP_BINDS], parts[LOOP_COND], env, ctx);
2139
280
}
2140
2141
/* (trap expression)
2142
 *
2143
 * suggested use:
2144
 * (match (trap expression)
2145
 *   ((exit-error (? err)) (error-handler err))
2146
 *   ((exit-ok    (? v))   (value-handler v)))
2147
 */
2148
8120
static void eval_trap(eval_context_t *ctx) {
2149
2150
8120
  lbm_value expr = get_cadr(ctx->curr_exp);
2151
  lbm_value retval;
2152

8120
  WITH_GC(retval, lbm_heap_allocate_list(2));
2153
8120
  lbm_set_car(retval, ENC_SYM_EXIT_OK); // Assume things will go well.
2154
8120
  lbm_uint *sptr = stack_reserve(ctx,3);
2155
8120
  sptr[0] = retval;
2156
8120
  sptr[1] = ctx->flags;
2157
8120
  sptr[2] = EXCEPTION_HANDLER;
2158
8120
  ctx->flags |= EVAL_CPS_CONTEXT_FLAG_TRAP_UNROLL_RETURN;
2159
8120
  ctx->curr_exp = expr;
2160
8120
}
2161
2162
// (let list-of-binding s
2163
//      body-exp)
2164
12128564
static void eval_let(eval_context_t *ctx) {
2165
12128564
  lbm_value env      = ctx->curr_env;
2166
  lbm_value parts[3];
2167
12128564
  extract_n(ctx->curr_exp, parts, 3);
2168
12128564
  let_bind_values_eval(parts[1], parts[2], env, ctx);
2169
12128564
}
2170
2171
// (and exp0 ... expN)
2172
1982008
static void eval_and(eval_context_t *ctx) {
2173
1982008
  lbm_value rest = get_cdr(ctx->curr_exp);
2174
1982008
  if (lbm_is_symbol_nil(rest)) {
2175
28
    ctx->app_cont = true;
2176
28
    ctx->r = ENC_SYM_TRUE;
2177
  } else {
2178
1981980
    lbm_value *sptr = stack_reserve(ctx, 3);
2179
1981980
    sptr[0] = ctx->curr_env;
2180
1981980
    sptr[1] = get_cdr(rest);
2181
1981980
    sptr[2] = AND;
2182
1981980
    ctx->curr_exp = get_car(rest);
2183
  }
2184
1982008
}
2185
2186
// (or exp0 ... expN)
2187
7224
static void eval_or(eval_context_t *ctx) {
2188
7224
  lbm_value rest = get_cdr(ctx->curr_exp);
2189
7224
  if (lbm_is_symbol_nil(rest)) {
2190
28
    ctx->app_cont = true;
2191
28
    ctx->r = ENC_SYM_NIL;
2192
  } else {
2193
7196
    lbm_value *sptr = stack_reserve(ctx, 3);
2194
7196
    sptr[0] = ctx->curr_env;
2195
7196
    sptr[1] = get_cdr(rest);
2196
7196
    sptr[2] = OR;
2197
7196
    ctx->curr_exp = get_car(rest);
2198
  }
2199
7224
}
2200
2201
// Pattern matching
2202
// format:
2203
// (match e (pattern body)
2204
//          (pattern body)
2205
//          ...  )
2206
//
2207
// There can be an optional pattern guard:
2208
// (match e (pattern guard body)
2209
//          ... )
2210
// a guard is a boolean expression.
2211
// Guards make match, pattern matching more complicated
2212
// than the recv pattern matching and requires staged execution
2213
// via the continuation system rather than a while loop over a list.
2214
2828
static void eval_match(eval_context_t *ctx) {
2215
2216
2828
  lbm_value rest = get_cdr(ctx->curr_exp);
2217
2828
  if (lbm_is_cons(rest)) {
2218
2828
    lbm_cons_t *cell = lbm_ref_cell(rest);
2219
2828
    lbm_value cdr_rest = cell->cdr;
2220
2828
    ctx->curr_exp = cell->car;
2221
2828
    lbm_value *sptr = stack_reserve(ctx, 3);
2222
2828
    sptr[0] = cdr_rest;
2223
2828
    sptr[1] = ctx->curr_env;
2224
2828
    sptr[2] = MATCH;
2225
  } else {
2226
    // syntax error to not include at least one pattern
2227
    error_ctx(ENC_SYM_EERROR);
2228
  }
2229
2828
}
2230
2231
8559
static void receive_base(eval_context_t *ctx, lbm_value pats) {
2232
8559
  if (ctx->num_mail == 0) {
2233
3069
      block_current_ctx(LBM_THREAD_STATE_RECV_BL,0,false);
2234
  } else {
2235
5490
    lbm_value *msgs = ctx->mailbox;
2236
5490
    lbm_uint  num   = ctx->num_mail;
2237
2238
    lbm_value e;
2239
5490
    lbm_value new_env = ctx->curr_env;
2240
#ifdef LBM_ALWAYS_GC
2241
    gc();
2242
#endif
2243
5490
    int n = find_match(pats, msgs, num, &e, &new_env);
2244
5490
    if (n == FM_NEED_GC) {
2245
      gc();
2246
      new_env = ctx->curr_env;
2247
      n = find_match(pats, msgs, num, &e, &new_env);
2248
      if (n == FM_NEED_GC) {
2249
        error_ctx(ENC_SYM_MERROR);
2250
      }
2251
    }
2252
5490
    if (n == FM_PATTERN_ERROR) {
2253
      lbm_set_error_reason("Incorrect pattern format for recv");
2254
      error_at_ctx(ENC_SYM_EERROR,pats);
2255
5490
    } else if (n >= 0 ) { /* Match */
2256
5488
      mailbox_remove_mail(ctx, (lbm_uint)n);
2257
5488
      ctx->curr_env = new_env;
2258
5488
      ctx->curr_exp = e;
2259
    } else { /* No match  go back to sleep */
2260
2
      ctx->r = ENC_SYM_NO_MATCH;
2261
2
      block_current_ctx(LBM_THREAD_STATE_RECV_BL, 0,false);
2262
    }
2263
  }
2264
8559
  return;
2265
}
2266
2267
// Receive-timeout
2268
// (recv-to timeout (pattern expr)
2269
//                  (pattern expr))
2270
252
static void eval_receive_timeout(eval_context_t *ctx) {
2271
252
  if (is_atomic) atomic_error();
2272
252
  lbm_value timeout_val = get_cadr(ctx->curr_exp);
2273
252
  lbm_value pats = get_cdr(get_cdr(ctx->curr_exp));
2274
252
  if (lbm_is_symbol_nil(pats)) {
2275
56
    lbm_set_error_reason((char*)lbm_error_str_num_args);
2276
56
    error_at_ctx(ENC_SYM_EERROR, ctx->curr_exp);
2277
  } else {
2278
196
    lbm_value *sptr = stack_reserve(ctx, 2);
2279
196
    sptr[0] = pats;
2280
196
    sptr[1] = RECV_TO;
2281
196
    ctx->curr_exp = timeout_val;
2282
  }
2283
196
}
2284
2285
// Receive
2286
// (recv (pattern expr)
2287
//       (pattern expr))
2288
8587
static void eval_receive(eval_context_t *ctx) {
2289
8587
  if (is_atomic) atomic_error();
2290
8587
  lbm_value pats = get_cdr(ctx->curr_exp);
2291
8587
  if (lbm_is_symbol_nil(pats)) {
2292
28
    lbm_set_error_reason((char*)lbm_error_str_num_args);
2293
28
    error_at_ctx(ENC_SYM_EERROR,ctx->curr_exp);
2294
  } else {
2295
8559
    receive_base(ctx, pats);
2296
  }
2297
8559
}
2298
2299
/*********************************************************/
2300
/*  Continuation functions                               */
2301
2302
// cont_set_global_env:
2303
//
2304
//   s[sp-1] = Key-symbol
2305
//
2306
//   ctx->r = Value
2307
4267508
static void cont_set_global_env(eval_context_t *ctx){
2308
2309
  lbm_value key;
2310
4267508
  lbm_value val = ctx->r;
2311
2312
4267508
  lbm_pop(&ctx->K, &key);
2313
4267508
  lbm_uint dec_key = lbm_dec_sym(key);
2314
4267508
  lbm_uint ix_key  = dec_key & GLOBAL_ENV_MASK;
2315
4267508
  lbm_value *global_env = lbm_get_global_env();
2316
4267508
  lbm_uint orig_env = global_env[ix_key];
2317
  lbm_value new_env;
2318
  // A key is a symbol and should not need to be remembered.
2319

4267508
  WITH_GC(new_env, lbm_env_set(orig_env,key,val));
2320
2321
4267508
  global_env[ix_key] = new_env;
2322
4267508
  ctx->r = val;
2323
2324
4267508
  ctx->app_cont = true;
2325
2326
4267508
  return;
2327
}
2328
2329
// cont_resume:
2330
//
2331
// s[sp-2] = Expression
2332
// s[sp-1] = Environment
2333
//
2334
// ctx->r = Irrelevant.
2335
4760
static void cont_resume(eval_context_t *ctx) {
2336
4760
  lbm_pop_2(&ctx->K, &ctx->curr_env, &ctx->curr_exp);
2337
4760
}
2338
2339
// cont_progn_rest:
2340
//
2341
// s[sp-3] = Environment to evaluate each expression in.
2342
// s[sp-2] = Flag indicating if env has been copied.
2343
// s[sp-1] = list of expressions to evaluate.
2344
//
2345
// ctx->r = Result of last evaluated expression.
2346
13862596
static void cont_progn_rest(eval_context_t *ctx) {
2347
13862596
  lbm_value *sptr = get_stack_ptr(ctx, 3);
2348
2349
13862596
  lbm_value env  = sptr[0];
2350
  // eval_progn and cont_progn_rest both ensure that sptr[2] is a list
2351
  // whenever cont_progn_rest is called.
2352
2353
13862596
  lbm_cons_t *rest_cell = lbm_ref_cell(sptr[2]);
2354
13862596
  lbm_value rest_cdr = rest_cell->cdr;
2355
13862596
  ctx->curr_exp = rest_cell->car;;
2356
13862596
  ctx->curr_env = env;
2357
13862596
  if (lbm_is_cons(rest_cdr)) {
2358
2354894
    sptr[2] = rest_cdr; // Requirement: rest_cdr is a cons
2359
2354894
    stack_reserve(ctx, 1)[0] = PROGN_REST;
2360
  } else {
2361
    // Nothing is pushed to stack for final element in progn. (tail-call req)
2362
11507702
    lbm_stack_drop(&ctx->K, 3);
2363
  }
2364
13862596
}
2365
2366
84
static void cont_wait(eval_context_t *ctx) {
2367
2368
  lbm_value cid_val;
2369
84
  lbm_pop(&ctx->K, &cid_val);
2370
84
  lbm_cid cid = (lbm_cid)lbm_dec_i(cid_val);
2371
2372
84
  bool exists = false;
2373
2374
84
  lbm_blocked_iterator(context_exists, &cid, &exists);
2375
84
  lbm_running_iterator(context_exists, &cid, &exists);
2376
2377
84
  if (ctx_running->id == cid) {
2378
    exists = true;
2379
  }
2380
2381
84
  if (exists) {
2382
28
    lbm_value *sptr = stack_reserve(ctx, 2);
2383
28
    sptr[0] = lbm_enc_i(cid);
2384
28
    sptr[1] = WAIT;
2385
28
    ctx->r = ENC_SYM_TRUE;
2386
28
    ctx->app_cont = true;
2387
28
    yield_ctx(50000);
2388
  } else {
2389
56
    ctx->r = ENC_SYM_TRUE;
2390
56
    ctx->app_cont = true;
2391
  }
2392
84
}
2393
2394
1775844
static lbm_value perform_setvar(lbm_value key, lbm_value val, lbm_value env) {
2395
2396
1775844
  lbm_uint s = lbm_dec_sym(key);
2397
1775844
  if (s >= RUNTIME_SYMBOLS_START) {
2398
1775816
    lbm_value new_env = lbm_env_modify_binding(env, key, val);
2399

1775816
    if (lbm_is_symbol(new_env) && new_env == ENC_SYM_NOT_FOUND) {
2400
841372
      lbm_uint ix_key = lbm_dec_sym(key) & GLOBAL_ENV_MASK;
2401
841372
      lbm_value *glob_env = lbm_get_global_env();
2402
841372
      new_env = lbm_env_modify_binding(glob_env[ix_key], key, val);
2403
841372
      glob_env[ix_key] = new_env;
2404
    }
2405

1775816
    if (lbm_is_symbol(new_env) && new_env == ENC_SYM_NOT_FOUND) {
2406
28
      lbm_set_error_reason((char*)lbm_error_str_variable_not_bound);
2407
28
      error_at_ctx(ENC_SYM_NOT_FOUND, key);
2408
    }
2409
1775788
    return val;
2410
  }
2411
28
  error_at_ctx(ENC_SYM_EERROR, ENC_SYM_SETVAR);
2412
  return ENC_SYM_NIL; // unreachable
2413
}
2414
2415
420
static void apply_setvar(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) {
2416

420
  if (nargs == 2 && lbm_is_symbol(args[0])) {
2417
    lbm_value res;
2418

308
    WITH_GC(res, perform_setvar(args[0], args[1], ctx->curr_env));
2419
308
    ctx->r = args[1];
2420
308
    lbm_stack_drop(&ctx->K, nargs+1);
2421
308
    ctx->app_cont = true;
2422
  } else {
2423
112
    if (nargs == 2) lbm_set_error_reason((char*)lbm_error_str_incorrect_arg);
2424
56
    else lbm_set_error_reason((char*)lbm_error_str_num_args);
2425
112
    error_at_ctx(ENC_SYM_EERROR, ENC_SYM_SETVAR);
2426
  }
2427
308
}
2428
2429
2430
#define READING_EXPRESSION             ((0 << LBM_VAL_SHIFT) | LBM_TYPE_U)
2431
#define READING_PROGRAM                ((1 << LBM_VAL_SHIFT) | LBM_TYPE_U)
2432
#define READING_PROGRAM_INCREMENTALLY  ((2 << LBM_VAL_SHIFT) | LBM_TYPE_U)
2433
2434
330428
static void apply_read_base(lbm_value *args, lbm_uint nargs, eval_context_t *ctx, bool program, bool incremental) {
2435
330428
  if (nargs == 1) {
2436
330400
    lbm_value chan = ENC_SYM_NIL;
2437
330400
    if (lbm_type_of_functional(args[0]) == LBM_TYPE_ARRAY) {
2438
304052
      char *str = lbm_dec_str(args[0]);
2439
304052
      if (str) {
2440
#ifdef LBM_ALWAYS_GC
2441
        gc();
2442
#endif
2443
303940
        if (!create_string_channel(lbm_dec_str(args[0]), &chan, args[0])) {
2444
1288
          gc();
2445
1288
          if (!create_string_channel(lbm_dec_str(args[0]), &chan, args[0])) {
2446
            error_ctx(ENC_SYM_MERROR);
2447
          }
2448
        }
2449
      } else {
2450
112
        error_ctx(ENC_SYM_EERROR);
2451
      }
2452
26348
    } else if (lbm_type_of(args[0]) == LBM_TYPE_CHANNEL) {
2453
26348
      chan = args[0];
2454
      // Streaming transfers can freeze the evaluator if the stream is cut while
2455
      // the reader is reading inside of an atomic block.
2456
      // It is generally not advisable to read in an atomic block but now it is also
2457
      // enforced in the case where it can cause problems.
2458

26348
      if (lbm_channel_may_block(lbm_dec_channel(chan)) && is_atomic) {
2459
       lbm_set_error_reason((char*)lbm_error_str_forbidden_in_atomic);
2460
       is_atomic = false;
2461
       error_ctx(ENC_SYM_EERROR);
2462
      }
2463
    } else {
2464
      error_ctx(ENC_SYM_EERROR);
2465
    }
2466
330288
    lbm_value *sptr = get_stack_ptr(ctx, 2);
2467
2468
    // If we are inside a reader, its settings are stored.
2469
330288
    sptr[0] = lbm_enc_u(ctx->flags);  // flags stored.
2470
330288
    sptr[1] = chan;
2471
330288
    lbm_value  *rptr = stack_reserve(ctx,2);
2472

330288
    if (!program && !incremental) {
2473
297024
      rptr[0] = READING_EXPRESSION;
2474

33264
    } else if (program && !incremental) {
2475
11186
      rptr[0] = READING_PROGRAM;
2476

22078
    } else if (program && incremental) {
2477
22078
      rptr[0] = READING_PROGRAM_INCREMENTALLY;
2478
    }  // the last combo is illegal
2479
330288
    rptr[1] = READ_DONE;
2480
2481
    // Each reader starts in a fresh situation
2482
330288
    ctx->flags &= ~EVAL_CPS_CONTEXT_READER_FLAGS_MASK;
2483
330288
    ctx->r = ENC_SYM_NIL; // set r to a known state.
2484
2485
330288
    if (program) {
2486
33264
      if (incremental) {
2487
22078
        ctx->flags |= EVAL_CPS_CONTEXT_FLAG_INCREMENTAL_READ;
2488
22078
        lbm_value  *rptr1 = stack_reserve(ctx,3);
2489
22078
        rptr1[0] = chan;
2490
22078
        rptr1[1] = ctx->curr_env;
2491
22078
        rptr1[2] = READ_EVAL_CONTINUE;
2492
      } else {
2493
11186
        ctx->flags &= ~EVAL_CPS_CONTEXT_FLAG_INCREMENTAL_READ;
2494
11186
        lbm_value  *rptr1 = stack_reserve(ctx,4);
2495
11186
        rptr1[0] = ENC_SYM_NIL;
2496
11186
        rptr1[1] = ENC_SYM_NIL;
2497
11186
        rptr1[2] = chan;
2498
11186
        rptr1[3] = READ_APPEND_CONTINUE;
2499
      }
2500
    }
2501
330288
    rptr = stack_reserve(ctx,3); // reuse of variable rptr
2502
330288
    rptr[0] = chan;
2503
330288
    rptr[1] = lbm_enc_u(1);
2504
330288
    rptr[2] = READ_NEXT_TOKEN;
2505
330288
    ctx->app_cont = true;
2506
  } else {
2507
28
    lbm_set_error_reason((char*)lbm_error_str_num_args);
2508
28
    error_ctx(ENC_SYM_EERROR);
2509
  }
2510
330288
}
2511
2512
11270
static void apply_read_program(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) {
2513
11270
  apply_read_base(args,nargs,ctx,true,false);
2514
11186
}
2515
2516
22078
static void apply_read_eval_program(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) {
2517
22078
  apply_read_base(args,nargs,ctx,true,true);
2518
22078
}
2519
2520
297080
static void apply_read(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) {
2521
297080
  apply_read_base(args,nargs,ctx,false,false);
2522
297024
}
2523
2524
1064
static void apply_spawn_base(lbm_value *args, lbm_uint nargs, eval_context_t *ctx, uint32_t context_flags) {
2525
2526
1064
  lbm_uint stack_size = EVAL_CPS_DEFAULT_STACK_SIZE;
2527
1064
  lbm_uint closure_pos = 0;
2528
1064
  char *name = NULL;
2529
  // allowed arguments:
2530
  // (spawn opt-name opt-stack-size closure arg1 ... argN)
2531
2532

2128
  if (nargs >= 1 &&
2533
1064
      lbm_is_closure(args[0])) {
2534
840
    closure_pos = 0;
2535

448
  } else if (nargs >= 2 &&
2536
308
      lbm_is_number(args[0]) &&
2537
84
      lbm_is_closure(args[1])) {
2538
84
    stack_size = lbm_dec_as_u32(args[0]);
2539
84
    closure_pos = 1;
2540

280
  } else if (nargs >= 2 &&
2541
280
             lbm_is_array_r(args[0]) &&
2542
140
             lbm_is_closure(args[1])) {
2543
    name = lbm_dec_str(args[0]);
2544
    closure_pos = 1;
2545

280
  } else if (nargs >= 3 &&
2546
280
             lbm_is_array_r(args[0]) &&
2547
280
             lbm_is_number(args[1]) &&
2548
140
             lbm_is_closure(args[2])) {
2549
140
    stack_size = lbm_dec_as_u32(args[1]);
2550
140
    closure_pos = 2;
2551
140
    name = lbm_dec_str(args[0]);
2552
  } else {
2553
    if (context_flags & EVAL_CPS_CONTEXT_FLAG_TRAP)
2554
      error_at_ctx(ENC_SYM_TERROR,ENC_SYM_SPAWN_TRAP);
2555
    else
2556
      error_at_ctx(ENC_SYM_TERROR,ENC_SYM_SPAWN);
2557
  }
2558
2559
  lbm_value cl[3];
2560
1064
  extract_n(get_cdr(args[closure_pos]), cl, 3);
2561
1064
  lbm_value curr_param = cl[CLO_PARAMS];
2562
1064
  lbm_value clo_env    = cl[CLO_ENV];
2563
1064
  lbm_uint i = closure_pos + 1;
2564

1820
  while (lbm_is_cons(curr_param) && i <= nargs) {
2565
756
    lbm_value entry = cons_with_gc(get_car(curr_param), args[i], clo_env);
2566
756
    lbm_value aug_env = cons_with_gc(entry, clo_env,ENC_SYM_NIL);
2567
756
    clo_env = aug_env;
2568
756
    curr_param = get_cdr(curr_param);
2569
756
    i ++;
2570
  }
2571
2572
1064
  lbm_stack_drop(&ctx->K, nargs+1);
2573
2574
1064
  lbm_value program = cons_with_gc(cl[CLO_BODY], ENC_SYM_NIL, clo_env);
2575
2576
1064
  lbm_cid cid = lbm_create_ctx_parent(program,
2577
                                      clo_env,
2578
                                      stack_size,
2579
                                      lbm_get_current_cid(),
2580
                                      context_flags,
2581
                                      name);
2582
1064
  ctx->r = lbm_enc_i(cid);
2583
1064
  ctx->app_cont = true;
2584
1064
  if (cid == -1) error_ctx(ENC_SYM_MERROR); // Kill parent and signal out of memory.
2585
1036
}
2586
2587
728
static void apply_spawn(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) {
2588
728
  apply_spawn_base(args,nargs,ctx, EVAL_CPS_CONTEXT_FLAG_NOTHING);
2589
700
}
2590
2591
336
static void apply_spawn_trap(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) {
2592
336
  apply_spawn_base(args,nargs,ctx, EVAL_CPS_CONTEXT_FLAG_TRAP);
2593
336
}
2594
2595
28405
static void apply_yield(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) {
2596

56810
  if (nargs == 1 && lbm_is_number(args[0])) {
2597
28405
    lbm_uint ts = lbm_dec_as_u32(args[0]);
2598
28405
    lbm_stack_drop(&ctx->K, nargs+1);
2599
28405
    yield_ctx(ts);
2600
  } else {
2601
    lbm_set_error_reason((char*)lbm_error_str_no_number);
2602
    error_at_ctx(ENC_SYM_TERROR, ENC_SYM_YIELD);
2603
  }
2604
28405
}
2605
2606
2128
static void apply_sleep(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) {
2607

4228
  if (nargs == 1 && lbm_is_number(args[0])) {
2608
2128
    lbm_uint ts = (lbm_uint)(1000000.0f * lbm_dec_as_float(args[0]));
2609
2128
    lbm_stack_drop(&ctx->K, nargs+1);
2610
2128
    yield_ctx(ts);
2611
  } else {
2612
    lbm_set_error_reason((char*)lbm_error_str_no_number);
2613
    error_at_ctx(ENC_SYM_TERROR, ENC_SYM_SLEEP);
2614
  }
2615
2100
}
2616
2617
56
static void apply_wait(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) {
2618

112
  if (nargs == 1 && lbm_type_of(args[0]) == LBM_TYPE_I) {
2619
56
    lbm_cid cid = (lbm_cid)lbm_dec_i(args[0]);
2620
56
    lbm_value *sptr = get_stack_ptr(ctx, 2);
2621
56
    sptr[0] = lbm_enc_i(cid);
2622
56
    sptr[1] = WAIT;
2623
56
    ctx->r = ENC_SYM_TRUE;
2624
56
    ctx->app_cont = true;
2625
56
    yield_ctx(50000);
2626
  } else {
2627
    error_at_ctx(ENC_SYM_TERROR, ENC_SYM_WAIT);
2628
  }
2629
56
}
2630
2631
/* (eval expr)
2632
   (eval env expr) */
2633
3181444
static void apply_eval(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) {
2634
3181444
  if ( nargs == 1) {
2635
3181444
    ctx->curr_exp = args[0];
2636
  } else if (nargs == 2) {
2637
    ctx->curr_exp = args[1];
2638
    ctx->curr_env = args[0];
2639
  } else {
2640
    lbm_set_error_reason((char*)lbm_error_str_num_args);
2641
    error_at_ctx(ENC_SYM_EERROR, ENC_SYM_EVAL);
2642
  }
2643
3181444
  lbm_stack_drop(&ctx->K, nargs+1);
2644
3181444
}
2645
2646
11410
static void apply_eval_program(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) {
2647
11410
  int prg_pos = 0;
2648
11410
  if (nargs == 2) {
2649
    prg_pos = 1;
2650
    ctx->curr_env = args[0]; // No check that args[0] is an actual env.
2651
  }
2652

11410
  if (nargs == 1 || nargs == 2) {
2653
11410
    lbm_value prg = args[prg_pos]; // No check that this is a program.
2654
    lbm_value app_cont;
2655
    lbm_value app_cont_prg;
2656
    lbm_value new_prg;
2657
    lbm_value prg_copy;
2658
2659
11410
    int len = -1;
2660

11410
    WITH_GC(prg_copy, lbm_list_copy(&len, prg));
2661
11410
    lbm_stack_drop(&ctx->K, nargs+1);
2662
    // There is always a continuation (DONE).
2663
    // If ctx->program is nil, the stack should contain DONE.
2664
    // after adding an intermediate done for prg, stack becomes DONE, DONE.
2665
11410
    app_cont = cons_with_gc(ENC_SYM_APP_CONT, ENC_SYM_NIL, prg_copy);
2666
11410
    app_cont_prg = cons_with_gc(app_cont, ENC_SYM_NIL, prg_copy);
2667
11410
    new_prg = lbm_list_append(app_cont_prg, ctx->program);
2668
11410
    new_prg = lbm_list_append(prg_copy, new_prg);
2669
    // new_prg is guaranteed to be a cons cell or nil
2670
    // even if the eval-program application is syntactically broken.
2671
11410
    stack_reserve(ctx, 1)[0] = DONE;
2672
11410
    ctx->program = get_cdr(new_prg);
2673
11410
    ctx->curr_exp = get_car(new_prg);
2674
  } else {
2675
    lbm_set_error_reason((char*)lbm_error_str_num_args);
2676
    error_at_ctx(ENC_SYM_EERROR, ENC_SYM_EVAL_PROGRAM);
2677
  }
2678
11410
}
2679
2680
3332
static void apply_send(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) {
2681
3332
  if (nargs == 2) {
2682
3332
    if (lbm_type_of(args[0]) == LBM_TYPE_I) {
2683
3332
      lbm_cid cid = (lbm_cid)lbm_dec_i(args[0]);
2684
3332
      lbm_value msg = args[1];
2685
3332
      int r = lbm_find_receiver_and_send(cid, msg);
2686
      /* return the status */
2687
3332
      lbm_stack_drop(&ctx->K, nargs+1);
2688
3332
      ctx->r = r == 0 ? ENC_SYM_TRUE : ENC_SYM_NIL;
2689
3332
      ctx->app_cont = true;
2690
    } else {
2691
      error_at_ctx(ENC_SYM_TERROR, ENC_SYM_SEND);
2692
    }
2693
  } else {
2694
    lbm_set_error_reason((char*)lbm_error_str_num_args);
2695
    error_at_ctx(ENC_SYM_EERROR, ENC_SYM_SEND);
2696
  }
2697
3332
}
2698
2699
static void apply_ok(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) {
2700
  lbm_value ok_val = ENC_SYM_TRUE;
2701
  if (nargs >= 1) {
2702
    ok_val = args[0];
2703
  }
2704
  ctx->r = ok_val;
2705
  ok_ctx();
2706
}
2707
2708
28
static void apply_error(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) {
2709
  (void) ctx;
2710
28
  lbm_value err_val = ENC_SYM_EERROR;
2711
28
  if (nargs >= 1) {
2712
28
    err_val = args[0];
2713
  }
2714
28
  error_at_ctx(err_val, ENC_SYM_EXIT_ERROR);
2715
}
2716
2717
// ////////////////////////////////////////////////////////////
2718
// Map takes a function f and a list ls as arguments.
2719
// The function f is applied to each element of ls.
2720
//
2721
// Normally when applying a function to an argument this happens:
2722
//   1. the function is evaluated
2723
//   2. the argument is evaluated
2724
//   3. the result of evaluating the function is applied to the result of evaluating
2725
//      the argument.
2726
//
2727
// When doing (map f arg-list) I assume one means to apply f to each element of arg-list
2728
// exactly as those elements are. That is, no evaluation of the argument.
2729
// The implementation of map below makes sure that the elements of the arg-list are not
2730
// evaluated by wrapping them each in a `quote`.
2731
//
2732
// Map creates a structure in memory that looks like this (f (quote dummy . nil) . nil).
2733
// Then, for each element from arg-list (example a1 ... aN) the object
2734
// (f (quote aM . nil) . nil) is created by substituting dummy for an element of the list.
2735
// after this substitution the evaluator is fired up to evaluate the entire (f (quote aM . nil) . nil)
2736
// structure resulting in an element for the result list.
2737
//
2738
// Here comes the fun part, if you (map quote arg-list), then the object
2739
// (quote (quote aM . nil) . nil) is created and evaluated. Now note that quote just gives back
2740
// exactly what you give to it when evaluated.
2741
// So (quote (quote aM . nil) . nil) gives you as result (quote aM . nil) and now also note that
2742
// this is a list, and a list is really just an address on the heap!
2743
// This leads to the very fun behavior that:
2744
//
2745
// # (map quote '(1 2 3 4))
2746
// > ((quote 4) (quote 4) (quote 4) (quote 4))
2747
//
2748
// A potential fix is to instead of creating the object (f (quote aM . nil) . nil)
2749
// we create the object (f var) for some unique var and then extend the environment
2750
// for each round of evaluation with a binding var => aM.
2751
2752
// (map f arg-list)
2753
896
static void apply_map(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) {
2754

896
  if (nargs == 2 && lbm_is_cons(args[1])) {
2755
784
    lbm_value *sptr = get_stack_ptr(ctx, 3);
2756
2757
784
    lbm_value f = args[0];
2758
784
    lbm_cons_t *args1_cell = lbm_ref_cell(args[1]);
2759
784
    lbm_value h = args1_cell->car;
2760
784
    lbm_value t = args1_cell->cdr;
2761
2762
    lbm_value appli_1;
2763
    lbm_value appli;
2764

784
    WITH_GC(appli_1, lbm_heap_allocate_list(2));
2765

784
    WITH_GC_RMBR_1(appli, lbm_heap_allocate_list(2), appli_1);
2766
2767
784
    lbm_value appli_0 = get_cdr(appli_1);
2768
2769
784
    lbm_set_car_and_cdr(appli_0, h, ENC_SYM_NIL);
2770
784
    lbm_set_car(appli_1, ENC_SYM_QUOTE);
2771
2772
784
    lbm_set_car_and_cdr(get_cdr(appli), appli_1, ENC_SYM_NIL);
2773
784
    lbm_set_car(appli, f);
2774
2775
784
    lbm_value elt = cons_with_gc(ctx->r, ENC_SYM_NIL, appli);
2776
784
    sptr[0] = t;     // reuse stack space
2777
784
    sptr[1] = ctx->curr_env;
2778
784
    sptr[2] = elt;
2779
784
    lbm_value *rptr = stack_reserve(ctx,4);
2780
784
    rptr[0] = elt;
2781
784
    rptr[1] = appli;
2782
784
    rptr[2] = appli_0;
2783
784
    rptr[3] = MAP;
2784
784
    ctx->curr_exp = appli;
2785

112
  } else if (nargs == 2 && lbm_is_symbol_nil(args[1])) {
2786
112
    lbm_stack_drop(&ctx->K, 3);
2787
112
    ctx->r = ENC_SYM_NIL;
2788
112
    ctx->app_cont = true;
2789
  } else {
2790
    lbm_set_error_reason((char*)lbm_error_str_num_args);
2791
    error_at_ctx(ENC_SYM_EERROR, ENC_SYM_MAP);
2792
  }
2793
896
}
2794
2795
140
static void apply_reverse(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) {
2796

140
  if (nargs == 1 && lbm_is_list(args[0])) {
2797
140
    lbm_value curr = args[0];
2798
2799
140
    lbm_value new_list = ENC_SYM_NIL;
2800
3332
    while (lbm_is_cons(curr)) {
2801
3192
      lbm_cons_t *curr_cell = lbm_ref_cell(curr); // known cons.
2802
3192
      lbm_value tmp = cons_with_gc(curr_cell->car, new_list, ENC_SYM_NIL);
2803
3192
      new_list = tmp;
2804
3192
      curr = curr_cell->cdr;
2805
    }
2806
140
    lbm_stack_drop(&ctx->K, 2);
2807
140
    ctx->r = new_list;
2808
140
    ctx->app_cont = true;
2809
  } else {
2810
    lbm_set_error_reason("Reverse requires a list argument");
2811
    error_at_ctx(ENC_SYM_EERROR, ENC_SYM_REVERSE);
2812
  }
2813
140
}
2814
2815
34594
static void apply_flatten(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) {
2816
34594
  if (nargs == 1) {
2817
#ifdef LBM_ALWAYS_GC
2818
    gc();
2819
#endif
2820
34566
    lbm_value v = flatten_value(args[0]);
2821
34566
    if ( v == ENC_SYM_MERROR) {
2822
2
      gc();
2823
2
      v = flatten_value(args[0]);
2824
    }
2825
2826
34566
    if (lbm_is_symbol(v)) {
2827
28
      error_at_ctx(v, ENC_SYM_FLATTEN);
2828
    } else {
2829
34538
      lbm_stack_drop(&ctx->K, 2);
2830
34538
      ctx->r = v;
2831
34538
      ctx->app_cont = true;
2832
    }
2833
34538
    return;
2834
  }
2835
28
  error_at_ctx(ENC_SYM_TERROR, ENC_SYM_FLATTEN);
2836
}
2837
2838
34510
static void apply_unflatten(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) {
2839
  lbm_array_header_t *array;
2840

34510
  if(nargs == 1 && (array = lbm_dec_array_r(args[0]))) {
2841
    lbm_flat_value_t fv;
2842
34510
    fv.buf = (uint8_t*)array->data;
2843
34510
    fv.buf_size = array->size;
2844
34510
    fv.buf_pos = 0;
2845
2846
    lbm_value res;
2847
2848
34510
    ctx->r = ENC_SYM_NIL;
2849
34510
    if (lbm_unflatten_value(&fv, &res)) {
2850
34510
      ctx->r =  res;
2851
    }
2852
34510
    lbm_stack_drop(&ctx->K, 2);
2853
34510
    ctx->app_cont = true;
2854
34510
    return;
2855
  }
2856
  error_at_ctx(ENC_SYM_TERROR, ENC_SYM_UNFLATTEN);
2857
}
2858
2859
84
static void apply_kill(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) {
2860

84
  if (nargs == 2 && lbm_is_number(args[0])) {
2861
84
    lbm_cid cid = lbm_dec_as_i32(args[0]);
2862
2863
84
    if (ctx->id == cid) {
2864
      ctx->r = args[1];
2865
      finish_ctx();
2866
      return;
2867
    }
2868
84
    mutex_lock(&qmutex);
2869
84
    eval_context_t *found = NULL;
2870
84
    found = lookup_ctx_nm(&blocked, cid);
2871
84
    if (found)
2872
      drop_ctx_nm(&blocked, found);
2873
    else
2874
84
      found = lookup_ctx_nm(&queue, cid);
2875
84
    if (found)
2876
84
      drop_ctx_nm(&queue, found);
2877
2878
84
    if (found) {
2879
84
      found->K.data[found->K.sp - 1] = KILL;
2880
84
      found->r = args[1];
2881
84
      found->app_cont = true;
2882
84
      found->state = LBM_THREAD_STATE_READY;
2883
84
      enqueue_ctx_nm(&queue,found);
2884
84
      ctx->r = ENC_SYM_TRUE;
2885
    } else {
2886
      ctx->r = ENC_SYM_NIL;
2887
    }
2888
84
    lbm_stack_drop(&ctx->K, 3);
2889
84
    ctx->app_cont = true;
2890
84
    mutex_unlock(&qmutex);
2891
84
    return;
2892
  }
2893
  error_at_ctx(ENC_SYM_TERROR, ENC_SYM_KILL);
2894
}
2895
2896
282828
static lbm_value cmp_to_clo(lbm_value cmp) {
2897
  lbm_value closure;
2898

282828
  WITH_GC(closure, lbm_heap_allocate_list(4));
2899
282828
  lbm_set_car(closure, ENC_SYM_CLOSURE);
2900
282828
  lbm_value cl1 = lbm_cdr(closure);
2901
  lbm_value par;
2902

282828
  WITH_GC_RMBR_1(par, lbm_heap_allocate_list_init(2, symbol_x, symbol_y), closure);
2903
282828
  lbm_set_car(cl1, par);
2904
282828
  lbm_value cl2 = lbm_cdr(cl1);
2905
  lbm_value body;
2906

282828
  WITH_GC_RMBR_1(body, lbm_heap_allocate_list_init(3, cmp, symbol_x, symbol_y), closure);
2907
282828
  lbm_set_car(cl2, body);
2908
282828
  lbm_value cl3 = lbm_cdr(cl2);
2909
282828
  lbm_set_car(cl3, ENC_SYM_NIL);
2910
282828
  return closure;
2911
}
2912
2913
// (merge comparator list1 list2)
2914
420
static void apply_merge(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) {
2915

420
  if (nargs == 3 && lbm_is_list(args[1]) && lbm_is_list(args[2])) {
2916
2917
420
    if (!lbm_is_closure(args[0])) {
2918
28
      args[0] = cmp_to_clo(args[0]);
2919
    }
2920
2921
    // Copy input lists for functional behaviour at top-level
2922
    // merge itself is in-place in the copied lists.
2923
    lbm_value a;
2924
    lbm_value b;
2925
420
    int len_a = -1;
2926
420
    int len_b = -1;
2927

420
    WITH_GC(a, lbm_list_copy(&len_a, args[1]));
2928

420
    WITH_GC_RMBR_1(b, lbm_list_copy(&len_b, args[2]), a);
2929
2930
420
    if (len_a == 0) {
2931
56
      ctx->r = b;
2932
56
      lbm_stack_drop(&ctx->K, 4);
2933
56
      ctx->app_cont = true;
2934
56
      return;
2935
    }
2936
364
    if (len_b == 0) {
2937
56
      ctx->r = a;
2938
56
      lbm_stack_drop(&ctx->K, 4);
2939
56
      ctx->app_cont = true;
2940
56
      return;
2941
    }
2942
2943
308
    args[1] = a; // keep safe by replacing the original on stack.
2944
308
    args[2] = b;
2945
2946
308
    lbm_value a_1 = a;
2947
308
    lbm_value a_rest = lbm_cdr(a);
2948
308
    lbm_value b_1 = b;
2949
308
    lbm_value b_rest = lbm_cdr(b);
2950
2951
    lbm_value cl[3]; // Comparator closure
2952
308
    extract_n(lbm_cdr(args[0]), cl, 3);
2953
308
    lbm_value cmp_env = cl[CLO_ENV];
2954
308
    lbm_value par1 = ENC_SYM_NIL;
2955
308
    lbm_value par2 = ENC_SYM_NIL;
2956
308
    lbm_uint len = lbm_list_length(cl[CLO_PARAMS]);
2957
308
    if (len == 2) {
2958
308
      par1 = get_car(cl[CLO_PARAMS]);
2959
308
      par2 = get_cadr(cl[CLO_PARAMS]);
2960
      lbm_value new_env0;
2961
      lbm_value new_env;
2962

308
      WITH_GC(new_env0, lbm_env_set(cmp_env, par1, lbm_car(a_1)));
2963

308
      WITH_GC_RMBR_1(new_env, lbm_env_set(new_env0, par2, lbm_car(b_1)),new_env0);
2964
308
      cmp_env = new_env;
2965
    } else {
2966
      error_at_ctx(ENC_SYM_TERROR, args[0]);
2967
    }
2968
308
    lbm_set_cdr(a_1, b_1);
2969
308
    lbm_set_cdr(b_1, ENC_SYM_NIL);
2970
308
    lbm_value cmp = cl[CLO_BODY];
2971
2972
308
    lbm_stack_drop(&ctx->K, 4); // TODO: Optimize drop 4 alloc 10 into alloc 6
2973
308
    lbm_uint *sptr = stack_reserve(ctx, 10);
2974
308
    sptr[0] = ENC_SYM_NIL; // head of merged list
2975
308
    sptr[1] = ENC_SYM_NIL; // last of merged list
2976
308
    sptr[2] = a_1;
2977
308
    sptr[3] = a_rest;
2978
308
    sptr[4] = b_rest;
2979
308
    sptr[5] = cmp;
2980
308
    sptr[6] = cmp_env;
2981
308
    sptr[7] = par1;
2982
308
    sptr[8] = par2;
2983
308
    sptr[9] = MERGE_REST;
2984
308
    ctx->curr_exp = cl[CLO_BODY];
2985
308
    ctx->curr_env = cmp_env;
2986
308
    return;
2987
  }
2988
  error_at_ctx(ENC_SYM_TERROR, ENC_SYM_MERGE);
2989
}
2990
2991
// (sort comparator list)
2992
283136
static void apply_sort(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) {
2993

283136
  if (nargs == 2 && lbm_is_list(args[1])) {
2994
2995
283136
    if (!lbm_is_closure(args[0])) {
2996
282800
      args[0] = cmp_to_clo(args[0]);
2997
    }
2998
2999
283136
    int len = -1;
3000
    lbm_value list_copy;
3001

283136
    WITH_GC(list_copy, lbm_list_copy(&len, args[1]));
3002
283136
    if (len <= 1) {
3003
28
      lbm_stack_drop(&ctx->K, 3);
3004
28
      ctx->r = list_copy;
3005
28
      ctx->app_cont = true;
3006
28
      return;
3007
    }
3008
3009
283108
    args[1] = list_copy; // Keep safe, original replaced on stack.
3010
3011
    // Take the headmost 2, 1-element sublists.
3012
283108
    lbm_value a = list_copy;
3013
283108
    lbm_value b = lbm_cdr(a);
3014
283108
    lbm_value rest = lbm_cdr(b);
3015
    // Do not terminate b. keep rest of list safe from GC in the following
3016
    // closure extraction.
3017
    //lbm_set_cdr(a, b); // This is void
3018
3019
    lbm_value cl[3]; // Comparator closure
3020
283108
    extract_n(lbm_cdr(args[0]), cl, 3);
3021
283108
    lbm_value cmp_env = cl[CLO_ENV];
3022
283108
    lbm_value par1 = ENC_SYM_NIL;
3023
283108
    lbm_value par2 = ENC_SYM_NIL;
3024
283108
    lbm_uint cl_len = lbm_list_length(cl[CLO_PARAMS]);
3025
283108
    if (cl_len == 2) {
3026
283108
      par1 = get_car(cl[CLO_PARAMS]);
3027
283108
      par2 = get_cadr(cl[CLO_PARAMS]);
3028
      lbm_value new_env0;
3029
      lbm_value new_env;
3030

283108
      WITH_GC(new_env0, lbm_env_set(cmp_env, par1, lbm_car(a)));
3031

283108
      WITH_GC_RMBR_1(new_env, lbm_env_set(new_env0, par2, lbm_car(b)), new_env0);
3032
283108
      cmp_env = new_env;
3033
    } else {
3034
      error_at_ctx(ENC_SYM_TERROR, args[0]);
3035
    }
3036
283108
    lbm_value cmp = cl[CLO_BODY];
3037
3038
    // Terminate the comparator argument list.
3039
283108
    lbm_set_cdr(b, ENC_SYM_NIL);
3040
3041
283108
    lbm_stack_drop(&ctx->K, 3);  //TODO: optimize drop 3, alloc 20 into alloc 17
3042
283108
    lbm_uint *sptr = stack_reserve(ctx, 20);
3043
283108
    sptr[0] = cmp;
3044
283108
    sptr[1] = cmp_env;
3045
283108
    sptr[2] = par1;
3046
283108
    sptr[3] = par2;
3047
283108
    sptr[4] = ENC_SYM_NIL; // head of merged accumulation of sublists
3048
283108
    sptr[5] = ENC_SYM_NIL; // last of merged accumulation of sublists
3049
283108
    sptr[6] = rest;
3050
283108
    sptr[7] = lbm_enc_i(1);
3051
283108
    sptr[8] = lbm_enc_i(len); //TODO: 28 bit i vs 32 bit i
3052
283108
    sptr[9] = MERGE_LAYER;
3053
283108
    sptr[10] = ENC_SYM_NIL; // head of merged sublist
3054
283108
    sptr[11] = ENC_SYM_NIL; // last of merged sublist
3055
283108
    sptr[12] = a;
3056
283108
    sptr[13] = ENC_SYM_NIL; // no a_rest, 1 element lists in layer 1.
3057
283108
    sptr[14] = ENC_SYM_NIL; // no b_rest, 1 element lists in layer 1.
3058
283108
    sptr[15] = cmp;
3059
283108
    sptr[16] = cmp_env;
3060
283108
    sptr[17] = par1;
3061
283108
    sptr[18] = par2;
3062
283108
    sptr[19] = MERGE_REST;
3063
283108
    ctx->curr_exp = cmp;
3064
283108
    ctx->curr_env = cmp_env;
3065
283108
    return;
3066
  }
3067
  error_ctx(ENC_SYM_TERROR);
3068
}
3069
3070
616308
static void apply_rest_args(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) {
3071
  lbm_value res;
3072
616308
  if (lbm_env_lookup_b(&res, ENC_SYM_REST_ARGS, ctx->curr_env)) {
3073

616280
    if (nargs == 1 && lbm_is_number(args[0])) {
3074
56140
      int32_t ix = lbm_dec_as_i32(args[0]);
3075
56140
      res = lbm_index_list(res, ix);
3076
    }
3077
616280
    ctx->r = res;
3078
  } else {
3079
28
    ctx->r = ENC_SYM_NIL;
3080
  }
3081
616308
  lbm_stack_drop(&ctx->K, nargs+1);
3082
616308
  ctx->app_cont = true;
3083
616308
}
3084
3085
/* (rotate list-expr dist/dir-expr) */
3086
84
static void apply_rotate(lbm_value *args, lbm_uint nargs, eval_context_t *ctx) {
3087

84
  if (nargs == 2 && lbm_is_list(args[0]) && lbm_is_number(args[1])) {
3088
84
    int len = -1;
3089
    lbm_value ls;
3090

84
    WITH_GC(ls, lbm_list_copy(&len, args[0]));
3091
84
    int dist = lbm_dec_as_i32(args[1]);
3092

84
    if (len > 0 && dist != 0) {
3093
56
      int d = dist;
3094
56
      if (dist > 0) {
3095
28
        ls = lbm_list_destructive_reverse(ls);
3096
      } else {
3097
28
        d = -dist;
3098
      }
3099
3100
56
      lbm_value start = ls;
3101
56
      lbm_value end = ENC_SYM_NIL;
3102
56
      lbm_value curr = start;
3103
308
      while (lbm_is_cons(curr)) {
3104
252
        end = curr;
3105
252
        curr = get_cdr(curr);
3106
      }
3107
3108
168
      for (int i = 0; i < d; i ++) {
3109
112
        lbm_value a = start;
3110
112
        start = lbm_cdr(start);
3111
112
        lbm_set_cdr(a, ENC_SYM_NIL);
3112
112
        lbm_set_cdr(end, a);
3113
112
        end = a;
3114
      }
3115
56
      ls = start;
3116
56
      if (dist > 0) {
3117
28
        ls = lbm_list_destructive_reverse(ls);
3118
      }
3119
    }
3120
84
    lbm_stack_drop(&ctx->K, nargs+1);
3121
84
    ctx->app_cont = true;
3122
84
    ctx->r = ls;
3123
84
    return;
3124
  }
3125
  error_ctx(ENC_SYM_EERROR);
3126
}
3127
3128
/***************************************************/
3129
/* Application lookup table                        */
3130
3131
typedef void (*apply_fun)(lbm_value *, lbm_uint, eval_context_t *);
3132
static const apply_fun fun_table[] =
3133
  {
3134
   apply_setvar,
3135
   apply_read,
3136
   apply_read_program,
3137
   apply_read_eval_program,
3138
   apply_spawn,
3139
   apply_spawn_trap,
3140
   apply_yield,
3141
   apply_wait,
3142
   apply_eval,
3143
   apply_eval_program,
3144
   apply_send,
3145
   apply_ok,
3146
   apply_error,
3147
   apply_map,
3148
   apply_reverse,
3149
   apply_flatten,
3150
   apply_unflatten,
3151
   apply_kill,
3152
   apply_sleep,
3153
   apply_merge,
3154
   apply_sort,
3155
   apply_rest_args,
3156
   apply_rotate,
3157
  };
3158
3159
/***************************************************/
3160
/* Application of function that takes arguments    */
3161
/* passed over the stack.                          */
3162
3163
77990317
static void application(eval_context_t *ctx, lbm_value *fun_args, lbm_uint arg_count) {
3164
  /* If arriving here, we know that the fun is a symbol.
3165
   *  and can be a built in operation or an extension.
3166
   */
3167
77990317
  lbm_value fun = fun_args[0];
3168
3169
77990317
  lbm_uint fun_val = lbm_dec_sym(fun);
3170
77990317
  lbm_uint fun_kind = SYMBOL_KIND(fun_val);
3171
3172

77990317
  switch (fun_kind) {
3173
184956
  case SYMBOL_KIND_EXTENSION: {
3174
184956
    extension_fptr f = extension_table[SYMBOL_IX(fun_val)].fptr;
3175
3176
    lbm_value ext_res;
3177

184956
    WITH_GC(ext_res, f(&fun_args[1], arg_count));
3178
184956
    if (lbm_is_error(ext_res)) { //Error other than merror
3179
2828
      error_at_ctx(ext_res, fun);
3180
    }
3181
182128
    lbm_stack_drop(&ctx->K, arg_count + 1);
3182
3183
182128
    ctx->app_cont = true;
3184
182128
    ctx->r = ext_res;
3185
3186
182128
    if (blocking_extension) {
3187
112
      if (is_atomic) {
3188
        // Check atomic_error explicitly so that the mutex
3189
        // can be released if there is an error.
3190
        blocking_extension = false;
3191
        mutex_unlock(&blocking_extension_mutex);
3192
        atomic_error();
3193
      }
3194
112
      blocking_extension = false;
3195
112
      if (blocking_extension_timeout) {
3196
        blocking_extension_timeout = false;
3197
        block_current_ctx(LBM_THREAD_STATE_TIMEOUT, blocking_extension_timeout_us,true);
3198
      } else {
3199
112
        block_current_ctx(LBM_THREAD_STATE_BLOCKED, 0,true);
3200
      }
3201
112
      mutex_unlock(&blocking_extension_mutex);
3202
    }
3203
182128
  }  break;
3204
73276474
  case SYMBOL_KIND_FUNDAMENTAL:
3205
73276474
    call_fundamental(SYMBOL_IX(fun_val), &fun_args[1], arg_count, ctx);
3206
73271818
    break;
3207
4528887
  case SYMBOL_KIND_APPFUN:
3208
4528887
    fun_table[SYMBOL_IX(fun_val)](&fun_args[1], arg_count, ctx);
3209
4528495
    break;
3210
  default:
3211
    // Symbols that are "special" but not in the way caught above
3212
    // ends up here.
3213
    lbm_set_error_reason("Symbol does not represent a function");
3214
    error_at_ctx(ENC_SYM_EERROR,fun_args[0]);
3215
    break;
3216
  }
3217
77982441
}
3218
3219
59418902
static void cont_closure_application_args(eval_context_t *ctx) {
3220
59418902
  lbm_uint* sptr = get_stack_ptr(ctx, 5);
3221
3222
59418902
  lbm_value arg_env = (lbm_value)sptr[0];
3223
59418902
  lbm_value exp     = (lbm_value)sptr[1];
3224
59418902
  lbm_value clo_env = (lbm_value)sptr[2];
3225
59418902
  lbm_value params  = (lbm_value)sptr[3];
3226
59418902
  lbm_value args    = (lbm_value)sptr[4];
3227
3228
  lbm_value car_params, cdr_params;
3229
59418902
  get_car_and_cdr(params, &car_params, &cdr_params);
3230
3231
59418902
  bool a_nil = lbm_is_symbol_nil(args);
3232
59418902
  bool p_nil = lbm_is_symbol_nil(cdr_params);
3233
3234
59418902
  lbm_value binder = allocate_binding(car_params, ctx->r, clo_env);
3235
3236

59418874
  if (!a_nil && !p_nil) {
3237
    lbm_value car_args, cdr_args;
3238
33207244
    get_car_and_cdr(args, &car_args, &cdr_args);
3239
33207244
    sptr[2] = binder;
3240
33207244
    sptr[3] = cdr_params;
3241
33207244
    sptr[4] = cdr_args;
3242
33207244
    stack_reserve(ctx,1)[0] = CLOSURE_ARGS;
3243
33207244
    ctx->curr_exp = car_args;
3244
33207244
    ctx->curr_env = arg_env;
3245

26211630
  } else if (a_nil && p_nil) {
3246
    // Arguments and parameters match up in number
3247
26183406
    lbm_stack_drop(&ctx->K, 5);
3248
26183406
    ctx->curr_env = binder;
3249
26183406
    ctx->curr_exp = exp;
3250
28224
  } else if (p_nil) {
3251
28224
    lbm_value rest_binder = allocate_binding(ENC_SYM_REST_ARGS, ENC_SYM_NIL, binder);
3252
28224
    sptr[2] = rest_binder;
3253
28224
    sptr[3] = get_cdr(args);
3254
28224
    sptr[4] = get_car(rest_binder); // last element of rest_args so far
3255
28224
    stack_reserve(ctx,1)[0] = CLOSURE_ARGS_REST;
3256
28224
    ctx->curr_exp = get_car(args);
3257
28224
    ctx->curr_env = arg_env;
3258
  }  else {
3259
    lbm_set_error_reason((char*)lbm_error_str_num_args);
3260
    error_ctx(ENC_SYM_EERROR);
3261
  }
3262
59418874
}
3263
3264
3265
5797008
static void cont_closure_args_rest(eval_context_t *ctx) {
3266
5797008
  lbm_uint* sptr = get_stack_ptr(ctx, 5);
3267
5797008
  lbm_value arg_env = (lbm_value)sptr[0];
3268
5797008
  lbm_value exp     = (lbm_value)sptr[1];
3269
5797008
  lbm_value clo_env = (lbm_value)sptr[2];
3270
5797008
  lbm_value args    = (lbm_value)sptr[3];
3271
5797008
  lbm_value last    = (lbm_value)sptr[4];
3272
5797008
  lbm_cons_t* heap = lbm_heap_state.heap;
3273
#ifdef LBM_ALWAYS_GC
3274
  gc();
3275
#endif
3276
5797008
  lbm_value binding = lbm_heap_state.freelist;
3277
5797008
  if (binding == ENC_SYM_NIL) {
3278
7498
    gc();
3279
7498
    binding = lbm_heap_state.freelist;
3280
7498
    if (binding == ENC_SYM_NIL) error_ctx(ENC_SYM_MERROR);
3281
  }
3282
5797008
  lbm_uint binding_ix = lbm_dec_ptr(binding);
3283
5797008
  lbm_heap_state.freelist = heap[binding_ix].cdr;
3284
5797008
  lbm_heap_state.num_alloc += 1;
3285
5797008
  heap[binding_ix].car = ctx->r;
3286
5797008
  heap[binding_ix].cdr = ENC_SYM_NIL;
3287
3288
3289
5797008
  lbm_set_cdr(last, binding);
3290
5797008
  sptr[4] = binding;
3291
3292
5797008
  if (args == ENC_SYM_NIL) {
3293
588252
    lbm_stack_drop(&ctx->K, 5);
3294
588252
    ctx->curr_env = clo_env;
3295
588252
    ctx->curr_exp = exp;
3296
  } else {
3297
5208756
    stack_reserve(ctx,1)[0] = CLOSURE_ARGS_REST;
3298
5208756
    sptr[3] = get_cdr(args);
3299
5208756
    ctx->curr_exp = get_car(args);
3300
5208756
    ctx->curr_env = arg_env;
3301
  }
3302
5797008
}
3303
3304
247668184
static void cont_application_args(eval_context_t *ctx) {
3305
247668184
  lbm_uint *sptr = get_stack_ptr(ctx, 3);
3306
3307
247668184
  lbm_value env = sptr[0];
3308
247668184
  lbm_value rest = sptr[1];
3309
247668184
  lbm_value count = sptr[2];
3310
3311
247668184
  ctx->curr_env = env;
3312
247668184
  sptr[0] = ctx->r; // Function 1st then Arguments
3313
247668184
  if (lbm_is_cons(rest)) {
3314
169677867
    lbm_cons_t *cell = lbm_ref_cell(rest);
3315
169677867
    sptr[1] = env;
3316
169677867
    sptr[2] = cell->cdr;
3317
169677867
    lbm_value *rptr = stack_reserve(ctx,2);
3318
169677867
    rptr[0] = count + (1 << LBM_VAL_SHIFT);
3319
169677867
    rptr[1] = APPLICATION_ARGS;
3320
169677867
    ctx->curr_exp = cell->car;
3321
  } else {
3322
    // No more arguments
3323
77990317
    lbm_stack_drop(&ctx->K, 2);
3324
77990317
    lbm_uint nargs = lbm_dec_u(count);
3325
77990317
    lbm_value *args = get_stack_ptr(ctx, (uint32_t)(nargs + 1));
3326
77990317
    application(ctx,args, nargs);
3327
  }
3328
247660308
}
3329
3330
3985688
static void cont_and(eval_context_t *ctx) {
3331
  lbm_value env;
3332
  lbm_value rest;
3333
3985688
  lbm_value arg = ctx->r;
3334
3985688
  lbm_pop_2(&ctx->K, &rest, &env);
3335
3985688
  if (lbm_is_symbol_nil(arg)) {
3336
280056
    ctx->app_cont = true;
3337
280056
    ctx->r = ENC_SYM_NIL;
3338
3705632
  } else if (lbm_is_symbol_nil(rest)) {
3339
1701924
    ctx->app_cont = true;
3340
  } else {
3341
2003708
    lbm_value *sptr = stack_reserve(ctx, 3);
3342
2003708
    sptr[0] = env;
3343
2003708
    sptr[1] = get_cdr(rest);
3344
2003708
    sptr[2] = AND;
3345
2003708
    ctx->curr_env = env;
3346
2003708
    ctx->curr_exp = get_car(rest);
3347
  }
3348
3985688
}
3349
3350
15988
static void cont_or(eval_context_t *ctx) {
3351
  lbm_value env;
3352
  lbm_value rest;
3353
15988
  lbm_value arg = ctx->r;
3354
15988
  lbm_pop_2(&ctx->K, &rest, &env);
3355
15988
  if (!lbm_is_symbol_nil(arg)) {
3356
840
    ctx->app_cont = true;
3357
15148
  } else if (lbm_is_symbol_nil(rest)) {
3358
6356
    ctx->app_cont = true;
3359
6356
    ctx->r = ENC_SYM_NIL;
3360
  } else {
3361
8792
    lbm_value *sptr = stack_reserve(ctx, 3);
3362
8792
    sptr[0] = env;
3363
8792
    sptr[1] = get_cdr(rest);
3364
8792
    sptr[2] = OR;
3365
8792
    ctx->curr_exp = get_car(rest);
3366
8792
    ctx->curr_env = env;
3367
  }
3368
15988
}
3369
3370
40888554
static int fill_binding_location(lbm_value key, lbm_value value, lbm_value env) {
3371
40888554
  if (lbm_type_of(key) == LBM_TYPE_SYMBOL) {
3372
26887518
    if (key == ENC_SYM_DONTCARE) return FB_OK;
3373
24087406
    lbm_env_modify_binding(env,key,value);
3374
24087406
    return FB_OK;
3375

28002072
  } else if (lbm_is_cons(key) &&
3376
14001036
             lbm_is_cons(value)) {
3377
14001036
    int r = fill_binding_location(get_car(key), get_car(value), env);
3378
14001036
    if (r == FB_OK) {
3379
14001036
      r = fill_binding_location(get_cdr(key), get_cdr(value), env);
3380
    }
3381
14001036
    return r;
3382
  }
3383
  return FB_TYPE_ERROR;
3384
}
3385
3386
12242748
static void cont_bind_to_key_rest(eval_context_t *ctx) {
3387
3388
12242748
  lbm_value *sptr = get_stack_ptr(ctx, 4);
3389
3390
12242748
  lbm_value rest = sptr[1];
3391
12242748
  lbm_value env  = sptr[2];
3392
12242748
  lbm_value key  = sptr[3];
3393
3394
12242748
  if (fill_binding_location(key, ctx->r, env) < 0) {
3395
    lbm_set_error_reason("Incorrect type of name/key in let-binding");
3396
    error_at_ctx(ENC_SYM_TERROR, key);
3397
  }
3398
3399
12242748
  if (lbm_is_cons(rest)) {
3400
113904
    lbm_value car_rest = get_car(rest);
3401
    lbm_value key_val[2];
3402
113904
    extract_n(car_rest, key_val, 2);
3403
3404
113904
    sptr[1] = get_cdr(rest);
3405
113904
    sptr[3] = key_val[0];
3406
113904
    stack_reserve(ctx,1)[0] = BIND_TO_KEY_REST;
3407
113904
    ctx->curr_exp = key_val[1];
3408
113904
    ctx->curr_env = env;
3409
  } else {
3410
    // Otherwise evaluate the expression in the populated env
3411
12128844
    ctx->curr_exp = sptr[0];
3412
12128844
    ctx->curr_env = env;
3413
12128844
    lbm_stack_drop(&ctx->K, 4);
3414
  }
3415
12242748
}
3416
3417
21762887
static void cont_if(eval_context_t *ctx) {
3418
3419
21762887
  lbm_value arg = ctx->r;
3420
3421
21762887
  lbm_value *sptr = pop_stack_ptr(ctx, 2);
3422
3423
21762887
  ctx->curr_env = sptr[1];
3424
21762887
  if (lbm_is_symbol_nil(arg)) {
3425
21739560
    ctx->curr_exp = get_cadr(sptr[0]); // else branch
3426
  } else {
3427
23327
    ctx->curr_exp = get_car(sptr[0]); // then branch
3428
  }
3429
21762887
}
3430
3431
5460
static void cont_match(eval_context_t *ctx) {
3432
5460
  lbm_value e = ctx->r;
3433
5460
  bool  do_gc = false;
3434
3435
5460
  lbm_uint *sptr = get_stack_ptr(ctx, 2);
3436
5460
  lbm_value patterns = (lbm_value)sptr[0];
3437
5460
  lbm_value orig_env = (lbm_value)sptr[1]; // restore enclosing environment.
3438
5460
  lbm_value new_env = orig_env;
3439
3440
5460
  if (lbm_is_symbol_nil(patterns)) {
3441
    // no more patterns
3442
    lbm_stack_drop(&ctx->K, 2);
3443
    ctx->r = ENC_SYM_NO_MATCH;
3444
    ctx->app_cont = true;
3445
5460
  } else if (lbm_is_cons(patterns)) {
3446
5460
    lbm_value match_case = get_car(patterns);
3447
5460
    lbm_value pattern = get_car(match_case);
3448
5460
    lbm_value n1      = get_cadr(match_case);
3449
5460
    lbm_value n2      = get_cadr(get_cdr(match_case));
3450
    lbm_value body;
3451
5460
    bool check_guard = false;
3452
5460
    if (lbm_is_symbol_nil(n2)) { // TODO: Not a very robust check.
3453
4200
      body = n1;
3454
    } else {
3455
1260
      body = n2;
3456
1260
      check_guard = true;
3457
    }
3458
#ifdef LBM_ALWAYS_GC
3459
    gc();
3460
#endif
3461
5460
    bool is_match = match(pattern, e, &new_env, &do_gc);
3462
5460
    if (do_gc) {
3463
      gc();
3464
      do_gc = false;
3465
      new_env = orig_env;
3466
      is_match = match(pattern, e, &new_env, &do_gc);
3467
      if (do_gc) {
3468
        error_ctx(ENC_SYM_MERROR);
3469
      }
3470
    }
3471
5460
    if (is_match) {
3472
3304
      if (check_guard) {
3473
1260
        lbm_value *rptr = stack_reserve(ctx,5);
3474
1260
        sptr[0] = get_cdr(patterns);
3475
1260
        sptr[1] = ctx->curr_env;
3476
1260
        rptr[0] = MATCH;
3477
1260
        rptr[1] = new_env;
3478
1260
        rptr[2] = body;
3479
1260
        rptr[3] = e;
3480
1260
        rptr[4] = MATCH_GUARD;
3481
1260
        ctx->curr_env = new_env;
3482
1260
        ctx->curr_exp = n1; // The guard
3483
      } else {
3484
2044
        lbm_stack_drop(&ctx->K, 2);
3485
2044
        ctx->curr_env = new_env;
3486
2044
        ctx->curr_exp = body;
3487
      }
3488
    } else {
3489
      // set up for checking of next pattern
3490
2156
      sptr[0] = get_cdr(patterns);
3491
2156
      sptr[1] = orig_env;
3492
2156
      stack_reserve(ctx,1)[0] = MATCH;
3493
      // leave r unaltered
3494
2156
      ctx->app_cont = true;
3495
    }
3496
  } else {
3497
    error_at_ctx(ENC_SYM_TERROR, ENC_SYM_MATCH);
3498
  }
3499
5460
}
3500
3501
224
static void cont_exit_atomic(eval_context_t *ctx) {
3502
224
  is_atomic = false; // atomic blocks cannot nest!
3503
224
  ctx->app_cont = true;
3504
224
}
3505
3506
// cont_map:
3507
//
3508
// sptr[0]: s[sp-6] = Rest of the input list.
3509
// sptr[1]: s[sp-5] = Environment to restore for the eval of each application.
3510
// sptr[2]: s[sp-4] = Result list.
3511
// sptr[3]: s[sp-3] = Cell that goes into result list after being populated with application result.
3512
// sptr[4]: s[sp-2] = Ref to application.
3513
// sptr[5]: s[sp-1] = Ref to application argument.
3514
//
3515
// ctx->r  = eval result of previous application.
3516
2296
static void cont_map(eval_context_t *ctx) {
3517
2296
  lbm_value *sptr = get_stack_ptr(ctx, 6);
3518
2296
  lbm_value ls  = sptr[0];
3519
2296
  lbm_value env = sptr[1];
3520
2296
  lbm_value t   = sptr[3];
3521
2296
  lbm_set_car(t, ctx->r); // update car field tailmost position.
3522
2296
  if (lbm_is_cons(ls)) {
3523
1512
    lbm_cons_t *cell = lbm_ref_cell(ls); // already checked that cons.
3524
1512
    lbm_value next = cell->car;
3525
1512
    lbm_value rest = cell->cdr;
3526
1512
    sptr[0] = rest;
3527
1512
    stack_reserve(ctx,1)[0] = MAP;
3528
1512
    lbm_set_car(sptr[5], next); // new arguments
3529
3530
1512
    lbm_value elt = cons_with_gc(ENC_SYM_NIL, ENC_SYM_NIL, ENC_SYM_NIL);
3531
1512
    lbm_set_cdr(t, elt);
3532
1512
    sptr[3] = elt;  // (r1 ... rN . (nil . nil))
3533
1512
    ctx->curr_exp = sptr[4];
3534
1512
    ctx->curr_env = env;
3535
  } else {
3536
784
    ctx->r = sptr[2]; //head of result list
3537
784
    ctx->curr_env = env;
3538
784
    lbm_stack_drop(&ctx->K, 6);
3539
784
    ctx->app_cont = true;
3540
  }
3541
2296
}
3542
3543
1260
static void cont_match_guard(eval_context_t *ctx) {
3544
1260
  if (lbm_is_symbol_nil(ctx->r)) {
3545
    lbm_value e;
3546
476
    lbm_pop(&ctx->K, &e);
3547
476
    lbm_stack_drop(&ctx->K, 2);
3548
476
    ctx->r = e;
3549
476
    ctx->app_cont = true;
3550
  } else {
3551
    lbm_value body;
3552
    lbm_value env;
3553
784
    lbm_stack_drop(&ctx->K, 1);
3554
784
    lbm_pop_2(&ctx->K, &body, &env);
3555
784
    lbm_stack_drop(&ctx->K, 3);
3556
784
    ctx->curr_env = env;
3557
784
    ctx->curr_exp = body;
3558
  }
3559
1260
}
3560
3561
28
static void cont_terminate(eval_context_t *ctx) {
3562
28
  error_ctx(ctx->r);
3563
}
3564
3565
925148
static void cont_loop(eval_context_t *ctx) {
3566
925148
  lbm_value *sptr = get_stack_ptr(ctx, 2);
3567
925148
  stack_reserve(ctx,1)[0] = LOOP_CONDITION;
3568
925148
  ctx->curr_exp = sptr[1];
3569
925148
}
3570
3571
925428
static void cont_loop_condition(eval_context_t *ctx) {
3572
925428
  if (lbm_is_symbol_nil(ctx->r)) {
3573
280
    lbm_stack_drop(&ctx->K, 2);
3574
280
    ctx->app_cont = true;  // A loop returns nil? Makes sense to me... but in general?
3575
280
    return;
3576
  }
3577
925148
  lbm_value *sptr = get_stack_ptr(ctx, 2);
3578
925148
  stack_reserve(ctx,1)[0] = LOOP;
3579
925148
  ctx->curr_exp = sptr[0];
3580
}
3581
3582
8791580
static void cont_merge_rest(eval_context_t *ctx) {
3583
8791580
  lbm_uint *sptr = get_stack_ptr(ctx, 9);
3584
3585
  // If comparator returns true (result is in ctx->r):
3586
  //   "a" should be moved to the last element position in merged list.
3587
  //   A new element from "a_rest" should be moved into comparator argument 1 pos.
3588
  // else
3589
  //   "b" should be moved to last element position in merged list.
3590
  //   A new element from "b_rest" should be moved into comparator argument 2 pos.
3591
  //
3592
  // If a_rest or b_rest is NIL:
3593
  //   we are done, the remaining elements of
3594
  //   non_nil list should be appended to merged list.
3595
  // else
3596
  //   Set up for a new comparator evaluation and recurse.
3597
8791580
  lbm_value a = sptr[2];
3598
8791580
  lbm_value b = lbm_cdr(a);
3599
8791580
  lbm_set_cdr(a, ENC_SYM_NIL); // terminate 1 element list
3600
3601
8791580
  if (ctx->r == ENC_SYM_NIL) { // Comparison false
3602
3603
5102216
    if (sptr[0] == ENC_SYM_NIL) {
3604
1983576
      sptr[0] = b;
3605
1983576
      sptr[1] = b;
3606
    } else {
3607
3118640
      lbm_set_cdr(sptr[1], b);
3608
3118640
      sptr[1] = b;
3609
    }
3610
5102216
    if (sptr[4] == ENC_SYM_NIL) {
3611
2549456
      lbm_set_cdr(a, sptr[3]);
3612
2549456
      lbm_set_cdr(sptr[1], a);
3613
2549456
      ctx->r = sptr[0];
3614
2549456
      lbm_stack_drop(&ctx->K, 9);
3615
2549456
      ctx->app_cont = true;
3616
2549456
      return;
3617
    } else {
3618
2552760
      b = sptr[4];
3619
2552760
      sptr[4] = lbm_cdr(sptr[4]);
3620
2552760
      lbm_set_cdr(b, ENC_SYM_NIL);
3621
    }
3622
  } else {
3623
3689364
    if (sptr[0] == ENC_SYM_NIL) {
3624
1134812
      sptr[0] = a;
3625
1134812
      sptr[1] = a;
3626
    } else {
3627
2554552
      lbm_set_cdr(sptr[1], a);
3628
2554552
      sptr[1] = a;
3629
    }
3630
3631
3689364
    if (sptr[3] == ENC_SYM_NIL) {
3632
568932
      lbm_set_cdr(b, sptr[4]);
3633
568932
      lbm_set_cdr(sptr[1], b);
3634
568932
      ctx->r = sptr[0];
3635
568932
      lbm_stack_drop(&ctx->K, 9);
3636
568932
      ctx->app_cont = true;
3637
568932
      return;
3638
    } else {
3639
3120432
      a = sptr[3];
3640
3120432
      sptr[3] = lbm_cdr(sptr[3]);
3641
3120432
      lbm_set_cdr(a, ENC_SYM_NIL);
3642
    }
3643
  }
3644
5673192
  lbm_set_cdr(a, b);
3645
5673192
  sptr[2] = a;
3646
3647
5673192
  lbm_value par1 = sptr[7];
3648
5673192
  lbm_value par2 = sptr[8];
3649
5673192
  lbm_value cmp_body = sptr[5];
3650
5673192
  lbm_value cmp_env = sptr[6];
3651
  // Environment should be preallocated already at this point
3652
  // and the operations below should never need GC.
3653
5673192
  lbm_value new_env0 = lbm_env_set(cmp_env, par1, lbm_car(a));
3654
5673192
  lbm_value new_env = lbm_env_set(new_env0, par2, lbm_car(b));
3655

5673192
  if (lbm_is_symbol(new_env0) || lbm_is_symbol(new_env)) {
3656
    error_ctx(ENC_SYM_FATAL_ERROR);
3657
  }
3658
5673192
  cmp_env = new_env;
3659
3660
5673192
  stack_reserve(ctx,1)[0] = MERGE_REST;
3661
5673192
  ctx->curr_exp = cmp_body;
3662
5673192
  ctx->curr_env = cmp_env;
3663
}
3664
3665
// merge_layer stack contents
3666
// s[sp-9] = cmp
3667
// s[sp-8] = cmp_env
3668
// s[sp-7] = par1
3669
// s[sp-6] = par2
3670
// s[sp-5] = acc - first cell
3671
// s[sp-4] = acc - last cell
3672
// s[sp-3] = rest;
3673
// s[sp-2] = layer
3674
// s[sp-1] = length or original list
3675
//
3676
// ctx->r merged sublist
3677
3401272
static void cont_merge_layer(eval_context_t *ctx) {
3678
3401272
  lbm_uint *sptr = get_stack_ptr(ctx, 9);
3679
3401272
  lbm_int layer = lbm_dec_i(sptr[7]);
3680
3401272
  lbm_int len = lbm_dec_i(sptr[8]);
3681
3682
3401272
  lbm_value r_curr = ctx->r;
3683
13620600
  while (lbm_is_cons(r_curr)) {
3684
13620600
    lbm_value next = lbm_cdr(r_curr);
3685
13620600
    if (next == ENC_SYM_NIL) {
3686
3401272
      break;
3687
    }
3688
10219328
    r_curr = next;
3689
  }
3690
3691
3401272
  if (sptr[4] == ENC_SYM_NIL) {
3692
1132348
    sptr[4] = ctx->r;
3693
1132348
    sptr[5] = r_curr;
3694
  } else {
3695
2268924
    lbm_set_cdr(sptr[5], ctx->r); // accumulate merged sublists.
3696
2268924
    sptr[5] = r_curr;
3697
  }
3698
3699
3401272
  lbm_value layer_rest = sptr[6];
3700
  // switch layer or done ?
3701
3401272
  if (layer_rest == ENC_SYM_NIL) {
3702
1132348
    if (layer * 2 >= len) {
3703
283108
      ctx->r = sptr[4];
3704
283108
      ctx->app_cont = true;
3705
283108
      lbm_stack_drop(&ctx->K, 9);
3706
283108
      return;
3707
    } else {
3708
      // Setup for merges of the next layer
3709
849240
      layer = layer * 2;
3710
849240
      sptr[7] = lbm_enc_i(layer);
3711
849240
      layer_rest = sptr[4]; // continue on the accumulation of all sublists.
3712
849240
      sptr[5] = ENC_SYM_NIL;
3713
849240
      sptr[4] = ENC_SYM_NIL;
3714
    }
3715
  }
3716
  // merge another sublist based on current layer.
3717
3118164
  lbm_value a_list = layer_rest;
3718
  // build sublist a
3719
3118164
  lbm_value curr = layer_rest;
3720
7661080
  for (int i = 0; i < layer-1; i ++) {
3721
4543028
    if (lbm_is_cons(curr)) {
3722
4542916
      curr = lbm_cdr(curr);
3723
    } else {
3724
112
      break;
3725
    }
3726
  }
3727
3118164
  layer_rest = lbm_cdr(curr);
3728
3118164
  lbm_set_cdr(curr, ENC_SYM_NIL); //terminate sublist.
3729
3730
3118164
  lbm_value b_list = layer_rest;
3731
  // build sublist b
3732
3118164
  curr = layer_rest;
3733
5959800
  for (int i = 0; i < layer-1; i ++) {
3734
3407796
    if (lbm_is_cons(curr)) {
3735
2841636
      curr = lbm_cdr(curr);
3736
    } else {
3737
566160
      break;
3738
    }
3739
  }
3740
3118164
  layer_rest = lbm_cdr(curr);
3741
3118164
  lbm_set_cdr(curr, ENC_SYM_NIL); //terminate sublist.
3742
3743
3118164
  sptr[6] = layer_rest;
3744
3745
3118164
  if (b_list == ENC_SYM_NIL) {
3746
283192
    stack_reserve(ctx,1)[0] = MERGE_LAYER;
3747
283192
    ctx->r = a_list;
3748
283192
    ctx->app_cont = true;
3749
283192
    return;
3750
  }
3751
  // Set up for a merge of sublists.
3752
3753
2834972
  lbm_value a_rest = lbm_cdr(a_list);
3754
2834972
  lbm_value b_rest = lbm_cdr(b_list);
3755
2834972
  lbm_value a = a_list;
3756
2834972
  lbm_value b = b_list;
3757
2834972
  lbm_set_cdr(a, b);
3758
  // Terminating the b list would be incorrect here
3759
  // if there was any chance that the environment update below
3760
  // performs GC.
3761
2834972
  lbm_set_cdr(b, ENC_SYM_NIL);
3762
3763
2834972
  lbm_value cmp_body = sptr[0];
3764
2834972
  lbm_value cmp_env = sptr[1];
3765
2834972
  lbm_value par1 = sptr[2];
3766
2834972
  lbm_value par2 = sptr[3];
3767
  // Environment should be preallocated already at this point
3768
  // and the operations below should never need GC.
3769
2834972
  lbm_value new_env0 = lbm_env_set(cmp_env, par1, lbm_car(a));
3770
2834972
  lbm_value new_env = lbm_env_set(cmp_env, par2, lbm_car(b));
3771

2834972
  if (lbm_is_symbol(new_env0) || lbm_is_symbol(new_env)) {
3772
    error_ctx(ENC_SYM_FATAL_ERROR);
3773
  }
3774
2834972
  cmp_env = new_env;
3775
3776
2834972
  lbm_uint *merge_cont = stack_reserve(ctx, 11);
3777
2834972
  merge_cont[0] = MERGE_LAYER;
3778
2834972
  merge_cont[1] = ENC_SYM_NIL;
3779
2834972
  merge_cont[2] = ENC_SYM_NIL;
3780
2834972
  merge_cont[3] = a;
3781
2834972
  merge_cont[4] = a_rest;
3782
2834972
  merge_cont[5] = b_rest;
3783
2834972
  merge_cont[6] = cmp_body;
3784
2834972
  merge_cont[7] = cmp_env;
3785
2834972
  merge_cont[8] = par1;
3786
2834972
  merge_cont[9] = par2;
3787
2834972
  merge_cont[10] = MERGE_REST;
3788
2834972
  ctx->curr_exp = cmp_body;
3789
2834972
  ctx->curr_env = cmp_env;
3790
2834972
  return;
3791
}
3792
3793
/****************************************************/
3794
/*   READER                                         */
3795
3796
33257
static void read_finish(lbm_char_channel_t *str, eval_context_t *ctx) {
3797
3798
  /* Tokenizer reached "end of file"
3799
     The parser could be in a state where it needs
3800
     more tokens to correctly finish an expression.
3801
3802
     Four cases
3803
     1. The program / expression is malformed and the context should die.
3804
     2. We are finished reading a program and should close off the
3805
     internal representation with a closing parenthesis. Then
3806
     apply continuation.
3807
     3. We are finished reading an expression and should
3808
     apply the continuation
3809
     4. We are finished read-and-evaluating
3810
3811
     In case 2, we should find the READ_DONE at sp - 5.
3812
     In case 3, we should find the READ_DONE at sp - 1.
3813
     In case 4, we should find the READ_DONE at sp - 4.
3814
3815
     case 3 should not end up here, but rather end up in
3816
     cont_read_done.
3817
  */
3818
3819
33257
  if (lbm_is_symbol(ctx->r)) {
3820
10704
    lbm_uint sym_val = lbm_dec_sym(ctx->r);
3821

10704
    if (sym_val >= TOKENIZER_SYMBOLS_START &&
3822
        sym_val <= TOKENIZER_SYMBOLS_END) {
3823
      read_error_ctx(lbm_channel_row(str), lbm_channel_column(str));
3824
    }
3825
  }
3826
3827

33257
  if (ctx->K.sp > 4  && (ctx->K.data[ctx->K.sp - 4] == READ_DONE) &&
3828
22071
      (ctx->K.data[ctx->K.sp - 5] == READING_PROGRAM_INCREMENTALLY)) {
3829
    /* read and evaluate is done */
3830
    lbm_value env;
3831
    lbm_value s;
3832
    lbm_value sym;
3833
22071
    lbm_pop_3(&ctx->K, &sym, &env, &s);
3834
22071
    ctx->curr_env = env;
3835
22071
    ctx->app_cont = true; // Program evaluated and result is in ctx->r.
3836

11186
  } else if (ctx->K.sp > 5 && (ctx->K.data[ctx->K.sp - 5] == READ_DONE) &&
3837
11186
             (ctx->K.data[ctx->K.sp - 6] == READING_PROGRAM)) {
3838
    /* successfully finished reading a program  (CASE 2) */
3839
11186
    ctx->r = ENC_SYM_CLOSEPAR;
3840
11186
    ctx->app_cont = true;
3841
  } else {
3842
    if (lbm_channel_row(str) == 1 && lbm_channel_column(str) == 1) {
3843
      // (read "") evaluates to nil.
3844
      ctx->r = ENC_SYM_NIL;
3845
      ctx->app_cont = true;
3846
    } else {
3847
      lbm_channel_reader_close(str);
3848
      lbm_set_error_reason((char*)lbm_error_str_parse_eof);
3849
      read_error_ctx(lbm_channel_row(str), lbm_channel_column(str));
3850
    }
3851
  }
3852
33257
}
3853
3854
/* cont_read_next_token
3855
   sp-2 : Stream
3856
   sp-1 : Grab row
3857
*/
3858
5697542
static void cont_read_next_token(eval_context_t *ctx) {
3859
5697542
  lbm_value *sptr = get_stack_ptr(ctx, 2);
3860
5697542
  lbm_value stream = sptr[0];
3861
5697542
  lbm_value grab_row0 = sptr[1];
3862
3863
5697542
  lbm_char_channel_t *chan = lbm_dec_channel(stream);
3864

5697542
  if (chan == NULL || chan->state == NULL) {
3865
    error_ctx(ENC_SYM_FATAL_ERROR);
3866
5697542
    return; // INFER does not understant that error_ctx longjmps
3867
            // out of this function.
3868
  }
3869
3870

5697542
  if (!lbm_channel_more(chan) && lbm_channel_is_empty(chan)) {
3871
11872
    lbm_stack_drop(&ctx->K, 2);
3872
11872
    read_finish(chan, ctx);
3873
11872
    return;
3874
  }
3875
  /* Eat whitespace and comments */
3876
5685670
  if (!tok_clean_whitespace(chan)) {
3877
625
    sptr[0] = stream;
3878
625
    sptr[1] = lbm_enc_u(0);
3879
625
    stack_reserve(ctx,1)[0] = READ_NEXT_TOKEN;
3880
625
    yield_ctx(EVAL_CPS_MIN_SLEEP);
3881
625
    return;
3882
  }
3883
  /* After eating whitespace we may be at end of file/stream */
3884

5685045
  if (!lbm_channel_more(chan) && lbm_channel_is_empty(chan)) {
3885
21385
    lbm_stack_drop(&ctx->K, 2);
3886
21385
    read_finish(chan, ctx);
3887
21385
    return;
3888
  }
3889
3890
5663660
  if (lbm_dec_u(grab_row0)) {
3891
377897
    ctx->row0 = (int32_t)lbm_channel_row(chan);
3892
377897
    ctx->row1 = -1; // a new start, end is unknown
3893
  }
3894
3895
  /* Attempt to extract tokens from the character stream */
3896
5663660
  int n = 0;
3897
5663660
  lbm_value res = ENC_SYM_NIL;
3898
5663660
  unsigned int string_len = 0;
3899
3900
  /*
3901
   * SYNTAX
3902
   */
3903
  uint32_t tok_match;
3904
5663660
  n = tok_syntax(chan, &tok_match);
3905
5663660
  if (n > 0) {
3906
1403640
    if (!lbm_channel_drop(chan, (unsigned int)n)) {
3907
      error_ctx(ENC_SYM_FATAL_ERROR);
3908
    }
3909
1403640
    ctx->app_cont = true;
3910



1403640
    switch(tok_match) {
3911
666008
    case TOKOPENPAR: {
3912
666008
      sptr[0] = ENC_SYM_NIL;
3913
666008
      sptr[1] = ENC_SYM_NIL;
3914
666008
      lbm_value *rptr = stack_reserve(ctx,5);
3915
666008
      rptr[0] = stream;
3916
666008
      rptr[1] = READ_APPEND_CONTINUE;
3917
666008
      rptr[2] = stream;
3918
666008
      rptr[3] = lbm_enc_u(0);
3919
666008
      rptr[4] = READ_NEXT_TOKEN;
3920
666008
      ctx->r = ENC_SYM_OPENPAR;
3921
666008
    } return;
3922
666008
    case TOKCLOSEPAR: {
3923
666008
      lbm_stack_drop(&ctx->K, 2);
3924
666008
      ctx->r = ENC_SYM_CLOSEPAR;
3925
666008
    } return;
3926
3304
    case TOKOPENBRACK: {
3927
3304
      sptr[0] = stream;
3928
3304
      sptr[1] = READ_START_ARRAY;
3929
3304
      lbm_value *rptr = stack_reserve(ctx, 3);
3930
3304
      rptr[0] = stream;
3931
3304
      rptr[1] = lbm_enc_u(0);
3932
3304
      rptr[2] = READ_NEXT_TOKEN;
3933
3304
      ctx->r = ENC_SYM_OPENBRACK;
3934
3304
    } return;
3935
3304
    case TOKCLOSEBRACK:
3936
3304
      lbm_stack_drop(&ctx->K, 2);
3937
3304
      ctx->r = ENC_SYM_CLOSEBRACK;
3938
3304
      return;
3939
6104
    case TOKDOT:
3940
6104
      lbm_stack_drop(&ctx->K, 2);
3941
6104
      ctx->r = ENC_SYM_DOT;
3942
6104
      return;
3943
980
    case TOKDONTCARE:
3944
980
      lbm_stack_drop(&ctx->K, 2);
3945
980
      ctx->r = ENC_SYM_DONTCARE;
3946
980
      return;
3947
27188
    case TOKQUOTE:
3948
27188
      sptr[0] = ENC_SYM_QUOTE;
3949
27188
      sptr[1] = WRAP_RESULT;
3950
27188
      break;
3951
5068
    case TOKBACKQUOTE: {
3952
5068
      sptr[0] = QQ_EXPAND_START;
3953
5068
      sptr[1] = stream;
3954
5068
      lbm_value *rptr = stack_reserve(ctx, 2);
3955
5068
      rptr[0] = lbm_enc_u(0);
3956
5068
      rptr[1] = READ_NEXT_TOKEN;
3957
5068
      ctx->app_cont = true;
3958
5068
    } return;
3959
140
    case TOKCOMMAAT:
3960
140
      sptr[0] = ENC_SYM_COMMAAT;
3961
140
      sptr[1] = WRAP_RESULT;
3962
140
      break;
3963
13944
    case TOKCOMMA:
3964
13944
      sptr[0] = ENC_SYM_COMMA;
3965
13944
      sptr[1] = WRAP_RESULT;
3966
13944
      break;
3967
6720
    case TOKMATCHANY:
3968
6720
      lbm_stack_drop(&ctx->K, 2);
3969
6720
      ctx->r = ENC_SYM_MATCH_ANY;
3970
6720
      return;
3971
2408
    case TOKOPENCURL: {
3972
2408
      sptr[0] = ENC_SYM_NIL;
3973
2408
      sptr[1] = ENC_SYM_NIL;
3974
2408
      lbm_value *rptr = stack_reserve(ctx,2);
3975
2408
      rptr[0] = stream;
3976
2408
      rptr[1] = READ_APPEND_CONTINUE;
3977
2408
      ctx->r = ENC_SYM_PROGN;
3978
2408
    } return;
3979
2408
    case TOKCLOSECURL:
3980
2408
      lbm_stack_drop(&ctx->K, 2);
3981
2408
      ctx->r = ENC_SYM_CLOSEPAR;
3982
2408
      return;
3983
56
    case TOKCONSTSTART: /* fall through */
3984
    case TOKCONSTEND: {
3985
56
      if (tok_match == TOKCONSTSTART)  ctx->flags |= EVAL_CPS_CONTEXT_FLAG_CONST;
3986
56
      if (tok_match == TOKCONSTEND)    ctx->flags &= ~EVAL_CPS_CONTEXT_FLAG_CONST;
3987
56
      sptr[0] = stream;
3988
56
      sptr[1] = lbm_enc_u(0);
3989
56
      stack_reserve(ctx,1)[0] = READ_NEXT_TOKEN;
3990
56
      ctx->app_cont = true;
3991
56
    } return;
3992
    default:
3993
      read_error_ctx(lbm_channel_row(chan), lbm_channel_column(chan));
3994
    }
3995
    // read next token
3996
41272
    lbm_value *rptr = stack_reserve(ctx, 3);
3997
41272
    rptr[0] = stream;
3998
41272
    rptr[1] = lbm_enc_u(0);
3999
41272
    rptr[2] = READ_NEXT_TOKEN;
4000
41272
    ctx->app_cont = true;
4001
41272
    return;
4002
4260020
  } else if (n < 0) goto retry_token;
4003
4004
  /*
4005
   *  STRING
4006
   */
4007
4260020
  n = tok_string(chan, &string_len);
4008
4260020
  if (n >= 2) {
4009
9380
    lbm_channel_drop(chan, (unsigned int)n);
4010
#ifdef LBM_ALWAYS_GC
4011
    gc();
4012
#endif
4013
9380
    if (!lbm_heap_allocate_array(&res, (unsigned int)(string_len+1))) {
4014
      gc();
4015
      lbm_heap_allocate_array(&res, (unsigned int)(string_len+1));
4016
    }
4017
9380
    if (lbm_is_ptr(res)) {
4018
9380
      lbm_array_header_t *arr = assume_array(res);
4019
9380
      char *data = (char*)arr->data;
4020
9380
      memset(data,0, string_len + 1);
4021
9380
      memcpy(data, tokpar_sym_str, string_len);
4022
9380
      lbm_stack_drop(&ctx->K, 2);
4023
9380
      ctx->r = res;
4024
9380
      ctx->app_cont = true;
4025
9380
      return;
4026
    } else {
4027
      error_ctx(ENC_SYM_MERROR);
4028
    }
4029
4250640
  } else if (n < 0) goto retry_token;
4030
4031
  /*
4032
   * FLOAT
4033
   */
4034
  token_float f_val;
4035
4250639
  n = tok_double(chan, &f_val);
4036
4250639
  if (n > 0) {
4037
11452
    lbm_channel_drop(chan, (unsigned int) n);
4038
11452
    switch(f_val.type) {
4039
8428
    case TOKTYPEF32:
4040

8428
      WITH_GC(res, lbm_enc_float((float)f_val.value));
4041
8428
      break;
4042
3024
    case TOKTYPEF64:
4043
3024
      res = lbm_enc_double(f_val.value);
4044
3024
      break;
4045
    default:
4046
      read_error_ctx(lbm_channel_row(chan), lbm_channel_column(chan));
4047
    }
4048
11452
    lbm_stack_drop(&ctx->K, 2);
4049
11452
    ctx->r = res;
4050
11452
    ctx->app_cont = true;
4051
11452
    return;
4052
4239187
  } else if (n < 0) goto retry_token;
4053
4054
  /*
4055
   * INTEGER
4056
   */
4057
  token_int int_result;
4058
4239184
  n = tok_integer(chan, &int_result);
4059
4239184
  if (n > 0) {
4060
3356584
    lbm_channel_drop(chan, (unsigned int)n);
4061


3356584
    switch(int_result.type) {
4062
2212
    case TOKTYPEBYTE:
4063
2212
      res = lbm_enc_char((uint8_t)(int_result.negative ? -int_result.value : int_result.value));
4064
2212
      break;
4065
3335528
    case TOKTYPEI:
4066
3335528
      res = lbm_enc_i((lbm_int)(int_result.negative ? -int_result.value : int_result.value));
4067
3335528
      break;
4068
3500
    case TOKTYPEU:
4069
3500
      res = lbm_enc_u((lbm_uint)(int_result.negative ? -int_result.value : int_result.value));
4070
3500
      break;
4071
3668
    case TOKTYPEI32:
4072


3668
      WITH_GC(res, lbm_enc_i32((int32_t)(int_result.negative ? -int_result.value : int_result.value)));
4073
3668
      break;
4074
4480
    case TOKTYPEU32:
4075


4480
      WITH_GC(res,lbm_enc_u32((uint32_t)(int_result.negative ? -int_result.value : int_result.value)));
4076
4480
      break;
4077
3780
    case TOKTYPEI64:
4078


3780
      WITH_GC(res,lbm_enc_i64((int64_t)(int_result.negative ? -int_result.value : int_result.value)));
4079
3780
      break;
4080
3416
    case TOKTYPEU64:
4081


3416
      WITH_GC(res,lbm_enc_u64((uint64_t)(int_result.negative ? -int_result.value : int_result.value)));
4082
3416
      break;
4083
    default:
4084
      read_error_ctx(lbm_channel_row(chan), lbm_channel_column(chan));
4085
    }
4086
3356584
    lbm_stack_drop(&ctx->K, 2);
4087
3356584
    ctx->r = res;
4088
3356584
    ctx->app_cont = true;
4089
3356584
    return;
4090
882600
  } else if (n < 0) goto retry_token;
4091
4092
  /*
4093
   * SYMBOL
4094
   */
4095
882600
  n = tok_symbol(chan);
4096
882600
  if (n > 0) {
4097
882420
    lbm_channel_drop(chan, (unsigned int) n);
4098
    lbm_uint symbol_id;
4099
882420
    if (!lbm_get_symbol_by_name(tokpar_sym_str, &symbol_id)) {
4100
99358
      int r = 0;
4101
99358
      if (n > 4 &&
4102
23814
          tokpar_sym_str[0] == 'e' &&
4103
434
          tokpar_sym_str[1] == 'x' &&
4104
70
          tokpar_sym_str[2] == 't' &&
4105
56
          tokpar_sym_str[3] == '-') {
4106
        lbm_uint ext_id;
4107
14
        lbm_uint ext_name_len = (lbm_uint)n + 1;
4108
#ifdef LBM_ALWAYS_GC
4109
        gc();
4110
#endif
4111
14
        char *ext_name = lbm_malloc(ext_name_len);
4112
14
        if (!ext_name) {
4113
          gc();
4114
          ext_name = lbm_malloc(ext_name_len);
4115
        }
4116
14
        if (ext_name) {
4117
14
          memcpy(ext_name, tokpar_sym_str, ext_name_len);
4118
14
          r = lbm_add_extension(ext_name, lbm_extensions_default);
4119
14
          if (!lbm_lookup_extension_id(ext_name, &ext_id)) {
4120
            error_ctx(ENC_SYM_FATAL_ERROR);
4121
          }
4122
14
          symbol_id = ext_id;
4123
        } else {
4124
          error_ctx(ENC_SYM_MERROR);
4125
        }
4126
      } else {
4127
99344
        if (ctx->flags & EVAL_CPS_CONTEXT_FLAG_CONST &&
4128
140
            ctx->flags & EVAL_CPS_CONTEXT_FLAG_INCREMENTAL_READ) {
4129
70
          r = lbm_add_symbol_base(tokpar_sym_str, &symbol_id, true); //flash
4130
70
          if (!r) {
4131
            lbm_set_error_reason((char*)lbm_error_str_flash_error);
4132
            error_ctx(ENC_SYM_FATAL_ERROR);
4133
          }
4134
        } else {
4135
#ifdef LBM_ALWAYS_GC
4136
          gc();
4137
#endif
4138
99274
          r = lbm_add_symbol_base(tokpar_sym_str, &symbol_id,false); //ram
4139
99274
          if (!r) {
4140
10
            gc();
4141
10
            r = lbm_add_symbol_base(tokpar_sym_str, &symbol_id,false); //ram
4142
          }
4143
        }
4144
      }
4145
99358
      if (!r) {
4146
        read_error_ctx(lbm_channel_row(chan), lbm_channel_column(chan));
4147
      }
4148
    }
4149
882420
    lbm_stack_drop(&ctx->K, 2);
4150
882420
    ctx->r = lbm_enc_sym(symbol_id);
4151
882420
    ctx->app_cont = true;
4152
882420
    return;
4153
180
  } else if (n == TOKENIZER_NEED_MORE) {
4154
12
    goto retry_token;
4155
168
  } else if (n <= TOKENIZER_STRING_ERROR) {
4156
    read_error_ctx(lbm_channel_row(chan), lbm_channel_column(chan));
4157
  }
4158
4159
  /*
4160
   * CHAR
4161
   */
4162
  char c_val;
4163
168
  n = tok_char(chan, &c_val);
4164
168
  if(n > 0) {
4165
168
    lbm_channel_drop(chan,(unsigned int) n);
4166
168
    lbm_stack_drop(&ctx->K, 2);
4167
168
    ctx->r = lbm_enc_char((uint8_t)c_val);
4168
168
    ctx->app_cont = true;
4169
168
    return;
4170
  }else if (n < 0) goto retry_token;
4171
4172
  read_error_ctx(lbm_channel_row(chan), lbm_channel_column(chan));
4173
4174
16
 retry_token:
4175
16
  if (n == TOKENIZER_NEED_MORE) {
4176
16
    sptr[0] = stream;
4177
16
    sptr[1] = lbm_enc_u(0);
4178
16
    stack_reserve(ctx,1)[0] = READ_NEXT_TOKEN;
4179
16
    yield_ctx(EVAL_CPS_MIN_SLEEP);
4180
16
    return;
4181
  }
4182
  read_error_ctx(lbm_channel_row(chan), lbm_channel_column(chan));
4183
}
4184
4185
3304
static void cont_read_start_array(eval_context_t *ctx) {
4186
3304
  lbm_value *sptr = get_stack_ptr(ctx, 1);
4187
3304
  lbm_value stream = sptr[0];
4188
4189
3304
  lbm_char_channel_t *str = lbm_dec_channel(stream);
4190

3304
  if (str == NULL || str->state == NULL) {
4191
    error_ctx(ENC_SYM_FATAL_ERROR);
4192
    return; // INFER does not understand that error_ctx longjmps out
4193
            // of this function here.
4194
  }
4195
3304
  if (ctx->r == ENC_SYM_CLOSEBRACK) {
4196
    lbm_value array;
4197
4198
56
    if (!lbm_heap_allocate_array(&array, 0)) {
4199
      gc();
4200
      if (!lbm_heap_allocate_array(&array, 0)) {
4201
        lbm_set_error_reason((char*)lbm_error_str_read_no_mem);
4202
        lbm_channel_reader_close(str);
4203
        error_ctx(ENC_SYM_FATAL_ERROR); // Terminates ctx
4204
      }
4205
    }
4206
56
    lbm_stack_drop(&ctx->K, 1);
4207
56
    ctx->r = array;
4208
56
    ctx->app_cont = true;
4209
3248
  } else if (lbm_is_number(ctx->r)) {
4210
#ifdef LBM_ALWAYS_GC
4211
    gc();
4212
#endif
4213
3248
    lbm_uint num_free = lbm_memory_longest_free();
4214
3248
    lbm_uint initial_size = (lbm_uint)((float)num_free * 0.9);
4215
3248
    if (initial_size == 0) {
4216
      gc();
4217
      num_free = lbm_memory_longest_free();
4218
      initial_size = (lbm_uint)((float)num_free * 0.9);
4219
      if (initial_size == 0) {
4220
        lbm_channel_reader_close(str);
4221
        error_ctx(ENC_SYM_MERROR);
4222
      }
4223
    }
4224
    lbm_value array;
4225
3248
    initial_size = sizeof(lbm_uint) * initial_size;
4226
4227
    // Keep in mind that this allocation can fail for both
4228
    // lbm_memory and heap reasons.
4229
3248
    if (!lbm_heap_allocate_array(&array, initial_size)) {
4230
      gc();
4231
      if (!lbm_heap_allocate_array(&array, initial_size)) {
4232
        lbm_set_error_reason((char*)lbm_error_str_read_no_mem);
4233
        lbm_channel_reader_close(str);
4234
        error_ctx(ENC_SYM_FATAL_ERROR);
4235
        // NOTE: If array is not created evaluation ends here.
4236
        // Static analysis seems unaware.
4237
      }
4238
    }
4239
4240
3248
    sptr[0] = array;
4241
3248
    lbm_value *rptr = stack_reserve(ctx, 4);
4242
3248
    rptr[0] = lbm_enc_u(initial_size);
4243
3248
    rptr[1] = lbm_enc_u(0);
4244
3248
    rptr[2] = stream;
4245
3248
    rptr[3] = READ_APPEND_ARRAY;
4246
3248
    ctx->app_cont = true;
4247
  } else {
4248
    lbm_channel_reader_close(str);
4249
    read_error_ctx(lbm_channel_row(str), lbm_channel_column(str));
4250
  }
4251
}
4252
4253
371000
static void cont_read_append_array(eval_context_t *ctx) {
4254
371000
  lbm_uint *sptr = get_stack_ptr(ctx, 4);
4255
4256
371000
  lbm_value array  = sptr[0];
4257
371000
  lbm_value size   = lbm_dec_as_u32(sptr[1]);
4258
371000
  lbm_value ix     = lbm_dec_as_u32(sptr[2]);
4259
371000
  lbm_value stream = sptr[3];
4260
4261
371000
  if (ix >= (size - 1)) {
4262
    error_ctx(ENC_SYM_MERROR);
4263
  }
4264
4265
  // if sptr[0] is not an array something is very very wrong.
4266
  // Not robust against a garbage on stack. But how would garbage get onto stack?
4267
371000
  lbm_array_header_t *arr = assume_array(array);
4268
371000
  if (lbm_is_number(ctx->r)) {
4269
367752
    ((uint8_t*)arr->data)[ix] = (uint8_t)lbm_dec_as_u32(ctx->r);
4270
4271
367752
    sptr[2] = lbm_enc_u(ix + 1);
4272
367752
    lbm_value *rptr = stack_reserve(ctx, 4);
4273
367752
    rptr[0] = READ_APPEND_ARRAY;
4274
367752
    rptr[1] = stream;
4275
367752
    rptr[2] = lbm_enc_u(0);
4276
367752
    rptr[3] = READ_NEXT_TOKEN;
4277
367752
    ctx->app_cont = true;
4278

3248
  } else if (lbm_is_symbol(ctx->r) && ctx->r == ENC_SYM_CLOSEBRACK) {
4279
3248
    lbm_uint array_size = ix / sizeof(lbm_uint);
4280
4281
3248
    if (ix % sizeof(lbm_uint) != 0) {
4282
2436
      array_size = array_size + 1;
4283
    }
4284
3248
    lbm_memory_shrink((lbm_uint*)arr->data, array_size);
4285
3248
    arr->size = ix;
4286
3248
    lbm_stack_drop(&ctx->K, 4);
4287
3248
    ctx->r = array;
4288
3248
    ctx->app_cont = true;
4289
  } else {
4290
    error_ctx(ENC_SYM_TERROR);
4291
  }
4292
371000
}
4293
4294
4880834
static void cont_read_append_continue(eval_context_t *ctx) {
4295
4880834
  lbm_value *sptr = get_stack_ptr(ctx, 3);
4296
4297
4880834
  lbm_value first_cell = sptr[0];
4298
4880834
  lbm_value last_cell  = sptr[1];
4299
4880834
  lbm_value stream     = sptr[2];
4300
4301
4880834
  lbm_char_channel_t *str = lbm_dec_channel(stream);
4302

4880834
  if (str == NULL || str->state == NULL) {
4303
    error_ctx(ENC_SYM_FATAL_ERROR);
4304
    return; // INFER does not understand that execution
4305
            // jumps out on error_ctx with a longjmp.
4306
  }
4307
4308
4880834
  if (lbm_type_of(ctx->r) == LBM_TYPE_SYMBOL) {
4309
4310
1526994
    switch(ctx->r) {
4311
673498
    case ENC_SYM_CLOSEPAR:
4312
673498
      if (lbm_type_of(last_cell) == LBM_TYPE_CONS) {
4313
670838
        lbm_set_cdr(last_cell, ENC_SYM_NIL); // terminate the list
4314
670838
        ctx->r = first_cell;
4315
      } else {
4316
2660
        ctx->r = ENC_SYM_NIL;
4317
      }
4318
673498
      lbm_stack_drop(&ctx->K, 3);
4319
      /* Skip reading another token and apply the continuation */
4320
673498
      ctx->app_cont = true;
4321
673498
      return;
4322
6104
    case ENC_SYM_DOT: {
4323
6104
      lbm_value *rptr = stack_reserve(ctx, 4);
4324
6104
      rptr[0] = READ_DOT_TERMINATE;
4325
6104
      rptr[1] = stream;
4326
6104
      rptr[2] = lbm_enc_u(0);
4327
6104
      rptr[3] = READ_NEXT_TOKEN;
4328
6104
      ctx->app_cont = true;
4329
6104
    } return;
4330
    }
4331
  }
4332
4201232
  lbm_value new_cell = cons_with_gc(ctx->r, ENC_SYM_NIL, ENC_SYM_NIL);
4333
  // Does not return if merror. So we cannot get a read-error here
4334
  // unless we write the a version of cons_with_gc here.
4335
  //if (lbm_is_symbol_merror(new_cell)) {
4336
  //  lbm_channel_reader_close(str);
4337
  //  read_error_ctx(lbm_channel_row(str), lbm_channel_column(str));
4338
  //  return;
4339
  //}
4340
4201232
  if (lbm_type_of(last_cell) == LBM_TYPE_CONS) {
4341
3524290
    lbm_set_cdr(last_cell, new_cell);
4342
3524290
    last_cell = new_cell;
4343
  } else {
4344
676942
    first_cell = last_cell = new_cell;
4345
  }
4346
4201232
  sptr[0] = first_cell;
4347
4201232
  sptr[1] = last_cell;
4348
  //sptr[2] = stream;    // unchanged.
4349
4201232
  lbm_value *rptr = stack_reserve(ctx, 4);
4350
4201232
  rptr[0] = READ_APPEND_CONTINUE;
4351
4201232
  rptr[1] = stream;
4352
4201232
  rptr[2] = lbm_enc_u(0);
4353
4201232
  rptr[3] = READ_NEXT_TOKEN;
4354
4201232
  ctx->app_cont = true;
4355
}
4356
4357
69720
static void cont_read_eval_continue(eval_context_t *ctx) {
4358
  lbm_value env;
4359
  lbm_value stream;
4360
69720
  lbm_value *sptr = get_stack_ptr(ctx, 2);
4361
69720
  env = sptr[1];
4362
69720
  stream = sptr[0];
4363
69720
  lbm_char_channel_t *str = lbm_dec_channel(stream);
4364

69720
  if (str && str->state) {
4365
69720
    ctx->row1 = (lbm_int)str->row(str);
4366
69720
    if (lbm_type_of(ctx->r) == LBM_TYPE_SYMBOL) {
4367
5600
      switch(ctx->r) {
4368
      case ENC_SYM_CLOSEPAR:
4369
        lbm_stack_drop(&ctx->K, 2);
4370
        ctx->app_cont = true;
4371
        return;
4372
      case ENC_SYM_DOT:
4373
        // A dot here is a syntax error.
4374
        lbm_set_error_reason((char*)lbm_error_str_parse_dot);
4375
        read_error_ctx(lbm_channel_row(str),lbm_channel_column(str));
4376
        return;
4377
      }
4378
    }
4379
69720
    lbm_value *rptr = stack_reserve(ctx, 6);
4380
69720
    rptr[0] = READ_EVAL_CONTINUE;
4381
69720
    rptr[1] = stream;
4382
69720
    rptr[2] = lbm_enc_u(1);
4383
69720
    rptr[3] = READ_NEXT_TOKEN;
4384
69720
    rptr[4] = lbm_enc_u(ctx->flags);
4385
69720
    rptr[5] = POP_READER_FLAGS;
4386
4387
69720
    ctx->curr_env = env;
4388
69720
    ctx->curr_exp = ctx->r;
4389
  } else {
4390
    error_ctx(ENC_SYM_FATAL_ERROR);
4391
  }
4392
}
4393
4394
6104
static void cont_read_expect_closepar(eval_context_t *ctx) {
4395
  lbm_value res;
4396
  lbm_value stream;
4397
4398
6104
  lbm_pop_2(&ctx->K, &res, &stream);
4399
4400
6104
  lbm_char_channel_t *str = lbm_dec_channel(stream);
4401

6104
  if (str == NULL || str->state == NULL) { // TODO: De Morgan these conditions.
4402
    error_ctx(ENC_SYM_FATAL_ERROR);
4403
  } else {
4404
6104
    if (lbm_type_of(ctx->r) == LBM_TYPE_SYMBOL &&
4405
6104
        ctx->r == ENC_SYM_CLOSEPAR) {
4406
6104
      ctx->r = res;
4407
6104
      ctx->app_cont = true;
4408
    } else {
4409
      lbm_channel_reader_close(str);
4410
      lbm_set_error_reason((char*)lbm_error_str_parse_close);
4411
      read_error_ctx(lbm_channel_row(str), lbm_channel_column(str));
4412
    }
4413
  }
4414
6104
}
4415
4416
6104
static void cont_read_dot_terminate(eval_context_t *ctx) {
4417
6104
  lbm_value *sptr = get_stack_ptr(ctx, 3);
4418
4419
6104
  lbm_value last_cell  = sptr[1];
4420
6104
  lbm_value stream = sptr[2];
4421
4422
6104
  lbm_char_channel_t *str = lbm_dec_channel(stream);
4423

6104
  if (str == NULL || str->state == NULL) {
4424
    error_ctx(ENC_SYM_FATAL_ERROR);
4425
6104
  } else if (lbm_type_of(ctx->r) == LBM_TYPE_SYMBOL &&
4426
1680
             (ctx->r == ENC_SYM_CLOSEPAR ||
4427
1680
              ctx->r == ENC_SYM_DOT)) {
4428
    lbm_channel_reader_close(str);
4429
    lbm_set_error_reason((char*)lbm_error_str_parse_dot);
4430
    read_error_ctx(lbm_channel_row(str), lbm_channel_column(str));
4431
6104
  } else if (lbm_is_cons(last_cell)) {
4432
6104
    lbm_set_cdr(last_cell, ctx->r);
4433
6104
    ctx->r = sptr[0]; // first cell
4434
6104
    lbm_value *rptr = stack_reserve(ctx, 3);
4435
6104
    sptr[0] = stream;
4436
6104
    sptr[1] = ctx->r;
4437
6104
    sptr[2] = READ_EXPECT_CLOSEPAR;
4438
6104
    rptr[0] = stream;
4439
6104
    rptr[1] = lbm_enc_u(0);
4440
6104
    rptr[2] = READ_NEXT_TOKEN;
4441
6104
    ctx->app_cont = true;
4442
  } else {
4443
    lbm_channel_reader_close(str);
4444
    lbm_set_error_reason((char*)lbm_error_str_parse_dot);
4445
    read_error_ctx(lbm_channel_row(str), lbm_channel_column(str));
4446
  }
4447
6104
}
4448
4449
330281
static void cont_read_done(eval_context_t *ctx) {
4450
  lbm_value stream;
4451
  lbm_value f_val;
4452
  lbm_value reader_mode;
4453
330281
  lbm_pop_3(&ctx->K, &reader_mode, &stream, &f_val);
4454
4455
330281
  uint32_t flags = lbm_dec_as_u32(f_val);
4456
330281
  ctx->flags &= ~EVAL_CPS_CONTEXT_READER_FLAGS_MASK;
4457
330281
  ctx->flags |= (flags & EVAL_CPS_CONTEXT_READER_FLAGS_MASK);
4458
4459
330281
  lbm_char_channel_t *str = lbm_dec_channel(stream);
4460

330281
  if (str == NULL || str->state == NULL) {
4461
    error_ctx(ENC_SYM_FATAL_ERROR);
4462
  } else {
4463
    // the "else" is there to make INFER understand
4464
    // that this only happens if str is non-null.
4465
    // the "else" is unnecessary though as
4466
    // error_ctx longjmps out.
4467
330281
    lbm_channel_reader_close(str);
4468
330281
    if (lbm_is_symbol(ctx->r)) {
4469
22379
      lbm_uint sym_val = lbm_dec_sym(ctx->r);
4470

22379
      if (sym_val >= TOKENIZER_SYMBOLS_START &&
4471
          sym_val <= TOKENIZER_SYMBOLS_END) {
4472
        read_error_ctx(lbm_channel_row(str), lbm_channel_column(str));
4473
      }
4474
    }
4475
330281
    ctx->row0 = -1;
4476
330281
    ctx->row1 = -1;
4477
330281
    ctx->app_cont = true;
4478
  }
4479
330281
}
4480
4481
41272
static void cont_wrap_result(eval_context_t *ctx) {
4482
  lbm_value cell;
4483
  lbm_value wrapper;
4484
41272
  lbm_pop(&ctx->K, &wrapper);
4485

41272
  WITH_GC(cell, lbm_heap_allocate_list_init(2,
4486
                                            wrapper,
4487
                                            ctx->r));
4488
41272
  ctx->r = cell;
4489
41272
  ctx->app_cont = true;
4490
41272
}
4491
4492
105086201
static void cont_application_start(eval_context_t *ctx) {
4493
4494
  /* sptr[0] = env
4495
   * sptr[1] = args
4496
   * ctx->r  = function
4497
   */
4498
4499
105086201
  if (lbm_is_symbol(ctx->r)) {
4500
77990383
    stack_reserve(ctx,1)[0] = lbm_enc_u(0);
4501
77990383
    cont_application_args(ctx);
4502
27095818
  } else if (lbm_is_cons(ctx->r)) {
4503
27095818
    lbm_uint *sptr = get_stack_ptr(ctx, 2);
4504
27095818
    lbm_value args = (lbm_value)sptr[1];
4505

27095818
    switch (get_car(ctx->r)) {
4506
27089434
    case ENC_SYM_CLOSURE: {
4507
      lbm_value cl[3];
4508
27089434
      extract_n(get_cdr(ctx->r), cl, 3);
4509
27089434
      lbm_value arg_env = (lbm_value)sptr[0];
4510
      lbm_value arg0, arg_rest;
4511
27089434
      get_car_and_cdr(args, &arg0, &arg_rest);
4512
27089434
      sptr[1] = cl[CLO_BODY];
4513
27089434
      bool a_nil = lbm_is_symbol_nil(args);
4514
27089434
      bool p_nil = lbm_is_symbol_nil(cl[CLO_PARAMS]);
4515
27089434
      lbm_value *reserved = stack_reserve(ctx, 4);
4516
4517

27089434
      if (!a_nil && !p_nil) {
4518
26211746
        reserved[0] = cl[CLO_ENV];
4519
26211746
        reserved[1] = cl[CLO_PARAMS];
4520
26211746
        reserved[2] = arg_rest;
4521
26211746
        reserved[3] = CLOSURE_ARGS;
4522
26211746
        ctx->curr_exp = arg0;
4523
26211746
        ctx->curr_env = arg_env;
4524

877688
      } else if (a_nil && p_nil) {
4525
        // No params, No args
4526
317660
        lbm_stack_drop(&ctx->K, 6);
4527
317660
        ctx->curr_exp = cl[CLO_BODY];
4528
317660
        ctx->curr_env = cl[CLO_ENV];
4529
560028
      } else if (p_nil) {
4530
560028
        reserved[1] = get_cdr(args);      // protect cdr(args) from allocate_binding
4531
560028
        ctx->curr_exp = get_car(args);    // protect car(args) from allocate binding
4532
560028
        ctx->curr_env = arg_env;
4533
560028
        lbm_value rest_binder = allocate_binding(ENC_SYM_REST_ARGS, ENC_SYM_NIL, cl[CLO_ENV]);
4534
560028
        reserved[0] = rest_binder;
4535
560028
        reserved[2] = get_car(rest_binder);
4536
560028
        reserved[3] = CLOSURE_ARGS_REST;
4537
      } else {
4538
        lbm_set_error_reason((char*)lbm_error_str_num_args);
4539
        error_at_ctx(ENC_SYM_EERROR, ctx->r);
4540
      }
4541
27089434
    } break;
4542
196
    case ENC_SYM_CONT:{
4543
      /* Continuation created using call-cc.
4544
       * ((SYM_CONT . cont-array) arg0 )
4545
       */
4546
196
      lbm_value c = get_cdr(ctx->r); /* should be the continuation array*/
4547
4548
196
      if (!lbm_is_lisp_array_r(c)) {
4549
        error_ctx(ENC_SYM_FATAL_ERROR);
4550
      }
4551
4552
196
      lbm_uint arg_count = lbm_list_length(args);
4553
196
      lbm_value arg = ENC_SYM_NIL;
4554
      switch (arg_count) {
4555
56
      case 0:
4556
56
        arg = ENC_SYM_NIL;
4557
56
        break;
4558
140
      case 1:
4559
140
        arg = get_car(args);
4560
140
        break;
4561
      default:
4562
        lbm_set_error_reason((char*)lbm_error_str_num_args);
4563
        error_ctx(ENC_SYM_EERROR);
4564
      }
4565
196
      lbm_stack_clear(&ctx->K);
4566
4567
196
      lbm_array_header_t *arr = assume_array(c);
4568
196
      ctx->K.sp = arr->size / sizeof(lbm_uint);
4569
196
      memcpy(ctx->K.data, arr->data, arr->size);
4570
4571
      lbm_value atomic;
4572
196
      lbm_pop(&ctx->K, &atomic);
4573
196
      is_atomic = atomic ? 1 : 0;
4574
4575
196
      ctx->curr_exp = arg;
4576
196
    } break;
4577
    case ENC_SYM_CONT_SP: {
4578
      // continuation created using call-cc-unsafe
4579
      // ((SYM_CONT_SP . stack_ptr) arg0 )
4580
      lbm_value c = get_cadr(ctx->r); /* should be the stack_ptr*/
4581
      lbm_value atomic = get_cadr(get_cdr(ctx->r));
4582
4583
      if (!lbm_is_number(c)) {
4584
        error_ctx(ENC_SYM_FATAL_ERROR);
4585
      }
4586
4587
      lbm_uint sp = (lbm_uint)lbm_dec_i(c);
4588
4589
      lbm_uint arg_count = lbm_list_length(args);
4590
      lbm_value arg = ENC_SYM_NIL;
4591
      switch (arg_count) {
4592
      case 0:
4593
        arg = ENC_SYM_NIL;
4594
        break;
4595
      case 1:
4596
        arg = get_car(args);
4597
        break;
4598
      default:
4599
        lbm_set_error_reason((char*)lbm_error_str_num_args);
4600
        error_ctx(ENC_SYM_EERROR);
4601
      }
4602
      if (sp > 0 && sp <= ctx->K.sp && IS_CONTINUATION(ctx->K.data[sp-1])) {
4603
              is_atomic = atomic ? 1 : 0; // works fine with nil/true
4604
              ctx->K.sp = sp;
4605
              ctx->curr_exp = arg;
4606
              return;
4607
      } else {
4608
        error_ctx(ENC_SYM_FATAL_ERROR);
4609
      }
4610
    } break;
4611
6188
    case ENC_SYM_MACRO:{
4612
      /*
4613
       * Perform macro expansion.
4614
       * Macro expansion is really just evaluation in an
4615
       * environment augmented with the unevaluated expressions passed
4616
       * as arguments.
4617
       */
4618
6188
      lbm_value env = (lbm_value)sptr[0];
4619
4620
6188
      lbm_value curr_param = get_cadr(ctx->r);
4621
6188
      lbm_value curr_arg = args;
4622
6188
      lbm_value expand_env = env;
4623

43316
      while (lbm_is_cons(curr_param) &&
4624
18564
             lbm_is_cons(curr_arg)) {
4625
18564
        lbm_cons_t *param_cell = lbm_ref_cell(curr_param); // already checked that cons.
4626
18564
        lbm_cons_t *arg_cell = lbm_ref_cell(curr_arg);
4627
18564
        lbm_value car_curr_param = param_cell->car;
4628
18564
        lbm_value cdr_curr_param = param_cell->cdr;
4629
18564
        lbm_value car_curr_arg = arg_cell->car;
4630
18564
        lbm_value cdr_curr_arg = arg_cell->cdr;
4631
4632
18564
        lbm_value entry = cons_with_gc(car_curr_param, car_curr_arg, expand_env);
4633
18564
        lbm_value aug_env = cons_with_gc(entry, expand_env,ENC_SYM_NIL);
4634
18564
        expand_env = aug_env;
4635
4636
18564
        curr_param = cdr_curr_param;
4637
18564
        curr_arg   = cdr_curr_arg;
4638
      }
4639
      /* Two rounds of evaluation is performed.
4640
       * First to instantiate the arguments into the macro body.
4641
       * Second to evaluate the resulting program.
4642
       */
4643
6188
      sptr[1] = EVAL_R;
4644
6188
      lbm_value exp = get_cadr(get_cdr(ctx->r));
4645
6188
      ctx->curr_exp = exp;
4646
6188
      ctx->curr_env = expand_env;
4647
6188
    } break;
4648
    default:
4649
      error_ctx(ENC_SYM_EERROR);
4650
    }
4651
  } else {
4652
    error_ctx(ENC_SYM_EERROR);
4653
  }
4654
}
4655
4656
6188
static void cont_eval_r(eval_context_t* ctx) {
4657
  lbm_value env;
4658
6188
  lbm_pop(&ctx->K, &env);
4659
6188
  ctx->curr_exp = ctx->r;
4660
6188
  ctx->curr_env = env;
4661
6188
}
4662
4663
643734
static void cont_progn_var(eval_context_t* ctx) {
4664
4665
  lbm_value key;
4666
  lbm_value env;
4667
4668
643734
  lbm_pop_2(&ctx->K, &key, &env);
4669
4670
643734
  if (fill_binding_location(key, ctx->r, env) < 0) {
4671
    lbm_set_error_reason("Incorrect type of name/key in let-binding");
4672
    error_at_ctx(ENC_SYM_TERROR, key);
4673
  }
4674
4675
643734
  ctx->app_cont = true;
4676
643734
}
4677
4678
1775536
static void cont_setq(eval_context_t *ctx) {
4679
  lbm_value sym;
4680
  lbm_value env;
4681
1775536
  lbm_pop_2(&ctx->K, &sym, &env);
4682
  lbm_value res;
4683

1775536
  WITH_GC(res, perform_setvar(sym, ctx->r, env));
4684
1775480
  ctx->r = res;
4685
1775480
  ctx->app_cont = true;
4686
1775480
}
4687
4688
2408
lbm_flash_status request_flash_storage_cell(lbm_value val, lbm_value *res) {
4689
4690
  lbm_value flash_cell;
4691
2408
  lbm_flash_status s = lbm_allocate_const_cell(&flash_cell);
4692
2408
  if (s != LBM_FLASH_WRITE_OK)
4693
    return s;
4694
2408
  lbm_value new_val = val;
4695
2408
  new_val &= ~LBM_PTR_VAL_MASK; // clear the value part of the ptr
4696
2408
  new_val |= (flash_cell & LBM_PTR_VAL_MASK);
4697
2408
  new_val |= LBM_PTR_TO_CONSTANT_BIT;
4698
2408
  *res = new_val;
4699
2408
  return s;
4700
}
4701
4702
840
static void cont_move_to_flash(eval_context_t *ctx) {
4703
4704
  lbm_value args;
4705
840
  lbm_pop(&ctx->K, &args);
4706
4707
840
  if (lbm_is_symbol_nil(args)) {
4708
    // Done looping over arguments. return true.
4709
364
    ctx->r = ENC_SYM_TRUE;
4710
364
    ctx->app_cont = true;
4711
840
    return;
4712
  }
4713
4714
  lbm_value first_arg, rest;
4715
476
  get_car_and_cdr(args, &first_arg, &rest);
4716
4717
  lbm_value val;
4718

476
  if (lbm_is_symbol(first_arg) && lbm_global_env_lookup(&val, first_arg)) {
4719
    // Prepare to copy the rest of the arguments when done with first.
4720
476
    lbm_value *rptr = stack_reserve(ctx, 2);
4721
476
    rptr[0] = rest;
4722
476
    rptr[1] = MOVE_TO_FLASH;
4723
476
    if (lbm_is_ptr(val) &&
4724
476
        (!(val & LBM_PTR_TO_CONSTANT_BIT))) {
4725
476
      lbm_value * rptr1 = stack_reserve(ctx, 3);
4726
476
      rptr1[0] = first_arg;
4727
476
      rptr1[1] = SET_GLOBAL_ENV;
4728
476
      rptr1[2] = MOVE_VAL_TO_FLASH_DISPATCH;
4729
476
      ctx->r = val;
4730
    }
4731
476
    ctx->app_cont = true;
4732
476
    return;
4733
  }
4734
  error_ctx(ENC_SYM_EERROR);
4735
}
4736
4737
3388
static void cont_move_val_to_flash_dispatch(eval_context_t *ctx) {
4738
4739
3388
  lbm_value val = ctx->r;
4740
4741
3388
  if (lbm_is_cons(val)) { // non-constant cons-cell
4742
798
    lbm_value *rptr = stack_reserve(ctx, 5);
4743
798
    rptr[0] = ENC_SYM_NIL; // fst cell of list
4744
798
    rptr[1] = ENC_SYM_NIL; // last cell of list
4745
798
    rptr[2] = get_cdr(val);
4746
798
    rptr[3] = MOVE_LIST_TO_FLASH;
4747
798
    rptr[4] = MOVE_VAL_TO_FLASH_DISPATCH;
4748
798
    ctx->r = get_car(val);
4749
798
    ctx->app_cont = true;
4750
798
    return;
4751
  }
4752
4753

2590
  if (lbm_is_ptr(val) && (val & LBM_PTR_TO_CONSTANT_BIT)) { // constant pointer cons or not.
4754
    //ctx->r unchanged
4755
    ctx->app_cont = true;
4756
    return;
4757
  }
4758
4759
2590
  if (lbm_is_ptr(val)) { // something that is not a cons but still a ptr type.
4760
280
    lbm_cons_t *ref = lbm_ref_cell(val);
4761
280
    if (lbm_type_of(ref->cdr) == LBM_TYPE_SYMBOL) {
4762

280
      switch (ref->cdr) {
4763
140
      case ENC_SYM_RAW_I_TYPE: /* fall through */
4764
      case ENC_SYM_RAW_U_TYPE:
4765
      case ENC_SYM_RAW_F_TYPE: {
4766
140
        lbm_value flash_cell = ENC_SYM_NIL;
4767
140
        handle_flash_status(request_flash_storage_cell(val, &flash_cell));
4768
140
        handle_flash_status(write_const_car(flash_cell, ref->car));
4769
140
        handle_flash_status(write_const_cdr(flash_cell, ref->cdr));
4770
140
        ctx->r = flash_cell;
4771
140
      } break;
4772
56
      case ENC_SYM_IND_I_TYPE: /* fall through */
4773
      case ENC_SYM_IND_U_TYPE:
4774
      case ENC_SYM_IND_F_TYPE: {
4775
#ifndef LBM64
4776
        /* 64 bit values are in lbm mem on 32bit platforms. */
4777
56
        lbm_uint *lbm_mem_ptr = (lbm_uint*)ref->car;
4778
        lbm_uint flash_ptr;
4779
4780
56
        handle_flash_status(lbm_write_const_raw(lbm_mem_ptr, 2, &flash_ptr));
4781
56
        lbm_value flash_cell = ENC_SYM_NIL;
4782
56
        handle_flash_status(request_flash_storage_cell(val, &flash_cell));
4783
56
        handle_flash_status(write_const_car(flash_cell, flash_ptr));
4784
56
        handle_flash_status(write_const_cdr(flash_cell, ref->cdr));
4785
56
        ctx->r = flash_cell;
4786
#else
4787
        // There are no indirect types in LBM64
4788
        error_ctx(ENC_SYM_FATAL_ERROR);
4789
#endif
4790
56
      } break;
4791
28
      case ENC_SYM_LISPARRAY_TYPE: {
4792
28
        lbm_array_header_t *arr = (lbm_array_header_t*)ref->car;
4793
28
        lbm_uint size = arr->size / sizeof(lbm_uint);
4794
28
        lbm_uint flash_addr = 0;
4795
28
        lbm_value *arrdata = (lbm_value *)arr->data;
4796
28
        lbm_value flash_cell = ENC_SYM_NIL;
4797
28
        handle_flash_status(request_flash_storage_cell(val, &flash_cell));
4798
28
        handle_flash_status(lbm_allocate_const_raw(size, &flash_addr));
4799
28
        lift_array_flash(flash_cell,
4800
                         false,
4801
                         (char *)flash_addr,
4802
                         arr->size);
4803
        // Move array contents to flash recursively
4804
28
        lbm_value *rptr = stack_reserve(ctx, 5);
4805
28
        rptr[0] = flash_cell;
4806
28
        rptr[1] = lbm_enc_u(0);
4807
28
        rptr[2] = val;
4808
28
        rptr[3] = MOVE_ARRAY_ELTS_TO_FLASH;
4809
28
        rptr[4] = MOVE_VAL_TO_FLASH_DISPATCH;
4810
28
        ctx->r = arrdata[0];
4811
28
        ctx->app_cont = true;
4812
28
        return;
4813
      }
4814
56
      case ENC_SYM_ARRAY_TYPE: {
4815
56
        lbm_array_header_t *arr = (lbm_array_header_t*)ref->car;
4816
        // arbitrary address: flash_arr.
4817
56
        lbm_uint flash_arr = 0;
4818
56
        handle_flash_status(lbm_write_const_array_padded((uint8_t*)arr->data, arr->size, &flash_arr));
4819
56
        lbm_value flash_cell = ENC_SYM_NIL;
4820
56
        handle_flash_status(request_flash_storage_cell(val, &flash_cell));
4821
56
        lift_array_flash(flash_cell,
4822
                         true,
4823
                         (char *)flash_arr,
4824
                         arr->size);
4825
56
        ctx->r = flash_cell;
4826
56
      } break;
4827
      case ENC_SYM_CHANNEL_TYPE: /* fall through */
4828
      case ENC_SYM_CUSTOM_TYPE:
4829
        lbm_set_error_reason((char *)lbm_error_str_flash_not_possible);
4830
        error_ctx(ENC_SYM_EERROR);
4831
      }
4832
252
    } else {
4833
      error_ctx(ENC_SYM_FATAL_ERROR);
4834
    }
4835
252
    ctx->app_cont = true;
4836
252
    return;
4837
  }
4838
4839
  // if no condition matches, nothing happens (id).
4840
2310
  ctx->r = val;
4841
2310
  ctx->app_cont = true;
4842
}
4843
4844
2016
static void cont_move_list_to_flash(eval_context_t *ctx) {
4845
4846
  // ctx->r holds the value that should go in car
4847
4848
2016
  lbm_value *sptr = get_stack_ptr(ctx, 3);
4849
4850
2016
  lbm_value fst = sptr[0];
4851
2016
  lbm_value lst = sptr[1];
4852
2016
  lbm_value val = sptr[2];
4853
4854
4855
2016
  lbm_value new_lst = ENC_SYM_NIL;
4856
  // Allocate element ptr storage after storing the element to flash.
4857
2016
  handle_flash_status(request_flash_storage_cell(lbm_enc_cons_ptr(LBM_PTR_NULL), &new_lst));
4858
4859
2016
  if (lbm_is_symbol_nil(fst)) {
4860
798
    lst = new_lst;
4861
798
    fst = new_lst;
4862
798
    handle_flash_status(write_const_car(lst, ctx->r));
4863
  } else {
4864
1218
    handle_flash_status(write_const_cdr(lst, new_lst)); // low before high
4865
1218
    handle_flash_status(write_const_car(new_lst, ctx->r));
4866
1218
    lst = new_lst;
4867
  }
4868
4869
2016
  if (lbm_is_cons(val)) {
4870
1218
    sptr[0] = fst;
4871
1218
    sptr[1] = lst;//rest_cell;
4872
1218
    sptr[2] = get_cdr(val);
4873
1218
    lbm_value *rptr = stack_reserve(ctx, 2);
4874
1218
    rptr[0] = MOVE_LIST_TO_FLASH;
4875
1218
    rptr[1] = MOVE_VAL_TO_FLASH_DISPATCH;
4876
1218
    ctx->r = get_car(val);
4877
  } else {
4878
798
    sptr[0] = fst;
4879
798
    sptr[1] = lst;
4880
798
    sptr[2] = CLOSE_LIST_IN_FLASH;
4881
798
    stack_reserve(ctx, 1)[0] = MOVE_VAL_TO_FLASH_DISPATCH;
4882
798
    ctx->r =  val;
4883
  }
4884
2016
  ctx->app_cont = true;
4885
2016
}
4886
4887
798
static void cont_close_list_in_flash(eval_context_t *ctx) {
4888
  lbm_value fst;
4889
  lbm_value lst;
4890
798
  lbm_pop_2(&ctx->K, &lst, &fst);
4891
798
  lbm_value val = ctx->r;
4892
798
  handle_flash_status(write_const_cdr(lst, val));
4893
798
  ctx->r = fst;
4894
798
  ctx->app_cont = true;
4895
798
}
4896
4897
84
static void cont_move_array_elts_to_flash(eval_context_t *ctx) {
4898
84
  lbm_value *sptr = get_stack_ptr(ctx, 3);
4899
  // sptr[2] = source array in RAM
4900
  // sptr[1] = current index
4901
  // sptr[0] = target array in flash
4902
84
  lbm_array_header_t *src_arr = assume_array(sptr[2]);
4903
84
  lbm_uint size = src_arr->size / sizeof(lbm_uint);
4904
84
  lbm_value *srcdata = (lbm_value *)src_arr->data;
4905
4906
84
  lbm_array_header_t *tgt_arr = assume_array(sptr[0]);
4907
84
  lbm_uint *tgtdata = (lbm_value *)tgt_arr->data;
4908
84
  lbm_uint ix = lbm_dec_as_u32(sptr[1]);
4909
84
  handle_flash_status(lbm_const_write(&tgtdata[ix], ctx->r));
4910
84
  if (ix >= size-1) {
4911
28
    ctx->r = sptr[0];
4912
28
    lbm_stack_drop(&ctx->K, 3);
4913
28
    ctx->app_cont = true;
4914
28
    return;
4915
  }
4916
56
  sptr[1] = lbm_enc_u(ix + 1);
4917
56
  lbm_value *rptr = stack_reserve(ctx, 2);
4918
56
  rptr[0] = MOVE_ARRAY_ELTS_TO_FLASH;
4919
56
  rptr[1] = MOVE_VAL_TO_FLASH_DISPATCH;
4920
56
  ctx->r = srcdata[ix+1];
4921
56
  ctx->app_cont = true;
4922
56
  return;
4923
}
4924
4925
5068
static void cont_qq_expand_start(eval_context_t *ctx) {
4926
5068
  lbm_value *rptr = stack_reserve(ctx, 2);
4927
5068
  rptr[0] = ctx->r;
4928
5068
  rptr[1] = QQ_EXPAND;
4929
5068
  ctx->r = ENC_SYM_NIL;
4930
5068
  ctx->app_cont = true;
4931
5068
}
4932
4933
10276
lbm_value quote_it(lbm_value qquoted) {
4934

20104
  if (lbm_is_symbol(qquoted) &&
4935
19656
      lbm_is_special(qquoted)) return qquoted;
4936
4937
448
  lbm_value val = cons_with_gc(qquoted, ENC_SYM_NIL, ENC_SYM_NIL);
4938
448
  return cons_with_gc(ENC_SYM_QUOTE, val, ENC_SYM_NIL);
4939
}
4940
4941
37968
bool is_append(lbm_value a) {
4942
75768
  return (lbm_is_cons(a) &&
4943

75768
          lbm_is_symbol(get_car(a)) &&
4944
37800
          (get_car(a) == ENC_SYM_APPEND));
4945
}
4946
4947
63840
lbm_value append(lbm_value front, lbm_value back) {
4948
63840
  if (lbm_is_symbol_nil(front)) return back;
4949
29456
  if (lbm_is_symbol_nil(back)) return front;
4950
4951

30016
  if (lbm_is_quoted_list(front) &&
4952
10388
      lbm_is_quoted_list(back)) {
4953
448
    lbm_value f = get_cadr(front);
4954
448
    lbm_value b = get_cadr(back);
4955
448
    return quote_it(lbm_list_append(f, b));
4956
  }
4957
4958

28728
  if (is_append(back) &&
4959
9940
      lbm_is_quoted_list(get_cadr(back)) &&
4960
392
       lbm_is_quoted_list(front)) {
4961
392
    lbm_value ql = get_cadr(back);
4962
392
    lbm_value f = get_cadr(front);
4963
392
    lbm_value b = get_cadr(ql);
4964
4965
392
    lbm_value v = lbm_list_append(f, b);
4966
392
    lbm_set_car(get_cdr(ql), v);
4967
392
    return back;
4968
  }
4969
4970
18788
  if (is_append(back)) {
4971
9156
    back  = get_cdr(back);
4972
9156
    lbm_value new = cons_with_gc(front, back, ENC_SYM_NIL);
4973
9156
    return cons_with_gc(ENC_SYM_APPEND, new, ENC_SYM_NIL);
4974
  }
4975
4976
  lbm_value t0, t1;
4977
4978
9632
  t0 = cons_with_gc(back, ENC_SYM_NIL, front);
4979
9632
  t1 = cons_with_gc(front, t0, ENC_SYM_NIL);
4980
9632
  return cons_with_gc(ENC_SYM_APPEND, t1, ENC_SYM_NIL);
4981
}
4982
4983
// ////////////////////////////////////////////////////////////
4984
// Quasiquotation expansion that takes place at read time
4985
// and is based on the paper by Bawden "Quasiquotation in lisp".
4986
// Bawden, Alan. "Quasiquotation in Lisp." PEPM. 1999.
4987
//
4988
// cont_qq_expand and cont_qq_expand_list corresponds (mostly) to
4989
// qq-expand and qq-expand-list in the paper.
4990
// One difference is that the case where a backquote is nested
4991
// inside of a backqoute is handled via the recursion through the
4992
// reader.
4993
4994
/* Bawden's qq-expand implementation
4995
(define (qq-expand x)
4996
  (cond ((tag-comma? x)
4997
         (tag-data x))
4998
        ((tag-comma-atsign? x)
4999
         (error "Illegal"))
5000
        ((tag-backquote? x)
5001
         (qq-expand
5002
          (qq-expand (tag-data x))))
5003
        ((pair? x)
5004
         `(append
5005
           ,(qq-expand-list (car x))
5006
           ,(qq-expand (cdr x))))
5007
        (else `',x)))
5008
 */
5009
34524
static void cont_qq_expand(eval_context_t *ctx) {
5010
  lbm_value qquoted;
5011
34524
  lbm_pop(&ctx->K, &qquoted);
5012
5013
34524
  switch(lbm_type_of(qquoted)) {
5014
24696
  case LBM_TYPE_CONS: {
5015
24696
    lbm_value car_val = get_car(qquoted);
5016
24696
    lbm_value cdr_val = get_cdr(qquoted);
5017

24696
    if (lbm_type_of(car_val) == LBM_TYPE_SYMBOL &&
5018
        car_val == ENC_SYM_COMMA) {
5019
28
      ctx->r = append(ctx->r, get_car(cdr_val));
5020
28
      ctx->app_cont = true;
5021

24668
    } else if (lbm_type_of(car_val) == LBM_TYPE_SYMBOL &&
5022
               car_val == ENC_SYM_COMMAAT) {
5023
      lbm_set_error_reason((char*)lbm_error_str_qq_expand);
5024
      error_at_ctx(ENC_SYM_RERROR, qquoted);
5025
    } else {
5026
24668
      lbm_value *rptr = stack_reserve(ctx, 6);
5027
24668
      rptr[0] = ctx->r;
5028
24668
      rptr[1] = QQ_APPEND;
5029
24668
      rptr[2] = cdr_val;
5030
24668
      rptr[3] = QQ_EXPAND;
5031
24668
      rptr[4] = car_val;
5032
24668
      rptr[5] = QQ_EXPAND_LIST;
5033
24668
      ctx->app_cont = true;
5034
24668
      ctx->r = ENC_SYM_NIL;
5035
    }
5036
5037
24696
  } break;
5038
9828
  default: {
5039
9828
    lbm_value res = quote_it(qquoted);
5040
9828
    ctx->r = append(ctx->r, res);
5041
9828
    ctx->app_cont = true;
5042
  }
5043
  }
5044
34524
}
5045
5046
29456
static void cont_qq_append(eval_context_t *ctx) {
5047
  lbm_value head;
5048
29456
  lbm_pop(&ctx->K, &head);
5049
29456
  ctx->r = append(head, ctx->r);
5050
29456
  ctx->app_cont = true;
5051
29456
}
5052
5053
/* Bawden's qq-expand-list implementation
5054
(define (qq-expand-list x)
5055
  (cond ((tag-comma? x)
5056
         `(list ,(tag-data x)))
5057
        ((tag-comma-atsign? x)
5058
         (tag-data x))
5059
        ((tag-backquote? x)
5060
         (qq-expand-list
5061
          (qq-expand (tag-data x))))
5062
        ((pair? x)
5063
         `(list
5064
           (append
5065
            ,(qq-expand-list (car x))
5066
            ,(qq-expand (cdr x)))))
5067
        (else `'(,x))))
5068
*/
5069
5070
29456
static void cont_qq_expand_list(eval_context_t* ctx) {
5071
  lbm_value l;
5072
29456
  lbm_pop(&ctx->K, &l);
5073
5074
29456
  ctx->app_cont = true;
5075
29456
  switch(lbm_type_of(l)) {
5076
18844
  case LBM_TYPE_CONS: {
5077
18844
    lbm_value car_val = get_car(l);
5078
18844
    lbm_value cdr_val = get_cdr(l);
5079

18844
    if (lbm_type_of(car_val) == LBM_TYPE_SYMBOL &&
5080
        car_val == ENC_SYM_COMMA) {
5081
13916
      lbm_value tl = cons_with_gc(get_car(cdr_val), ENC_SYM_NIL, ENC_SYM_NIL);
5082
13916
      lbm_value tmp = cons_with_gc(ENC_SYM_LIST, tl, ENC_SYM_NIL);
5083
13916
      ctx->r = append(ctx->r, tmp);
5084
14056
      return;
5085

4928
    } else if (lbm_type_of(car_val) == LBM_TYPE_SYMBOL &&
5086
               car_val == ENC_SYM_COMMAAT) {
5087
140
      lbm_value cadr_val = lbm_car(cdr_val);
5088
140
      ctx->r = cadr_val;
5089
140
      return;
5090
    } else {
5091
4788
      lbm_value *rptr = stack_reserve(ctx, 7);
5092
4788
      rptr[0] = QQ_LIST;
5093
4788
      rptr[1] = ctx->r;
5094
4788
      rptr[2] = QQ_APPEND;
5095
4788
      rptr[3] = cdr_val;
5096
4788
      rptr[4] = QQ_EXPAND;
5097
4788
      rptr[5] = car_val;
5098
4788
      rptr[6] = QQ_EXPAND_LIST;
5099
4788
      ctx->r = ENC_SYM_NIL;
5100
    }
5101
5102
4788
  } break;
5103
10612
  default: {
5104
10612
    lbm_value a_list = cons_with_gc(l, ENC_SYM_NIL, ENC_SYM_NIL);
5105
10612
    lbm_value tl = cons_with_gc(a_list, ENC_SYM_NIL, ENC_SYM_NIL);
5106
10612
    lbm_value tmp = cons_with_gc(ENC_SYM_QUOTE, tl, ENC_SYM_NIL);
5107
10612
    ctx->r = append(ctx->r, tmp);
5108
  }
5109
  }
5110
}
5111
5112
4788
static void cont_qq_list(eval_context_t *ctx) {
5113
4788
  lbm_value val = ctx->r;
5114
4788
  lbm_value apnd_app = cons_with_gc(val, ENC_SYM_NIL, ENC_SYM_NIL);
5115
4788
  lbm_value tmp = cons_with_gc(ENC_SYM_LIST, apnd_app, ENC_SYM_NIL);
5116
4788
  ctx->r = tmp;
5117
4788
  ctx->app_cont = true;
5118
4788
}
5119
5120
84
static void cont_kill(eval_context_t *ctx) {
5121
  (void) ctx;
5122
84
  ok_ctx();
5123
84
}
5124
5125
69716
static void cont_pop_reader_flags(eval_context_t *ctx) {
5126
  lbm_value flags;
5127
69716
  lbm_pop(&ctx->K, &flags);
5128
69716
  ctx->flags = ctx->flags & ~EVAL_CPS_CONTEXT_READER_FLAGS_MASK;
5129
69716
  ctx->flags |= (lbm_dec_as_u32(flags) & EVAL_CPS_CONTEXT_READER_FLAGS_MASK);
5130
  // r is unchanged.
5131
69716
  ctx->app_cont = true;
5132
69716
}
5133
5134
8120
static void cont_exception_handler(eval_context_t *ctx) {
5135
8120
  lbm_value *sptr = pop_stack_ptr(ctx, 2);
5136
8120
  lbm_value retval = sptr[0];
5137
8120
  lbm_value flags = sptr[1];
5138
8120
  lbm_set_car(get_cdr(retval), ctx->r);
5139
8120
  ctx->flags = (uint32_t)flags;
5140
8120
  ctx->r = retval;
5141
8120
  ctx->app_cont = true;
5142
8120
}
5143
5144
// cont_recv_to:
5145
//
5146
// s[sp-1] = patterns
5147
//
5148
// ctx->r = timeout value
5149
196
static void cont_recv_to(eval_context_t *ctx) {
5150
196
  if (lbm_is_number(ctx->r)) {
5151
196
    lbm_value *sptr = get_stack_ptr(ctx, 1); // patterns at sptr[0]
5152
196
    float timeout_time = lbm_dec_as_float(ctx->r);
5153
196
    if (timeout_time < 0.0) timeout_time = 0.0; // clamp.
5154
196
    if (ctx->num_mail > 0) {
5155
      lbm_value e;
5156
56
      lbm_value new_env = ctx->curr_env;
5157
#ifdef LBM_ALWAYS_GC
5158
      gc();
5159
#endif
5160
56
      int n = find_match(sptr[0], ctx->mailbox, ctx->num_mail, &e, &new_env);
5161
56
      if (n == FM_NEED_GC) {
5162
        gc();
5163
        new_env = ctx->curr_env;
5164
        n = find_match(sptr[0], ctx->mailbox, ctx->num_mail, &e, &new_env);
5165
        if (n == FM_NEED_GC) error_ctx(ENC_SYM_MERROR);
5166
      }
5167
56
      if (n == FM_PATTERN_ERROR) {
5168
        lbm_set_error_reason("Incorrect pattern format for recv");
5169
        error_at_ctx(ENC_SYM_EERROR, sptr[0]);
5170
56
      } else if (n >= 0) { // match
5171
56
        mailbox_remove_mail(ctx, (lbm_uint)n);
5172
56
        ctx->curr_env = new_env;
5173
56
        ctx->curr_exp = e;
5174
56
        lbm_stack_drop(&ctx->K, 1);
5175
56
        return;
5176
      }
5177
    }
5178
    // If no mail or no match, go to sleep
5179
140
    lbm_uint *rptr = stack_reserve(ctx,2);
5180
140
    rptr[0] = ctx->r;
5181
140
    rptr[1] = RECV_TO_RETRY;
5182
140
    block_current_ctx(LBM_THREAD_STATE_RECV_TO,S_TO_US(timeout_time),true);
5183
  } else {
5184
    error_ctx(ENC_SYM_TERROR);
5185
  }
5186
}
5187
5188
// cont_recv_to_retry
5189
//
5190
// s[sp-2] = patterns
5191
// s[sp-1] = timeout value
5192
//
5193
// ctx->r = nonsense | timeout symbol
5194
140
static void cont_recv_to_retry(eval_context_t *ctx) {
5195
140
  lbm_value *sptr = get_stack_ptr(ctx, 2); //sptr[0] = patterns, sptr[1] = timeout
5196
5197
140
  if (ctx->num_mail > 0) {
5198
    lbm_value e;
5199
140
    lbm_value new_env = ctx->curr_env;
5200
#ifdef LBM_ALWAYS_GC
5201
    gc();
5202
#endif
5203
140
    int n = find_match(sptr[0], ctx->mailbox, ctx->num_mail, &e, &new_env);
5204
140
    if (n == FM_NEED_GC) {
5205
      gc();
5206
      new_env = ctx->curr_env;
5207
      n = find_match(sptr[0], ctx->mailbox, ctx->num_mail, &e, &new_env);
5208
      if (n == FM_NEED_GC) error_ctx(ENC_SYM_MERROR);
5209
    }
5210
140
    if (n == FM_PATTERN_ERROR) {
5211
      lbm_set_error_reason("Incorrect pattern format for recv");
5212
      error_at_ctx(ENC_SYM_EERROR, sptr[0]);
5213
140
    } else if (n >= 0) { // match
5214
56
      mailbox_remove_mail(ctx, (lbm_uint)n);
5215
56
      ctx->curr_env = new_env;
5216
56
      ctx->curr_exp = e;
5217
56
      lbm_stack_drop(&ctx->K, 2);
5218
56
      return;
5219
    }
5220
  }
5221
5222
  // No message matched but the timeout was reached.
5223
  // This is like having a recv-to with no case that matches
5224
  // the timeout symbol.
5225
84
  if (ctx->r == ENC_SYM_TIMEOUT) {
5226
84
    lbm_stack_drop(&ctx->K, 2);
5227
84
    ctx->app_cont = true;
5228
84
    return;
5229
  }
5230
5231
  stack_reserve(ctx,1)[0] = RECV_TO_RETRY;
5232
  reblock_current_ctx(LBM_THREAD_STATE_RECV_TO,true);
5233
}
5234
5235
5236
/*********************************************************/
5237
/* Continuations table                                   */
5238
typedef void (*cont_fun)(eval_context_t *);
5239
5240
static const cont_fun continuations[NUM_CONTINUATIONS] =
5241
  { advance_ctx,  // CONT_DONE
5242
    cont_set_global_env,
5243
    cont_bind_to_key_rest,
5244
    cont_if,
5245
    cont_progn_rest,
5246
    cont_application_args,
5247
    cont_and,
5248
    cont_or,
5249
    cont_wait,
5250
    cont_match,
5251
    cont_application_start,
5252
    cont_eval_r,
5253
    cont_resume,
5254
    cont_closure_application_args,
5255
    cont_exit_atomic,
5256
    cont_read_next_token,
5257
    cont_read_append_continue,
5258
    cont_read_eval_continue,
5259
    cont_read_expect_closepar,
5260
    cont_read_dot_terminate,
5261
    cont_read_done,
5262
    cont_read_start_array,
5263
    cont_read_append_array,
5264
    cont_map,
5265
    cont_match_guard,
5266
    cont_terminate,
5267
    cont_progn_var,
5268
    cont_setq,
5269
    cont_move_to_flash,
5270
    cont_move_val_to_flash_dispatch,
5271
    cont_move_list_to_flash,
5272
    cont_close_list_in_flash,
5273
    cont_qq_expand_start,
5274
    cont_qq_expand,
5275
    cont_qq_append,
5276
    cont_qq_expand_list,
5277
    cont_qq_list,
5278
    cont_kill,
5279
    cont_loop,
5280
    cont_loop_condition,
5281
    cont_merge_rest,
5282
    cont_merge_layer,
5283
    cont_closure_args_rest,
5284
    cont_move_array_elts_to_flash,
5285
    cont_pop_reader_flags,
5286
    cont_exception_handler,
5287
    cont_recv_to,
5288
    cont_wrap_result,
5289
    cont_recv_to_retry,
5290
  };
5291
5292
/*********************************************************/
5293
/* Evaluators lookup table (special forms)               */
5294
typedef void (*evaluator_fun)(eval_context_t *);
5295
5296
static const evaluator_fun evaluators[] =
5297
  {
5298
   eval_quote,
5299
   eval_define,
5300
   eval_progn,
5301
   eval_lambda,
5302
   eval_if,
5303
   eval_let,
5304
   eval_and,
5305
   eval_or,
5306
   eval_match,
5307
   eval_receive,
5308
   eval_receive_timeout,
5309
   eval_callcc,
5310
   eval_atomic,
5311
   eval_selfevaluating, // macro
5312
   eval_selfevaluating, // cont
5313
   eval_selfevaluating, // closure
5314
   eval_cond,
5315
   eval_app_cont,
5316
   eval_var,
5317
   eval_setq,
5318
   eval_move_to_flash,
5319
   eval_loop,
5320
   eval_trap,
5321
   eval_call_cc_unsafe,
5322
   eval_selfevaluating, // cont_sp
5323
  };
5324
5325
5326
/*********************************************************/
5327
/* Evaluator step function                               */
5328
5329
912105056
static void evaluation_step(void){
5330
912105056
  eval_context_t *ctx = ctx_running;
5331
#ifdef VISUALIZE_HEAP
5332
  heap_vis_gen_image();
5333
#endif
5334
5335
912105056
  if (ctx->app_cont) {
5336
    lbm_value k;
5337
424259771
    lbm_pop(&ctx->K, &k);
5338
424259771
    ctx->app_cont = false;
5339
5340
424259771
    lbm_uint decoded_k = DEC_CONTINUATION(k);
5341
    // If app_cont is true, then top of stack must be a valid continuation!
5342
424259771
    if (decoded_k < NUM_CONTINUATIONS) {
5343
424259771
      continuations[decoded_k](ctx);
5344
    } else {
5345
      error_ctx(ENC_SYM_FATAL_ERROR);
5346
    }
5347
424251783
    return;
5348
  }
5349
5350
487845285
  if (lbm_is_symbol(ctx->curr_exp)) {
5351
224402016
    eval_symbol(ctx);
5352
224401932
    return;
5353
  }
5354
263443269
  if (lbm_is_cons(ctx->curr_exp)) {
5355
168914971
    lbm_cons_t *cell = lbm_ref_cell(ctx->curr_exp);
5356
168914971
    lbm_value h = cell->car;
5357

168914971
    if (lbm_is_symbol(h) && ((h & ENC_SPECIAL_FORMS_MASK) == ENC_SPECIAL_FORMS_BIT)) {
5358
63829050
      lbm_uint eval_index = lbm_dec_sym(h) & SPECIAL_FORMS_INDEX_MASK;
5359
63829050
      evaluators[eval_index](ctx);
5360
63828966
      return;
5361
    }
5362
    /*
5363
     * At this point head can be anything. It should evaluate
5364
     * into a form that can be applied (closure, symbol, ...) though.
5365
     */
5366
105085921
    lbm_value *reserved = stack_reserve(ctx, 3);
5367
105085921
    reserved[0] = ctx->curr_env; // INFER: stack_reserve aborts context if error.
5368
105085921
    reserved[1] = cell->cdr;
5369
105085921
    reserved[2] = APPLICATION_START;
5370
105085921
    ctx->curr_exp = h; // evaluate the function
5371
105085921
    return;
5372
  }
5373
5374
94528298
  eval_selfevaluating(ctx);
5375
94528298
  return;
5376
}
5377
5378
5379
// Reset has a built in pause.
5380
// so after reset, continue.
5381
void lbm_reset_eval(void) {
5382
  eval_cps_next_state_arg = 0;
5383
  eval_cps_next_state = EVAL_CPS_STATE_RESET;
5384
  if (eval_cps_next_state != eval_cps_run_state) eval_cps_state_changed = true;
5385
}
5386
5387
21580
void lbm_pause_eval(void ) {
5388
21580
  eval_cps_next_state_arg = 0;
5389
21580
  eval_cps_next_state = EVAL_CPS_STATE_PAUSED;
5390
21580
  if (eval_cps_next_state != eval_cps_run_state) eval_cps_state_changed = true;
5391
21580
}
5392
5393
21588
void lbm_pause_eval_with_gc(uint32_t num_free) {
5394
21588
  eval_cps_next_state_arg = num_free;
5395
21588
  eval_cps_next_state = EVAL_CPS_STATE_PAUSED;
5396
21588
  if (eval_cps_next_state != eval_cps_run_state) eval_cps_state_changed = true;
5397
21588
}
5398
5399
21588
void lbm_continue_eval(void) {
5400
21588
  eval_cps_next_state = EVAL_CPS_STATE_RUNNING;
5401
21588
  if (eval_cps_next_state != eval_cps_run_state) eval_cps_state_changed = true;
5402
21588
}
5403
5404
void lbm_kill_eval(void) {
5405
  eval_cps_next_state = EVAL_CPS_STATE_KILL;
5406
  if (eval_cps_next_state != eval_cps_run_state) eval_cps_state_changed = true;
5407
}
5408
5409
146089
uint32_t lbm_get_eval_state(void) {
5410
146089
  return eval_cps_run_state;
5411
}
5412
5413
// Only unblocks threads that are unblockable.
5414
// A sleeping thread is not unblockable.
5415
84
static void handle_event_unblock_ctx(lbm_cid cid, lbm_value v) {
5416
84
  eval_context_t *found = NULL;
5417
84
  mutex_lock(&qmutex);
5418
5419
84
  found = lookup_ctx_nm(&blocked, cid);
5420

84
  if (found && LBM_IS_STATE_UNBLOCKABLE(found->state)){
5421
84
    drop_ctx_nm(&blocked,found);
5422
84
    if (lbm_is_error(v)) {
5423
28
      get_stack_ptr(found, 1)[0] = TERMINATE; // replace TOS
5424
28
      found->app_cont = true;
5425
    }
5426
84
    found->r = v;
5427
84
    found->state = LBM_THREAD_STATE_READY;
5428
84
    enqueue_ctx_nm(&queue,found);
5429
  }
5430
84
  mutex_unlock(&qmutex);
5431
84
}
5432
5433
static void handle_event_define(lbm_value key, lbm_value val) {
5434
  lbm_uint dec_key = lbm_dec_sym(key);
5435
  lbm_uint ix_key  = dec_key & GLOBAL_ENV_MASK;
5436
  lbm_value *global_env = lbm_get_global_env();
5437
  lbm_uint orig_env = global_env[ix_key];
5438
  lbm_value new_env;
5439
  // A key is a symbol and should not need to be remembered.
5440
  WITH_GC(new_env, lbm_env_set(orig_env,key,val));
5441
5442
  global_env[ix_key] = new_env;
5443
}
5444
5445
7017
static lbm_value get_event_value(lbm_event_t *e) {
5446
  lbm_value v;
5447
7017
  if (e->buf_len > 0) {
5448
    lbm_flat_value_t fv;
5449
7017
    fv.buf = (uint8_t*)e->buf_ptr;
5450
7017
    fv.buf_size = e->buf_len;
5451
7017
    fv.buf_pos = 0;
5452
7017
    if (!lbm_unflatten_value(&fv, &v)) {
5453
      lbm_set_flags(LBM_FLAG_HANDLER_EVENT_DELIVERY_FAILED);
5454
      v = ENC_SYM_EERROR;
5455
    }
5456
    // Free the flat value buffer. GC is unaware of its existence.
5457
7017
    lbm_free(fv.buf);
5458
  } else {
5459
    v = (lbm_value)e->buf_ptr;
5460
  }
5461
7017
  return v;
5462
}
5463
5464
93328586
static void process_events(void) {
5465
5466
93328586
  if (!lbm_events) {
5467
    return;
5468
  }
5469
5470
  lbm_event_t e;
5471
186664189
  while (lbm_event_pop(&e)) {
5472
7017
    lbm_value event_val = get_event_value(&e);
5473

7017
    switch(e.type) {
5474
84
    case LBM_EVENT_UNBLOCK_CTX:
5475
84
      handle_event_unblock_ctx((lbm_cid)e.parameter, event_val);
5476
84
      break;
5477
    case LBM_EVENT_DEFINE:
5478
      handle_event_define((lbm_value)e.parameter, event_val);
5479
      break;
5480
6933
    case LBM_EVENT_FOR_HANDLER:
5481
6933
      if (lbm_event_handler_pid >= 0) {
5482
6933
        lbm_find_receiver_and_send(lbm_event_handler_pid, event_val);
5483
      }
5484
6933
      break;
5485
    case LBM_EVENT_RUN_USER_CALLBACK:
5486
      user_callback((void*)e.parameter);
5487
      break;
5488
    }
5489
93335603
  }
5490
}
5491
5492
/* eval_cps_run can be paused
5493
   I think it would be better use a mailbox for
5494
   communication between other threads and the run_eval
5495
   but for now a set of variables will be used. */
5496
21588
void lbm_run_eval(void){
5497
5498
21588
  if (setjmp(critical_error_jmp_buf) > 0) {
5499
    printf_callback("GC stack overflow!\n");
5500
    critical_error_callback();
5501
    // terminate evaluation thread.
5502
    return;
5503
  }
5504
5505
21588
  setjmp(error_jmp_buf);
5506
5507
113668
  while (eval_running) {
5508

128493
    if (eval_cps_state_changed  || eval_cps_run_state == EVAL_CPS_STATE_PAUSED) {
5509
98787
      eval_cps_state_changed = false;
5510

98787
      switch (eval_cps_next_state) {
5511
      case EVAL_CPS_STATE_RESET:
5512
        if (eval_cps_run_state != EVAL_CPS_STATE_RESET) {
5513
          is_atomic = false;
5514
          blocked.first = NULL;
5515
          blocked.last = NULL;
5516
          queue.first = NULL;
5517
          queue.last = NULL;
5518
          ctx_running = NULL;
5519
          eval_steps_quota = eval_steps_refill;
5520
          eval_cps_run_state = EVAL_CPS_STATE_RESET;
5521
          if (blocking_extension) {
5522
            blocking_extension = false;
5523
            mutex_unlock(&blocking_extension_mutex);
5524
          }
5525
        }
5526
        usleep_callback(EVAL_CPS_MIN_SLEEP);
5527
        continue;
5528
77199
      case EVAL_CPS_STATE_PAUSED:
5529
77199
        if (eval_cps_run_state != EVAL_CPS_STATE_PAUSED) {
5530
43168
          if (lbm_heap_num_free() < eval_cps_next_state_arg) {
5531
            gc();
5532
          }
5533
43168
          eval_cps_next_state_arg = 0;
5534
43168
          eval_cps_run_state = EVAL_CPS_STATE_PAUSED;
5535
        }
5536
77199
        usleep_callback(EVAL_CPS_MIN_SLEEP);
5537
40794
        continue;
5538
      case EVAL_CPS_STATE_KILL:
5539
        eval_cps_run_state = EVAL_CPS_STATE_DEAD;
5540
        eval_running = false;
5541
        continue;
5542
21588
      default: // running state
5543
21588
        eval_cps_run_state = eval_cps_next_state;
5544
21588
        break;
5545
      }
5546
29706
    }
5547
    while (true) {
5548

1005477472
      if (eval_steps_quota && ctx_running) {
5549
912105056
        eval_steps_quota--;
5550
912105056
        evaluation_step();
5551
      } else {
5552
93372416
        if (eval_cps_state_changed) break;
5553
93329286
        eval_steps_quota = eval_steps_refill;
5554
93329286
        if (!is_atomic) {
5555
93328586
          if (gc_requested) {
5556
96
            gc();
5557
          }
5558
93328586
          process_events();
5559
93328586
          mutex_lock(&qmutex);
5560
93328586
          if (ctx_running) {
5561
91165199
            enqueue_ctx_nm(&queue, ctx_running);
5562
91165199
            ctx_running = NULL;
5563
          }
5564
93328586
          wake_up_ctxs_nm();
5565
93328586
          ctx_running = dequeue_ctx_nm(&queue);
5566
93328586
          mutex_unlock(&qmutex);
5567
93328586
          if (!ctx_running) {
5568
2106353
            lbm_system_sleeping = true;
5569
            //Fixed sleep interval to poll events regularly.
5570
2106353
            usleep_callback(EVAL_CPS_MIN_SLEEP);
5571
2106345
            lbm_system_sleeping = false;
5572
          }
5573
        }
5574
      }
5575
    }
5576
  }
5577
}
5578
5579
lbm_cid lbm_eval_program(lbm_value lisp) {
5580
  return lbm_create_ctx(lisp, ENC_SYM_NIL, 256, NULL);
5581
}
5582
5583
lbm_cid lbm_eval_program_ext(lbm_value lisp, unsigned int stack_size) {
5584
  return lbm_create_ctx(lisp, ENC_SYM_NIL, stack_size, NULL);
5585
}
5586
5587
21588
int lbm_eval_init() {
5588
21588
  if (!qmutex_initialized) {
5589
21588
    mutex_init(&qmutex);
5590
21588
    qmutex_initialized = true;
5591
  }
5592
21588
  if (!lbm_events_mutex_initialized) {
5593
21588
    mutex_init(&lbm_events_mutex);
5594
21588
    lbm_events_mutex_initialized = true;
5595
  }
5596
21588
  if (!blocking_extension_mutex_initialized) {
5597
21588
    mutex_init(&blocking_extension_mutex);
5598
21588
    blocking_extension_mutex_initialized = true;
5599
  }
5600
5601
21588
  mutex_lock(&qmutex);
5602
21588
  mutex_lock(&lbm_events_mutex);
5603
5604
21588
  blocked.first = NULL;
5605
21588
  blocked.last = NULL;
5606
21588
  queue.first = NULL;
5607
21588
  queue.last = NULL;
5608
21588
  ctx_running = NULL;
5609
5610
21588
  eval_cps_run_state = EVAL_CPS_STATE_RUNNING;
5611
5612
21588
  mutex_unlock(&lbm_events_mutex);
5613
21588
  mutex_unlock(&qmutex);
5614
5615
21588
  reset_infer_canary();
5616
5617
21588
  if (!lbm_init_env()) return 0;
5618
21588
  eval_running = true;
5619
21588
  return 1;
5620
}
5621
5622
21588
bool lbm_eval_init_events(unsigned int num_events) {
5623
5624
21588
  mutex_lock(&lbm_events_mutex);
5625
21588
  lbm_events = (lbm_event_t*)lbm_malloc(num_events * sizeof(lbm_event_t));
5626
21588
  bool r = false;
5627
21588
  if (lbm_events) {
5628
21588
    lbm_events_max = num_events;
5629
21588
    lbm_events_head = 0;
5630
21588
    lbm_events_tail = 0;
5631
21588
    lbm_events_full = false;
5632
21588
    lbm_event_handler_pid = -1;
5633
21588
    r = true;
5634
  }
5635
21588
  mutex_unlock(&lbm_events_mutex);
5636
21588
  return r;
5637
}