diff --git a/README.md b/README.md index 99d783aa..a2e45cae 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,14 @@ # lispBM (LBM) -A concurrent lisp-like language with message-passing and -pattern-matching implemented in C for 32 bit and 64 bit platforms. +LispBM is a lisp or scheme like programming language for +microcontrollers. LispBM also borrows a couple of ideas from Erlang +when it comes to concurrency, message passing, pattern matching and +process monitoring. The LispBM runtime system can be compiled for +either 32 or 64 bit platforms and runs on a wide range of hardware +such as for example STM32, NRF52, ESP32 or X86. When running the +LispBM runtime system on a microcontroller it can be built on top of +ChibiOS, FreeRTOS or ZephyrOS and it can also be built to run on top +of a regular linux. ![LispBM mascot](https://github.com/svenssonjoel/lispBM/blob/master/mascot/lispbm_llama_small.png) diff --git a/benchmarks/plot_bench.py b/benchmarks/plot_bench.py index ac6d24a9..8a3fa8d9 100644 --- a/benchmarks/plot_bench.py +++ b/benchmarks/plot_bench.py @@ -36,7 +36,9 @@ for bench in benches: lgd = plt.legend(loc='center left', bbox_to_anchor=(1, 0.5)) ax = plt.gca() for tick in ax.get_xticklabels(): - tick.set_rotation(45) + tick.set_rotation(90) +ax.tick_params(axis='both', which='major', labelsize=6) +ax.tick_params(axis='both', which='minor', labelsize=4) plt.ylabel("Sec") plt.grid() plt.savefig('benchresults.png', dpi=600, bbox_extra_artists=(lgd,), bbox_inches='tight') diff --git a/doc/lbmref.md b/doc/lbmref.md index 7c98f49c..b7a8a502 100644 --- a/doc/lbmref.md +++ b/doc/lbmref.md @@ -347,53 +347,6 @@ the argument. ## Low level operations -### encode-i32 - -The `encode-i32` function converts a list of four (byte sized) values -into an i32 value. - -Example that evaluates to the i32 value 1024. -```clj -(encode-i32 (list 0 0 4 0)) -``` - - ---- - -### encode-u32 - -The `encode-u32` function converts a list of four (byte sized) values -into an u32 value. - -Example that evaluates to the u32 value 1024. -```clj -(encode-u32 (list 0 0 4 0)) -``` - ---- - -### encode-float - -The `encode-float` function converts a list four (byte sized) values -into a float value. - -Example that evaluates to 3.14. -```clj -(encode-float (list 64 72 245 195)) -``` - - ---- - -### decode - -The `decode` function decodes a value into a list of four (byte sized) values. - -Example that decodes float 3.14 into the list (64 72 245 195). -```clj -(decode 3.14) -``` - --- ## nil and t diff --git a/include/eval_cps.h b/include/eval_cps.h index e61dfd70..9d6f4126 100644 --- a/include/eval_cps.h +++ b/include/eval_cps.h @@ -141,16 +141,6 @@ void lbm_pause_eval(void); * \param num_free Perform GC if there are less than this many elements free on the heap. */ void lbm_pause_eval_with_gc(uint32_t num_free); -/** Perform a single step of evaluation. - * The evaluator should be in EVAL_CPS_STATE_PAUSED before running this function. - * After taking one step of evaluation, the evaluator will return to being in the - * EVAL_CPS_STATE_PUASED state. - */ -void lbm_step_eval(void); -/** Perform multiple steps of evaluation. - * \param n Number of eval steps to perform. - */ -void lbm_step_n_eval(uint32_t n); /** Resume from being in EVAL_CPS_STATE_PAUSED. * */ diff --git a/repl/repl.c b/repl/repl.c index f84942fd..416653b9 100644 --- a/repl/repl.c +++ b/repl/repl.c @@ -833,12 +833,6 @@ int main(int argc, char **argv) { } else if (strncmp(str, ":continue", 9) == 0) { lbm_continue_eval(); free(str); - } else if (strncmp(str, ":step", 5) == 0) { - - int num = atoi(str + 5); - - lbm_step_n_eval((uint32_t)num); - free(str); } else if (strncmp(str, ":inspect", 8) == 0) { int i = 8; diff --git a/src/eval_cps.c b/src/eval_cps.c index cd5c1a53..dbc72b8e 100644 --- a/src/eval_cps.c +++ b/src/eval_cps.c @@ -137,9 +137,10 @@ void lbm_set_eval_step_quota(uint32_t quota) { eval_steps_refill = quota; } -static uint32_t eval_cps_run_state = EVAL_CPS_STATE_INIT; -static volatile uint32_t eval_cps_next_state = EVAL_CPS_STATE_INIT; +static uint32_t eval_cps_run_state = EVAL_CPS_STATE_RUNNING; +static volatile uint32_t eval_cps_next_state = EVAL_CPS_STATE_RUNNING; static volatile uint32_t eval_cps_next_state_arg = 0; +static volatile bool eval_cps_state_changed = false; /* On ChibiOs the CH_CFG_ST_FREQUENCY setting in chconf.h sets the @@ -2884,29 +2885,23 @@ static void evaluation_step(void){ void lbm_pause_eval(void ) { eval_cps_next_state_arg = 0; eval_cps_next_state = EVAL_CPS_STATE_PAUSED; + if (eval_cps_next_state != eval_cps_run_state) eval_cps_state_changed = true; } void lbm_pause_eval_with_gc(uint32_t num_free) { eval_cps_next_state_arg = num_free; eval_cps_next_state = EVAL_CPS_STATE_PAUSED; -} - -void lbm_step_eval(void) { - eval_cps_next_state_arg = 1; - eval_cps_next_state = EVAL_CPS_STATE_STEP; -} - -void lbm_step_n_eval(uint32_t n) { - eval_cps_next_state_arg = n; - eval_cps_next_state = EVAL_CPS_STATE_STEP; + if (eval_cps_next_state != eval_cps_run_state) eval_cps_state_changed = true; } void lbm_continue_eval(void) { eval_cps_next_state = EVAL_CPS_STATE_RUNNING; + if (eval_cps_next_state != eval_cps_run_state) eval_cps_state_changed = true; } void lbm_kill_eval(void) { eval_cps_next_state = EVAL_CPS_STATE_KILL; + if (eval_cps_next_state != eval_cps_run_state) eval_cps_state_changed = true; } uint32_t lbm_get_eval_state(void) { @@ -2920,19 +2915,8 @@ uint32_t lbm_get_eval_state(void) { void lbm_run_eval(void){ while (eval_running) { - + eval_cps_state_changed = false; switch (eval_cps_next_state) { - case EVAL_CPS_STATE_INIT: - eval_cps_run_state = EVAL_CPS_STATE_RUNNING; - break; - case EVAL_CPS_STATE_STEP: - if (eval_cps_next_state_arg > 1) { - eval_cps_next_state = EVAL_CPS_STATE_STEP; - eval_cps_next_state_arg --; - } else { - eval_cps_next_state = EVAL_CPS_STATE_PAUSED; - } - break; case EVAL_CPS_STATE_PAUSED: if (eval_cps_run_state != EVAL_CPS_STATE_PAUSED) { if (lbm_heap_num_free() < eval_cps_next_state_arg) { @@ -2946,46 +2930,49 @@ void lbm_run_eval(void){ case EVAL_CPS_STATE_KILL: eval_running = false; continue; - default: + default: // running state eval_cps_run_state = eval_cps_next_state; break; } - eval_context_t *next_to_run = NULL; - if (eval_steps_quota <= 0 || !ctx_running) { - uint32_t us = EVAL_CPS_MIN_SLEEP; - - if (is_atomic) { - if (ctx_running) { - next_to_run = ctx_running; - ctx_running = NULL; - } else { - is_atomic = false; - // This is not right! - // but there is no context available to - // report an error in. - } + while (true) { + eval_context_t *next_to_run = NULL; + if (eval_steps_quota && ctx_running) { + eval_steps_quota--; + evaluation_step(); } else { - next_to_run = dequeue_ctx(&sleeping, &us); - } + if (eval_cps_state_changed) break; + uint32_t us = EVAL_CPS_MIN_SLEEP; - if (!next_to_run) { - next_to_run = enqueue_dequeue_ctx(&queue, ctx_running); - } else if (ctx_running) { - enqueue_ctx(&queue, ctx_running); - } + if (is_atomic) { + if (ctx_running) { + next_to_run = ctx_running; + ctx_running = NULL; + } else { + is_atomic = false; + // This is not right! + // but there is no context available to + // report an error in. + } + } else { + next_to_run = dequeue_ctx(&sleeping, &us); + } - eval_steps_quota = eval_steps_refill; - ctx_running = next_to_run; + if (!next_to_run) { + next_to_run = enqueue_dequeue_ctx(&queue, ctx_running); + } else if (ctx_running) { + enqueue_ctx(&queue, ctx_running); + } - if (!ctx_running) { - usleep_callback(us); - continue; + eval_steps_quota = eval_steps_refill; + ctx_running = next_to_run; + + if (!ctx_running) { + usleep_callback(us); + continue; + } } } - - eval_steps_quota--; - evaluation_step(); } } @@ -3010,7 +2997,7 @@ int lbm_eval_init() { done.last = NULL; ctx_running = NULL; - eval_cps_run_state = EVAL_CPS_STATE_INIT; + eval_cps_run_state = EVAL_CPS_STATE_RUNNING; mutex_init(&qmutex); diff --git a/tests/test_lisp_code_cps.c b/tests/test_lisp_code_cps.c index 1dd60db6..75ca857f 100644 --- a/tests/test_lisp_code_cps.c +++ b/tests/test_lisp_code_cps.c @@ -1,5 +1,5 @@ /* - Copyright 2018,2020 Joel Svensson svenssonjoel@yahoo.se + Copyright 2018,2020 Joel Svensson svenssonjoel@yahoo.se This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -109,8 +109,8 @@ bool dyn_load(const char *str, const char **code) { } else if (strlen(str) == 7 && strncmp(str, "reverse", 7) == 0) { *code = "(define reverse (lambda (xs)" "(let ((revacc (lambda (acc xs)" - "(if (eq nil xs) acc" - "(revacc (cons (car xs) acc) (cdr xs))))))" + "(if (eq nil xs) acc" + "(revacc (cons (car xs) acc) (cdr xs))))))" "(revacc nil xs))))"; res = true; } else if (strlen(str) == 4 && strncmp(str, "iota", 4) == 0) { @@ -122,45 +122,45 @@ bool dyn_load(const char *str, const char **code) { res = true; } else if (strlen(str) == 6 && strncmp(str, "length", 6) == 0) { *code = "(define length (lambda (xs)" - "(let ((len (lambda (l xs)" - "(if (eq xs nil) l" - "(len (+ l 1) (cdr xs))))))" + "(let ((len (lambda (l xs)" + "(if (eq xs nil) l" + "(len (+ l 1) (cdr xs))))))" "(len 0 xs))))"; res = true; } else if (strlen(str) == 4 && strncmp(str, "take", 4) == 0) { *code = "(define take (lambda (n xs)" - "(let ((take-tail (lambda (acc n xs)" - "(if (= n 0) acc" - "(take-tail (cons (car xs) acc) (- n 1) (cdr xs))))))" + "(let ((take-tail (lambda (acc n xs)" + "(if (= n 0) acc" + "(take-tail (cons (car xs) acc) (- n 1) (cdr xs))))))" "(reverse (take-tail nil n xs)))))"; res = true; } else if (strlen(str) == 4 && strncmp(str, "drop", 4) == 0) { *code = "(define drop (lambda (n xs)" - "(if (= n 0) xs" - "(if (eq xs nil) nil" + "(if (= n 0) xs" + "(if (eq xs nil) nil" "(drop (- n 1) (cdr xs))))))"; res = true; } else if (strlen(str) == 3 && strncmp(str, "zip", 3) == 0) { *code = "(define zip (lambda (xs ys)" - "(if (eq xs nil) nil" - "(if (eq ys nil) nil" + "(if (eq xs nil) nil" + "(if (eq ys nil) nil" "(cons (cons (car xs) (car ys)) (zip (cdr xs) (cdr ys)))))))"; res = true; } else if (strlen(str) == 3 && strncmp(str, "map", 3) == 0) { *code = "(define map (lambda (f xs)" - "(if (eq xs nil) nil" + "(if (eq xs nil) nil" "(cons (f (car xs)) (map f (cdr xs))))))"; res = true; } else if (strlen(str) == 6 && strncmp(str, "lookup", 6) == 0) { *code = "(define lookup (lambda (x xs)" - "(if (eq xs nil) nil" - "(if (eq (car (car xs)) x)" - "(car (cdr (car xs)))" + "(if (eq xs nil) nil" + "(if (eq (car (car xs)) x)" + "(car (cdr (car xs)))" "(lookup x (cdr xs))))))"; res = true; } else if (strlen(str) == 5 && strncmp(str, "foldr", 5) == 0) { *code = "(define foldr (lambda (f i xs)" - "(if (eq xs nil) i" + "(if (eq xs nil) i" "(f (car xs) (foldr f i (cdr xs))))))"; res = true; } else if (strlen(str) == 5 && strncmp(str, "foldl", 5) == 0) { @@ -212,7 +212,7 @@ LBM_EXTENSION(ext_numbers, args, argn) { break; } } - return lbm_enc_sym(b ? SYM_TRUE : SYM_NIL); + return lbm_enc_sym(b ? SYM_TRUE : SYM_NIL); } @@ -225,7 +225,7 @@ int main(int argc, char **argv) { // bool compress_decompress = false; bool stream_source = false; - + pthread_t lispbm_thd; lbm_cons_t *heap_storage = NULL; @@ -378,7 +378,7 @@ int main(int argc, char **argv) { printf("Error adding extension.\n"); return 0; } - + lbm_set_dynamic_load_callback(dyn_load); lbm_set_timestamp_us_callback(timestamp_callback); lbm_set_usleep_callback(sleep_callback); @@ -427,6 +427,11 @@ int main(int argc, char **argv) { //lbm_create_char_stream_from_string(&string_tok_state, // &string_tok, // code_buffer); + + lbm_pause_eval_with_gc(20); + while (lbm_get_eval_state() != EVAL_CPS_STATE_PAUSED) { + sleep_callback(1000); + } if (stream_source) { lbm_create_buffered_char_channel(&buffered_tok_state, &string_tok); @@ -455,7 +460,7 @@ int main(int argc, char **argv) { break; } int ch_res = lbm_channel_write(&string_tok, code_buffer[i]); - + if (ch_res == CHANNEL_SUCCESS) { //printf("wrote: %c\n", code_buffer[i]); i ++;