Merge commit '4dbe8c6837099ad4a676f74a04f62ef0041afd1a'

This commit is contained in:
Benjamin Vedder 2022-05-03 22:58:33 +02:00
commit 5a7f769d27
7 changed files with 174 additions and 31 deletions

View File

@ -124,7 +124,7 @@ An important difference between `eq` and `=` is
that equals compare the numerical values of the arguments. A 3 is a 3 that equals compare the numerical values of the arguments. A 3 is a 3
independent of them being different types. `eq` on the other independent of them being different types. `eq` on the other
hand compares the representations of the arguments exactly and they must hand compares the representations of the arguments exactly and they must
match in structure, type and value to be considered equal. match in structure, type and value to be considered equal.
Example of `=` comparison. Example of `=` comparison.
@ -560,7 +560,7 @@ A lambda can be immediately applied to an argument.
```clj ```clj
((lambda (x) (+ x 1)) 10) ((lambda (x) (+ x 1)) 10)
``` ```
The application above results in the value 11. The application above results in the value 11.
Using <a href="#define"> define </a> you can give a name to the function. Using <a href="#define"> define </a> you can give a name to the function.
```clj ```clj
(define inc (lambda (x) (+ x 1))) (define inc (lambda (x) (+ x 1)))
@ -651,7 +651,7 @@ The variable `a` is now `10` in the global environment.
Now, the value of `a` will be 20. Note that `a` is quoted in the `setvar` form application Now, the value of `a` will be 20. Note that `a` is quoted in the `setvar` form application
while it is not in the `define` form. This is because `define` requires the first while it is not in the `define` form. This is because `define` requires the first
argument to be a symbol while the `setvar` form requires the first argument to evaluate argument to be a symbol while the `setvar` form requires the first argument to evaluate
into a symbol. into a symbol.
You can also set the value of a let bound variable. You can also set the value of a let bound variable.
```clj ```clj
@ -660,12 +660,12 @@ You can also set the value of a let bound variable.
And you can change the value of a `#var`. And you can change the value of a `#var`.
```clj ```clj
(define #a 10) (define #a 10)
(set '#a 20) (set '#a 20)
``` ```
`#a` is now 20. `#a` is now 20.
--- ---
@ -852,6 +852,19 @@ Example that evaluates to 2.
--- ---
### setix
Destructively update an element in a list. The form of a `setix` expression
is `(setix list-expr index-extr value-expr)`. Indexing starts from 0 and
if you index out of bounds the result is nil.
```lisp
# (setix (list 1 2 3 4 5) 2 77)
> (1 2 77 4 5)
```
---
### setcar ### setcar
The `setcar` is a destructive update of the car field The `setcar` is a destructive update of the car field

View File

@ -78,9 +78,9 @@ extern int lbm_eval_init(void);
* \return 1 if a context was successfully removed otherwise 0. * \return 1 if a context was successfully removed otherwise 0.
*/ */
extern int lbm_remove_done_ctx(lbm_cid cid, lbm_value *v); extern int lbm_remove_done_ctx(lbm_cid cid, lbm_value *v);
/** Wait until a given cid is not present in any of the queues. /** Wait until a given cid is not present in any of the queues.
* If you have spawned this cid, you can conclude that it has * If you have spawned this cid, you can conclude that it has
* run to completion or failure. * run to completion or failure.
* *
* \param cid Context id to wait for. * \param cid Context id to wait for.
* \param timeout_ms timeout in ms or 0 for no timeout. * \param timeout_ms timeout in ms or 0 for no timeout.
@ -162,6 +162,16 @@ extern int lbm_set_error_reason(char *error_str);
* \return * \return
*/ */
extern lbm_cid lbm_create_ctx(lbm_value program, lbm_value env, lbm_uint stack_size); extern lbm_cid lbm_create_ctx(lbm_value program, lbm_value env, lbm_uint stack_size);
/** Block a context from an extension
*/
extern void lbm_block_ctx_from_extension(void);
/** Unblock a context that has been blocked by a C extension
* Trying to unblock a context that is waiting on a message
* in a mailbox is not encouraged
* \param cid Lisp process to wake up.
* \param result Value passed to the lisp process as the result from the blocking function.
*/
extern bool lbm_unblock_ctx(lbm_cid cid, lbm_value result);
/** Iterate over all ready contexts and apply function on each context. /** Iterate over all ready contexts and apply function on each context.
* *
* \param f Function to apply to each context. * \param f Function to apply to each context.
@ -221,6 +231,12 @@ extern void lbm_set_dynamic_load_callback(bool (*fptr)(const char *, const char
* within a context * within a context
*/ */
extern void lbm_set_reader_done_callback(void (*fptr)(lbm_cid)); extern void lbm_set_reader_done_callback(void (*fptr)(lbm_cid));
/** Get the CID of the currently executing context.
* Should be called from an extension where there is
* a guarantee that a context is running
*/
extern lbm_cid lbm_get_current_cid(void);
/** Create a token stream for parsing for code /** Create a token stream for parsing for code
* *
* \param str character stream to convert into a token stream. * \param str character stream to convert into a token stream.

View File

@ -29,6 +29,9 @@
/*! \page changelog Changelog /*! \page changelog Changelog
May 1 2022: Version 0.5.2
- Added lbm_stack_reserve for allocating multiple words on stack
in one function call (and one check on stack limits).
Apr 19 2022: Version 0.5.2 Apr 19 2022: Version 0.5.2
- Added a reader_done_callback that is run when a context is done - Added a reader_done_callback that is run when a context is done

View File

@ -73,6 +73,15 @@ extern lbm_uint *lbm_get_stack_ptr(lbm_stack_t *s, lbm_uint n);
* \return 1 on Success and 0 on failure. * \return 1 on Success and 0 on failure.
*/ */
extern int lbm_stack_drop(lbm_stack_t *s, lbm_uint n); extern int lbm_stack_drop(lbm_stack_t *s, lbm_uint n);
/** Reserve place for n elements on the stack and
* move the stack pointer to the new top.
* \param s Stack to reserve values on
* \param n Number of values to reserve
* \return Pointer into stack position of reserver value 0 or NULL
* on failure
*/
extern lbm_uint *lbm_stack_reserve(lbm_stack_t *s, lbm_uint n);
/** Push an element onto a stack. /** Push an element onto a stack.
* *
* \param s Stack to push a value onto. * \param s Stack to push a value onto.

View File

@ -322,6 +322,12 @@ bool dyn_load(const char *str, const char **code) {
return res; return res;
} }
lbm_value ext_block(lbm_value *args, lbm_uint argn) {
printf("blocking CID: %d\n", lbm_get_current_cid());
lbm_block_ctx_from_extension();
return lbm_enc_sym(SYM_TRUE);
}
lbm_value ext_print(lbm_value *args, lbm_uint argn) { lbm_value ext_print(lbm_value *args, lbm_uint argn) {
erase(); erase();
@ -511,7 +517,7 @@ int main(int argc, char **argv) {
} else { } else {
printf("Loading array extensions failed\n"); printf("Loading array extensions failed\n");
} }
if (lbm_string_extensions_init()) { if (lbm_string_extensions_init()) {
printf("String extensions loaded\n"); printf("String extensions loaded\n");
} else { } else {
@ -523,8 +529,13 @@ int main(int argc, char **argv) {
} else { } else {
printf("Loading math extensions failed\n"); printf("Loading math extensions failed\n");
} }
res = lbm_add_extension("block", ext_block);
if (res)
printf("Extension added.\n");
else
printf("Error adding extension.\n");
res = lbm_add_extension("print", ext_print); res = lbm_add_extension("print", ext_print);
if (res) if (res)
printf("Extension added.\n"); printf("Extension added.\n");
@ -640,6 +651,11 @@ int main(int argc, char **argv) {
printf("****** Done contexts ******\n"); printf("****** Done contexts ******\n");
lbm_done_iterator(print_ctx_info, NULL, NULL); lbm_done_iterator(print_ctx_info, NULL, NULL);
free(str); free(str);
} else if (strncmp(str, ":unblock", 8) == 0) {
int id = atoi(str + 8);
printf("Unblocking: %d\n", id);
lbm_unblock_ctx(id, lbm_enc_i(42));
free(str);
} else if (strncmp(str, ":wait", 5) == 0) { } else if (strncmp(str, ":wait", 5) == 0) {
int id = atoi(str + 5); int id = atoi(str + 5);
bool exists = false; bool exists = false;
@ -696,6 +712,12 @@ int main(int argc, char **argv) {
printf("Loading math extensions failed\n"); printf("Loading math extensions failed\n");
} }
res = lbm_add_extension("block", ext_block);
if (res)
printf("Extension added.\n");
else
printf("Error adding extension.\n");
lbm_add_extension("print", ext_print); lbm_add_extension("print", ext_print);
free(str); free(str);
} }

View File

@ -141,6 +141,8 @@ volatile uint32_t eval_cps_next_state_arg = 0;
static bool eval_running = false; static bool eval_running = false;
static uint32_t next_ctx_id = 1; static uint32_t next_ctx_id = 1;
static volatile bool blocking_extension = false;
typedef struct { typedef struct {
eval_context_t *first; eval_context_t *first;
eval_context_t *last; eval_context_t *last;
@ -196,6 +198,13 @@ void lbm_set_reader_done_callback(void (*fptr)(lbm_cid)) {
reader_done_callback = fptr; reader_done_callback = fptr;
} }
lbm_cid lbm_get_current_cid(void) {
if (ctx_running)
return ctx_running->id;
else
return -1;
}
void done_reading(lbm_cid cid) { void done_reading(lbm_cid cid) {
if (reader_done_callback != NULL) { if (reader_done_callback != NULL) {
reader_done_callback(cid); reader_done_callback(cid);
@ -689,6 +698,24 @@ static void advance_ctx(void) {
} }
} }
bool lbm_unblock_ctx(lbm_cid cid, lbm_value result) {
eval_context_t *found = NULL;
found = lookup_ctx(&blocked, cid);
if (found == NULL)
return false;
drop_ctx(&blocked,found);
found->r = result;
enqueue_ctx(&queue,found);
return true;
}
void lbm_block_ctx_from_extension(void) {
blocking_extension = true;
}
lbm_value lbm_find_receiver_and_send(lbm_cid cid, lbm_value msg) { lbm_value lbm_find_receiver_and_send(lbm_cid cid, lbm_value msg) {
eval_context_t *found = NULL; eval_context_t *found = NULL;
@ -1159,16 +1186,24 @@ static inline void eval_define(eval_context_t *ctx) {
lbm_value rest_args = lbm_cdr(args); lbm_value rest_args = lbm_cdr(args);
lbm_value val_exp = lbm_car(rest_args); lbm_value val_exp = lbm_car(rest_args);
lbm_uint *sptr = lbm_stack_reserve(&ctx->K, 2);
if (!sptr) {
error_ctx(lbm_enc_sym(SYM_STACK_ERROR));
return;
}
if (lbm_is_symbol(key) && lbm_is_symbol_nil(lbm_cdr(rest_args))) { if (lbm_is_symbol(key) && lbm_is_symbol_nil(lbm_cdr(rest_args))) {
lbm_uint sym_val = lbm_dec_sym(key); lbm_uint sym_val = lbm_dec_sym(key);
sptr[0] = key;
if ((sym_val >= VARIABLE_SYMBOLS_START) && if ((sym_val >= VARIABLE_SYMBOLS_START) &&
(sym_val < VARIABLE_SYMBOLS_END)) { (sym_val < VARIABLE_SYMBOLS_END)) {
CHECK_STACK(lbm_push_2(&ctx->K, key, SET_VARIABLE)); sptr[1] = SET_VARIABLE;
ctx->curr_exp = val_exp; ctx->curr_exp = val_exp;
return; return;
} else if (sym_val >= RUNTIME_SYMBOLS_START) { } else if (sym_val >= RUNTIME_SYMBOLS_START) {
CHECK_STACK(lbm_push_2(&ctx->K, key, SET_GLOBAL_ENV)); sptr[1] = SET_GLOBAL_ENV;
ctx->curr_exp = val_exp; ctx->curr_exp = val_exp;
return; return;
} }
@ -1183,7 +1218,14 @@ static inline void eval_progn(eval_context_t *ctx) {
lbm_value env = ctx->curr_env; lbm_value env = ctx->curr_env;
if (lbm_is_list(exps)) { if (lbm_is_list(exps)) {
CHECK_STACK(lbm_push_3(&ctx->K, env, lbm_cdr(exps), PROGN_REST)); lbm_uint *sptr = lbm_stack_reserve(&ctx->K, 3);
if (!sptr) {
error_ctx(lbm_enc_sym(SYM_STACK_ERROR));
return;
}
sptr[0] = env;
sptr[1] = lbm_cdr(exps);
sptr[2] = PROGN_REST;
ctx->curr_exp = lbm_car(exps); ctx->curr_exp = lbm_car(exps);
ctx->curr_env = env; ctx->curr_env = env;
} else if (lbm_is_symbol_nil(exps)) { } else if (lbm_is_symbol_nil(exps)) {
@ -1217,11 +1259,15 @@ static inline void eval_if(eval_context_t *ctx) {
lbm_value then_branch = lbm_car(cddr); lbm_value then_branch = lbm_car(cddr);
lbm_value else_branch = lbm_cadr(cddr); lbm_value else_branch = lbm_cadr(cddr);
CHECK_STACK(lbm_push_4(&ctx->K, lbm_uint *sptr = lbm_stack_reserve(&ctx->K, 4);
else_branch, if (!sptr) {
then_branch, error_ctx(lbm_enc_sym(SYM_STACK_ERROR));
ctx->curr_env, return;
IF)); }
sptr[0] = else_branch;
sptr[1] = then_branch;
sptr[2] = ctx->curr_env;
sptr[3] = IF;
ctx->curr_exp = lbm_cadr(ctx->curr_exp); ctx->curr_exp = lbm_cadr(ctx->curr_exp);
} }
@ -1254,8 +1300,16 @@ static inline void eval_let(eval_context_t *ctx) {
lbm_value key0 = lbm_car(lbm_car(binds)); lbm_value key0 = lbm_car(lbm_car(binds));
lbm_value val0_exp = lbm_cadr(lbm_car(binds)); lbm_value val0_exp = lbm_cadr(lbm_car(binds));
CHECK_STACK(lbm_push_5(&ctx->K, exp, lbm_cdr(binds), new_env, lbm_uint *sptr = lbm_stack_reserve(&ctx->K, 5);
key0, BIND_TO_KEY_REST)); if (!sptr) {
error_ctx(lbm_enc_sym(SYM_STACK_ERROR));
return;
}
sptr[0] = exp;
sptr[1] = lbm_cdr(binds);
sptr[2] = new_env;
sptr[3] = key0;
sptr[4] = BIND_TO_KEY_REST;
ctx->curr_exp = val0_exp; ctx->curr_exp = val0_exp;
ctx->curr_env = new_env; ctx->curr_env = new_env;
return; return;
@ -1738,6 +1792,15 @@ static inline void cont_application(eval_context_t *ctx) {
} }
lbm_stack_drop(&ctx->K, lbm_dec_u(count) + 1); lbm_stack_drop(&ctx->K, lbm_dec_u(count) + 1);
if (blocking_extension) {
blocking_extension = false;
ctx->timestamp = timestamp_us_callback();
ctx->sleep_us = 0;
ctx->app_cont = true;
enqueue_ctx(&blocked,ctx);
ctx_running = NULL;
break;
}
ctx->app_cont = true; ctx->app_cont = true;
ctx->r = ext_res; ctx->r = ext_res;
break; break;
@ -2311,11 +2374,15 @@ static inline void cont_application_start(eval_context_t *ctx) {
lbm_value clo_env = lbm_car(cdddr_fun); lbm_value clo_env = lbm_car(cdddr_fun);
lbm_value arg_env = (lbm_value)sptr[0]; lbm_value arg_env = (lbm_value)sptr[0];
sptr[1] = exp; sptr[1] = exp;
CHECK_STACK(lbm_push_4(&ctx->K, lbm_value *reserved = lbm_stack_reserve(&ctx->K, 4);
clo_env, if (!reserved) {
params, error_ctx(lbm_enc_sym(SYM_STACK_ERROR));
lbm_cdr(args), return;
CLOSURE_ARGS)); }
reserved[0] = clo_env;
reserved[1] = params;
reserved[2] = lbm_cdr(args);
reserved[3] = CLOSURE_ARGS;
ctx->curr_exp = lbm_car(args); ctx->curr_exp = lbm_car(args);
ctx->curr_env = arg_env; ctx->curr_env = arg_env;
ctx->app_cont = false; ctx->app_cont = false;
@ -2446,11 +2513,14 @@ static void evaluation_step(void){
* At this point head can be a closure, fundamental, extension or a macro. * At this point head can be a closure, fundamental, extension or a macro.
* Anything else would be an error. * Anything else would be an error.
*/ */
lbm_value *reserved = lbm_stack_reserve(&ctx->K, 3);
CHECK_STACK(lbm_push_3(&ctx->K, if (!reserved) {
ctx->curr_env, error_ctx(lbm_enc_sym(SYM_STACK_ERROR));
lbm_cdr(ctx->curr_exp), return;
APPLICATION_START)); }
reserved[0] = ctx->curr_env;
reserved[1] = lbm_cdr(ctx->curr_exp);
reserved[2] = APPLICATION_START;
ctx->curr_exp = head; // evaluate the function ctx->curr_exp = head; // evaluate the function
break; break;

View File

@ -65,6 +65,16 @@ int lbm_stack_drop(lbm_stack_t *s, lbm_uint n) {
return 1; return 1;
} }
lbm_uint *lbm_stack_reserve(lbm_stack_t *s, lbm_uint n) {
if (s->sp + n >= s->size) {
return NULL;
}
lbm_uint *ptr = &s->data[s->sp];
s->sp += n;
return ptr;
}
int lbm_push(lbm_stack_t *s, lbm_uint val) { int lbm_push(lbm_stack_t *s, lbm_uint val) {
int res = 1; int res = 1;
if (s->sp == s->size) { if (s->sp == s->size) {