Squashed 'lispBM/lispBM/' changes from 520fde1a..edb024b7

edb024b7 BUGFIX: in lbm_is_number that would sometimes incorrectly state something is a number when it is not
a620e42c small tweak to REPL
f9911c0f terminate REPL upon problems, added error codes for traceability
c36f1e72 small tweak lbmref
04cad3d8 rearrange charts
86429f0f added images related to f32, f64 performance on various platforms
108791b9 update the reference manual
90dbbf63 added secs-since operation to repl
134c59a5 changes to perfomance plot python script
6a02f301 Update lbmref.md
b913f173 small note about usage of vesc hardware for data collection
0bbf246a update lbmref with some typo fixes
c62eec6d updates to lbmref
f6e188e0 adding some information about numerical operation performance
b8a55394 adding some information about numerical operation performance
ef58050d adding some information about numerical operation performance
e98d5b87 correct file 32bit
3002333a added perfomance examples
c74558cc adding to refman about numbers
ebca34cb type fix lbmref
3fde1dcb type fix lbmref
bfcd8247 update to lbmref
32b56fd3 added some info to lbmref

git-subtree-dir: lispBM/lispBM
git-subtree-split: edb024b75fe37c8f948fccb4e7d992cf783fd429
This commit is contained in:
Benjamin Vedder 2024-02-27 13:39:30 +01:00
parent 3c051b179a
commit 128e19cfa6
20 changed files with 375 additions and 77 deletions

View File

@ -40,6 +40,8 @@ lgd = plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))
ax = plt.gca() ax = plt.gca()
for tick in ax.get_xticklabels(): for tick in ax.get_xticklabels():
tick.set_rotation(90) tick.set_rotation(90)
n = 4 # Keeps every 7th label
[l.set_visible(False) for (i,l) in enumerate(ax.xaxis.get_ticklabels()) if i % n != 0]
ax.tick_params(axis='both', which='major', labelsize=6) ax.tick_params(axis='both', which='major', labelsize=6)
ax.tick_params(axis='both', which='minor', labelsize=4) ax.tick_params(axis='both', which='minor', labelsize=4)
ax.set_facecolor("lightgray"); ax.set_facecolor("lightgray");

BIN
doc/images/float_riscv.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
doc/images/float_stm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
doc/images/float_x86_32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
doc/images/float_x86_64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
doc/images/millions.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
doc/images/millions64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -37,7 +37,7 @@ should not be able to redefine and trying to redefine them leads to an error.
Symbols that start with `ext-` are special and reserved for use together Symbols that start with `ext-` are special and reserved for use together
with extensions that are loaded and bound at runtime. with extensions that are loaded and bound at runtime.
Examples of symbols used as data are `nil` and `t`. `nil` representds Examples of symbols used as data are `nil` and `t`. `nil` represents
"nothing", the empty list or other similar things and `t` "nothing", the empty list or other similar things and `t`
represents true. But any symbol can be used as data by quoting it represents true. But any symbol can be used as data by quoting it
`'`, see <a href="#quotes-and-quasiquotation"> Quotes and `'`, see <a href="#quotes-and-quasiquotation"> Quotes and
@ -75,37 +75,254 @@ The numerical types in LBM are
8. f32 - (float) a 32bit floating point value. 8. f32 - (float) a 32bit floating point value.
9. f64 - (double) a 64bit floating point value. 9. f64 - (double) a 64bit floating point value.
The byte and the char value have identical representation and type, thus char is an unsigned 8 bit type in LBM. The byte and the char value have identical representation and type,
thus char is an unsigned 8 bit type in LBM.
An integer literal is interpreted to be of type 'i', a 28/56bit signed integer value. An integer literal is interpreted to be of type `i`, a 28/56bit signed
A literal with decimal point is interpreted to be a type 'f32' or float value. integer value. A literal with decimal point is interpreted to be a
type `f32` or float value.
To specify literals of the othertype the value is to be postfixed with a qualifier string. To specify literals of the other types, the value must be postfixed with
The qualifiers available in LBM are: 'b', 'i', 'u', 'i32', 'u32', 'i64', 'u64', 'f32' and 'f63'. a qualifier string. The qualifiers available in LBM are: `b`, `i`,
The 'i' and 'f32' qualifiers are never strictly needed but can be added if one so wishes. `u`, `i32`, `u32`, `i64`, `u64`, `f32` and `f63`. The `i` and `f32`
qualifiers are never strictly needed but can be added if one so
wishes.
So for example: So for example:
1. '1b' - Specifies a byte typed value of 1 1. `1b` - Specifies a byte typed value of 1
2. '1.0f64' - Specifies a 64bit float with value 1.0. 2. `1.0f64` - Specifies a 64bit float with value 1.0.
**Note** that it is an absolute requirement to include a decimal when writing a floating point literal in LBM. **Note** that it is an absolute requirement to include a decimal when
writing a floating point literal in LBM.
We are trying to make type conversions to not feel too unfamilar to people We are trying to make type conversions feel familar to people who are
who are familiar with the C programming language. On a 32bit platform familiar with the C programming language. On a 32bit platform LBM
LBM numerical types are ordered according to: 'byte < i < u < i32 < u32 < i64 < u64 < float < double'. numerical types are ordered according to: `byte < i < u < i32 < u32 <
Operations such as '(+ a b)', figures out the largest type according to the ordering above and converts the i64 < u64 < float < double`. Operations such as `(+ a b)`, figures
all values to this largest type. out the largest type according to the ordering above and converts all
the values to this largest type.
Example: Example:
1. '(+ 1u 3i32)' - Promotes the 1u value type i32 and performs the addition, resulting in 4i32. 1. `(+ 1u 3i32)` - Promotes the 1u value type i32 and performs the addition, resulting in 4i32.
2. '(+ 1 3.14)' - Here the value 1 is of type 'i' which is smaller than 'f32', the result 4.14f32. 2. `(+ 1 3.14)` - Here the value 1 is of type `i` which is smaller than `f32`, the result 4.14f32.
A potential source of confusion is that 'f32' is a larger type than 'i64' and 'u64'. this means A potential source of confusion is that `f32` is a larger type than
that if you, for example, add 1.0 to an 'i64' value you will get an 'f32' back. If you instead wanted `i64` and `u64`. this means that if you, for example, add 1.0 to an
the float to be converted into a double before the addition, this has to be done manually. `i64` value you will get an `f32` back. If you instead wanted the
float to be converted into a double before the addition, this has to
be done manually.
Example: Example:
1. '(+ (to-double 1.0) 5i64)' - Manually convert a value to double. 1. `(+ (to-double 1.0) 5i64)` - Manually convert a value to double.
The `type-of` operation can be used to query a value for its type. On the
numerical types the `type-of` operation answers as follows:
1. `(type-of 1b)` -> `type-char`
2. `(type-of 1)` -> `type-i`
3. `(type-of 1u)` -> `type-u`
4. `(type-of 1i32)` -> `type-i32`
5. `(type-of 1u32)` -> `type-u32`
6. `(type-of 1i64)` -> `type-i64`
7. `(type-of 1u64)` -> `type-u64`
8. `(type-of 1.0)` -> `type-float`
9. `(type-of 1.0f64)` -> `type-double`
### Overflow behaviour
Operations on fixed bitwidth mumerical types can lead to overflow.
The ranges representable in 32bit LBMs integer types are the following:
1. `type-char` : 0 - 255
2. `type-i` : -134217728 - 134217727
3. `type-u` : 0 - 268435455
4. `type-i32` : -2147483648 - 2147483647
5. `type-u32` : 0- 4294967295
6. `type-i64` : -9223372036854775808 - 9223372036854775807
7. `type-u64` : 0 - 18446744073709551615
**Overflow of Byte**
```
# (+ 255b 1b)
> 0b
```
**Underflow of byte**
```
# (- 0b 1b)
> 255b
```
**Overflow of i**
```
# ( + 134217727 1)
> -134217728
```
**Underflow of i**
```
# (- -134217728 1)
> 134217727
```
**Overflow of u**
```
# (+ 268435455u 1u)
> 0u
```
**Underflow of u**
```
# (- 0u 1u)
> 268435455u
```
**Overflow of i32**
```
# (+ 2147483647i32 1i32)
> -2147483648i32
```
**Underflow of i32**
```
# (- 2147483648i32 1i32)
> 2147483647i32
```
**Overflow of u32**
```
# (+ 4294967295u32 1)
> 0u32
```
**Underflow of u32**
```
# (- 0u32 1)
> 4294967295u32
```
**Overflow of i64**
```
# (+ 9223372036854775807i64 1i64)
> -9223372036854775808i64
```
**Underflow of i64**
```
# (- -9223372036854775808i64 1i64)
> 9223372036854775807i64
```
**Overflow of u64**
```
# (+ 18446744073709551615u64 1u64)
> 0u64
```
**Underflow of u64**
```
# (- 0u64 1u64)
> 18446744073709551615u64
```
### Cost of numerical operations
All Values in LBM are encoded in one way or another. The encoded value
holds additional information about type and garbage collection mark
bit. Operations that operate on an LBM value needs to unpack this
encoded format and extract the actual numerical information from the
representation. This has a cost and operations on numbers are in
general a bit slower than what one gets in, for example C.
The chart below shows the time it takes to perform 10 million
additions on the x86 architecture (a i7-6820HQ) in 32Bit mode. The
difference in cost is negligible between the types `byte` - `u32` with
a huge increase in cost for 64 bit types.
All Integer types | 32Bit or smaller
:-------------------------:|:-------------------------:
![Performance of 10 million additions at various types on x86 32bit](./images/millions.png) |![Performance of 10 million additions at various types on x86 32bit](./images/millions_zoom.png)
The charts below compare floating point operations to `u32` operations on x86 32Bit.
There is little difference in cost of `f32` and `u32` operations, but a large increase
in cost when going to `f64` (double).
`f32` and `f64` vs `u32` | `f32` vs `u32`
:-------------------------:|:-------------------------:
![Performance of floating point additions on x86 32bit](./images/float_x86_32.png) |![Performance floating point additions on x86 32bit](./images/float_x86_32_zoom.png)
In 64Bit mode the x86 version of LBM shows negligible differences in
cost of additions at different types.
All Integer types | `f32` and `f64` vs `u32`
:-------------------------:|:-------------------------:
![Performance of 10 million additions at various types on x86 64bit](./images/millions64.png) | ![Performance of floating point additions on x86 64bit](./images/float_x86_64.png)
On 64Bit x86 the difference in cost is little accross all LBM types.
For addition performance on embedded systems, we use the the EDU VESC
motorcontroller as the STM32F4 candidate and the VESC EXPRESS for a
RISCV data point.
On ESP32C3, a 160MHz 32Bit RISCV core, time is measured over 100000
additions. There is a more pronounced gap between 28Bit and smaller
types and the 32Bit types here. Likely because of the differences in
encoding of 28Bit or less types and 32Bit types.
All Integer types | 32Bit or smaller
:-------------------------:|:-------------------------:
![Performance of 100000 addtions at various types on ESP32C3 RISCV](./images/thousands_riscv.png) | ![Performance of 100000 addtions at various types on ESP32C3 RISCV](./images/thousands_riscv_zoom.png)
On RISCV the difference in cost between `u32` and `f32` operations is small.
This is a bit surprising as the ESP32C3 does not have a floating point unit. It
is possible that the encoding/decoding of numbers is dominating the cost
of any numerical opeation.
`f32` and `f64` vs `u32` | `f32` vs `u32`
:-------------------------:|:-------------------------:
![Performance of floating point additions on ESP32C3 RISCV](./images/float_riscv.png) |![Performance floating point additions on ESP32C3 RISCV](./images/float_riscv_zoom.png)
On the STM32F4 at 168MHz (an EDU VESC) The results are similar to
ESP32 but slower. The slower performance on the VESC compared to the
VESC_Express ESP32 may be caused by the VESC firmware running
motorcontrol interrups at a high frequency.
All Integer types | 32Bit or smaller
:-------------------------:|:-------------------------:
![Performance of 100000 addtions at various types on STM32F4](./images/thousands_arm.png) | ![Performance of 100000 addtions at various types on STM32F4](./images/thousands_arm_zoom.png)
The cost of `f32` operations compared to `u32` on the STM32F4 shows
little differences. As expected there is a jump up in cost when going to 64Bit.
`f32` and `f64` vs `u32` | `f32` vs `u32`
:-------------------------:|:-------------------------:
![Performance of floating point additions on STM32F4](./images/float_stm.png) |![Performance floating point additions on STM32F4](./images/float_stm_zoom.png)
In general, on 32Bit platforms, the cost of operations on numerical
types that are 32Bit or less are about equal in cost. The costs
presented here was created by timing a large number of 2 argument
additions. Do not see these measurements as the "truth carved in stone",
LBM performance keeps changing over time as we make improvements, but
use them as a rough guiding principle. If anything can be taken away
from this it is to stay away from 64Bit value operations in your
tightest and most time critical loops.
---
# Reference Manual
## Arithmetic ## Arithmetic

View File

@ -798,9 +798,10 @@ static inline bool lbm_is_cons(lbm_value x) {
* \return true is x represents a number and false otherwise. * \return true is x represents a number and false otherwise.
*/ */
static inline bool lbm_is_number(lbm_value x) { static inline bool lbm_is_number(lbm_value x) {
return (x & LBM_VAL_TYPE_MASK || return
((x & (LBM_NUMBER_MASK | LBM_PTR_MASK)) == (x & LBM_PTR_BIT) ?
(LBM_NUMBER_MASK | LBM_PTR_MASK))); ((x & LBM_NUMBER_MASK) == LBM_NUMBER_MASK) :
(x & LBM_VAL_TYPE_MASK);
} }
/** Check if value is an array that can be READ /** Check if value is an array that can be READ

View File

@ -41,6 +41,7 @@
#include "lbm_version.h" #include "lbm_version.h"
#include "repl_exts.h" #include "repl_exts.h"
#include "repl_defines.h"
#define GC_STACK_SIZE 256 #define GC_STACK_SIZE 256
#define PRINT_STACK_SIZE 256 #define PRINT_STACK_SIZE 256
@ -64,6 +65,13 @@ volatile bool silent_mode = false;
void shutdown_procedure(void); void shutdown_procedure(void);
void terminate_repl(int exit_code) {
if (!silent_mode) {
printf("%s\n", repl_exit_message[exit_code]);
}
exit(exit_code);
}
bool const_heap_write(lbm_uint ix, lbm_uint w) { bool const_heap_write(lbm_uint ix, lbm_uint w) {
if (ix >= CONSTANT_MEMORY_SIZE) return false; if (ix >= CONSTANT_MEMORY_SIZE) return false;
if (constants_memory[ix] == 0xffffffff) { if (constants_memory[ix] == 0xffffffff) {
@ -110,7 +118,7 @@ void *eval_thd_wrapper(void *v) {
void critical(void) { void critical(void) {
printf("CRITICAL ERROR\n"); printf("CRITICAL ERROR\n");
exit(0); terminate_repl(REPL_EXIT_CRITICAL_ERROR);
} }
void done_callback(eval_context_t *ctx) { void done_callback(eval_context_t *ctx) {
@ -340,6 +348,7 @@ typedef struct src_list_s {
src_list_t *sources = NULL; src_list_t *sources = NULL;
bool src_list_add(char *filename) { bool src_list_add(char *filename) {
if (strlen(filename) == 0) return false;
src_list_t *entry=malloc(sizeof(src_list_t)); src_list_t *entry=malloc(sizeof(src_list_t));
if (!entry) return false; if (!entry) return false;
@ -399,11 +408,11 @@ void parse_opts(int argc, char **argv) {
"Multiple sources are evaluated in sequence in the order they are specified\n" \ "Multiple sources are evaluated in sequence in the order they are specified\n" \
"on the command-line. Source file N will not start evaluating until after\n" \ "on the command-line. Source file N will not start evaluating until after\n" \
"source file (N-1) has terminated, for N larger than 1.\n"); "source file (N-1) has terminated, for N larger than 1.\n");
exit(EXIT_SUCCESS); terminate_repl(REPL_EXIT_SUCCESS);
case 's': case 's':
if (!src_list_add((char*)optarg)) { if (!src_list_add((char*)optarg)) {
printf("Error adding source file to source list\n"); printf("Error adding source file to source list\n");
exit(EXIT_FAILURE); terminate_repl(REPL_EXIT_INVALID_SOURCE_FILE);
} }
break; break;
case LOAD_ENVIRONMENT: case LOAD_ENVIRONMENT:
@ -524,8 +533,7 @@ void startup_procedure(void) {
if (env_input_file) { if (env_input_file) {
FILE *fp = fopen(env_input_file, "r"); FILE *fp = fopen(env_input_file, "r");
if (!fp) { if (!fp) {
printf("Error opening environment input file\n"); terminate_repl(REPL_EXIT_UNABLE_TO_OPEN_ENV_FILE);
goto startup_procedure_1;
} }
while (true) { while (true) {
uint32_t name_len; uint32_t name_len;
@ -533,41 +541,36 @@ void startup_procedure(void) {
if ( n < sizeof(uint32_t) ) { if ( n < sizeof(uint32_t) ) {
// We are successfully done if n == 0; // We are successfully done if n == 0;
if (n > 0) { if (n > 0) {
printf("ALERT: Trailing data in env file\n"); terminate_repl(REPL_EXIT_INVALID_ENV_FILE);
} }
break; break;
} }
if (name_len == 0) { if (name_len == 0) {
printf("ALERT: Zero length name in env file\n"); terminate_repl(REPL_EXIT_ZERO_LENGTH_KEY_IN_ENV_FILE);
break;
} }
memset(name_buf, 0, NAME_BUF_SIZE); memset(name_buf, 0, NAME_BUF_SIZE);
n = fread(name_buf, 1, name_len, fp); n = fread(name_buf, 1, name_len, fp);
if (n < name_len) { if (n < name_len) {
printf("ALERT: Malformed name in env file\n"); terminate_repl(REPL_EXIT_INVALID_KEY_IN_ENV_FILE);
break;
} }
lbm_uint sym_id = 0; lbm_uint sym_id = 0;
if (!lbm_get_symbol_by_name(name_buf, &sym_id)) { if (!lbm_get_symbol_by_name(name_buf, &sym_id)) {
if (!lbm_add_symbol(name_buf, &sym_id)) { if (!lbm_add_symbol(name_buf, &sym_id)) {
printf("ALERT: Unable to add symbol\n"); terminate_repl(REPL_EXIT_UNABLE_TO_CREATE_SYMBOL);
break;
} }
} }
char *sym = (char*)lbm_get_name_by_symbol(sym_id); char *sym = (char*)lbm_get_name_by_symbol(sym_id);
if (!sym) { if (!sym) {
printf("ERROR\n"); terminate_repl(REPL_EXIT_UNABLE_TO_CREATE_SYMBOL);
exit(EXIT_FAILURE);
} }
lbm_value key = lbm_enc_sym(sym_id); lbm_value key = lbm_enc_sym(sym_id);
uint32_t val_len; uint32_t val_len;
n = fread(&val_len, 1, sizeof(uint32_t), fp); n = fread(&val_len, 1, sizeof(uint32_t), fp);
if (n < sizeof(uint32_t) || val_len == 0) { if (n < sizeof(uint32_t) || val_len == 0) {
printf("ALERT: Invalid value size in env file\n"); terminate_repl(REPL_EXIT_INVALID_VALUE_SIZE_IN_ENV_FILE);
break;
} }
int retry = 0; int retry = 0;
uint8_t *data = NULL; uint8_t *data = NULL;
@ -583,14 +586,11 @@ void startup_procedure(void) {
} }
} }
if (retry >= 100) { if (retry >= 100) {
printf("ALERT: Unable to allocate memory for flat value inside LBM\n"); terminate_repl(REPL_EXIT_OUT_OF_MEMORY_WHILE_PROCESSING_ENV_FILE);
break;
} }
n = fread(data, 1, val_len, fp); n = fread(data, 1, val_len, fp);
if (n < val_len) { if (n < val_len) {
lbm_free(data); terminate_repl(REPL_EXIT_INVALID_VALUE_IN_ENV_FILE);
printf("ALERT: Invalid value in env file\n");
break;
} }
// Nonintuitive that fv is reused before event is processed. // Nonintuitive that fv is reused before event is processed.
@ -613,38 +613,32 @@ void startup_procedure(void) {
} }
} }
if (retry >= 100) { if (retry >= 100) {
printf("ALERT: Unable to create define-event inside LBM\n"); terminate_repl(REPL_EXIT_LBM_EVENT_QUEUE_FULL);
break;
} }
while (!lbm_event_queue_is_empty()) { while (!lbm_event_queue_is_empty()) {
sleep_callback(100); sleep_callback(100);
} }
sleep_callback(1000); sleep_callback(1000);
// delay a little bit to allow all the events to be handled // delay a little bit to allow all the events to be handled
// and the environment be fully populated // and the environment be fully populated
} }
} }
startup_procedure_1:
if (sources) { if (sources) {
evaluate_sources(); evaluate_sources();
} }
if(terminate_after_startup) { if(terminate_after_startup) {
shutdown_procedure(); shutdown_procedure();
exit(EXIT_SUCCESS); terminate_repl(REPL_EXIT_SUCCESS);
} }
} }
void shutdown_procedure(void) {
if (env_output_file) {
int store_env(char *filename) {
FILE *fp = fopen(env_output_file, "w"); FILE *fp = fopen(env_output_file, "w");
if (!fp) { if (!fp) {
printf("Error opening environment output file\n"); terminate_repl(REPL_EXIT_UNABLE_TO_OPEN_ENV_FILE);
goto shutdown_procedure_1;
} }
lbm_value* env = lbm_get_global_env(); lbm_value* env = lbm_get_global_env();
for (int i = 0; i < GLOBAL_ENV_ROOTS; i ++) { for (int i = 0; i < GLOBAL_ENV_ROOTS; i ++) {
@ -653,14 +647,13 @@ void shutdown_procedure(void) {
lbm_value name_field = lbm_caar(curr); lbm_value name_field = lbm_caar(curr);
lbm_value val_field = lbm_cdr(lbm_car(curr)); lbm_value val_field = lbm_cdr(lbm_car(curr));
char *name = (char*)lbm_get_name_by_symbol(lbm_dec_sym(name_field)); char *name = (char*)lbm_get_name_by_symbol(lbm_dec_sym(name_field));
if (!name) goto shutdown_procedure_1; if (!name) return REPL_EXIT_UNABLE_TO_ACCESS_SYMBOL_STRING;
int32_t fv_size = flatten_value_size(val_field, 0); int32_t fv_size = flatten_value_size(val_field, 0);
if (fv_size > 0) { if (fv_size > 0) {
lbm_flat_value_t fv; lbm_flat_value_t fv;
fv.buf = malloc((uint32_t)fv_size); fv.buf = malloc((uint32_t)fv_size);
if (!fv.buf) { if (!fv.buf) {
printf("Error allocating memory for flat value\n"); return REPL_EXIT_ERROR_FLATTEN_NO_MEM;
exit(EXIT_FAILURE);
} }
fv.buf_size = (uint32_t)fv_size; fv.buf_size = (uint32_t)fv_size;
fv.buf_pos = 0; fv.buf_pos = 0;
@ -673,33 +666,29 @@ void shutdown_procedure(void) {
fwrite(&fv_size, 1, sizeof(int32_t),fp); fwrite(&fv_size, 1, sizeof(int32_t),fp);
fwrite(fv.buf,1,(size_t)fv_size,fp); fwrite(fv.buf,1,(size_t)fv_size,fp);
} else { } else {
printf("ALERT: Malformed key in environment\n"); return REPL_EXIT_INVALID_KEY_IN_ENVIRONMENT;
} }
} else { } else {
printf("ALERT: A value in the environment was not flattenable\n");
switch (r) { switch (r) {
case FLATTEN_VALUE_ERROR_CANNOT_BE_FLATTENED: case FLATTEN_VALUE_ERROR_CANNOT_BE_FLATTENED:
printf("CANNOT_BE_FLATTENED Error!\n"); return REPL_EXIT_VALUE_CANNOT_BE_FLATTENED;
break; break;
case FLATTEN_VALUE_ERROR_BUFFER_TOO_SMALL: case FLATTEN_VALUE_ERROR_BUFFER_TOO_SMALL:
printf("Flat value buffer too small!\n"); return REPL_EXIT_FLAT_VALUE_BUFFER_TOO_SMALL;
break; break;
case FLATTEN_VALUE_ERROR_FATAL: case FLATTEN_VALUE_ERROR_FATAL:
printf("Fatal error during flattening\n"); return REPL_EXIT_FATAL_ERROR_WHILE_FLATTENING;
break; break;
case FLATTEN_VALUE_ERROR_CIRCULAR: case FLATTEN_VALUE_ERROR_CIRCULAR:
printf("Potentially circular value\n"); return REPL_EXIT_CIRCULAR_VALUE;
break; break;
case FLATTEN_VALUE_ERROR_MAXIMUM_DEPTH: case FLATTEN_VALUE_ERROR_MAXIMUM_DEPTH:
printf("Maximum cons depth reached\n"); return REPL_EXIT_FLATTENING_MAXIMUM_DEPTH;
break; break;
case FLATTEN_VALUE_ERROR_NOT_ENOUGH_MEMORY: case FLATTEN_VALUE_ERROR_NOT_ENOUGH_MEMORY:
printf("Not enough memory while flattening\n"); return REPL_EXIT_OUT_OF_MEMORY_WHILE_FLATTENING;
break; break;
} }
char buf[1024];
lbm_print_value(buf,1024, val_field);
printf("Value: %s\n", buf);
} }
free(fv.buf); free(fv.buf);
} }
@ -707,8 +696,15 @@ void shutdown_procedure(void) {
} }
} }
fclose(fp); fclose(fp);
return REPL_EXIT_SUCCESS;
}
void shutdown_procedure(void) {
if (env_output_file) {
int r = store_env(env_output_file);
if (r != REPL_EXIT_SUCCESS) terminate_repl(r);
} }
shutdown_procedure_1:
return; return;
} }
@ -718,8 +714,7 @@ int main(int argc, char **argv) {
using_history(); using_history();
if (!init_repl()) { if (!init_repl()) {
printf ("Failed to initialize REPL\n"); terminate_repl(REPL_EXIT_UNABLE_TO_INIT_LBM);
return -1;
} }
startup_procedure(); startup_procedure();
char output[1024]; char output[1024];
@ -732,7 +727,7 @@ int main(int argc, char **argv) {
} else { } else {
str = readline("# "); str = readline("# ");
} }
if (str == NULL) exit(EXIT_SUCCESS); if (str == NULL) terminate_repl(REPL_EXIT_SUCCESS);
add_history(str); add_history(str);
unsigned int n = strlen(str); unsigned int n = strlen(str);
@ -880,14 +875,14 @@ int main(int argc, char **argv) {
heap_size = (unsigned int)size; heap_size = (unsigned int)size;
if (!init_repl()) { if (!init_repl()) {
printf("Failed to initialize REPL after heap resize\n"); printf("Failed to initialize REPL after heap resize\n");
return -1; terminate_repl(REPL_EXIT_UNABLE_TO_INIT_LBM);
} }
} }
free(str); free(str);
} else if (strncmp(str, ":reset", 6) == 0) { } else if (strncmp(str, ":reset", 6) == 0) {
if (!init_repl()) { if (!init_repl()) {
printf ("Failed to initialize REPL\n"); printf ("Failed to initialize REPL\n");
return -1; terminate_repl(REPL_EXIT_UNABLE_TO_INIT_LBM);
} }
free(str); free(str);
} else if (strncmp(str, ":send", 5) == 0) { } else if (strncmp(str, ":send", 5) == 0) {
@ -957,5 +952,5 @@ int main(int argc, char **argv) {
} }
} }
free(heap_storage); free(heap_storage);
return 0; terminate_repl(REPL_EXIT_SUCCESS);
} }

72
repl/repl_defines.h Normal file
View File

@ -0,0 +1,72 @@
/*
Copyright 2024 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
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef REPL_DEFINES_H_
#define REPL_DEFINES_H_
#define REPL_EXIT_SUCCESS 0
#define REPL_EXIT_OUT_OF_MEMORY_WHILE_FLATTENING 1
#define REPL_EXIT_FLATTENING_MAXIMUM_DEPTH 2
#define REPL_EXIT_CIRCULAR_VALUE 3
#define REPL_EXIT_FATAL_ERROR_WHILE_FLATTENING 4
#define REPL_EXIT_FLAT_VALUE_BUFFER_TOO_SMALL 5
#define REPL_EXIT_VALUE_CANNOT_BE_FLATTENED 6
#define REPL_EXIT_INVALID_KEY_IN_ENVIRONMENT 7
#define REPL_EXIT_LBM_EVENT_QUEUE_FULL 8
#define REPL_EXIT_INVALID_VALUE_IN_ENV_FILE 9
#define REPL_EXIT_OUT_OF_MEMORY_WHILE_PROCESSING_ENV_FILE 10
#define REPL_EXIT_INVALID_VALUE_SIZE_IN_ENV_FILE 11
#define REPL_EXIT_INVALID_KEY_IN_ENV_FILE 12
#define REPL_EXIT_ZERO_LENGTH_KEY_IN_ENV_FILE 13
#define REPL_EXIT_INVALID_ENV_FILE 14
#define REPL_EXIT_UNABLE_TO_OPEN_ENV_FILE 15
#define REPL_EXIT_UNABLE_TO_INIT_LBM 16
#define REPL_EXIT_ERROR_FLATTEN_NO_MEM 17
#define REPL_EXIT_UNABLE_TO_CREATE_SYMBOL 18
#define REPL_EXIT_INVALID_SOURCE_FILE 19
#define REPL_EXIT_UNABLE_TO_ACCESS_SYMBOL_STRING 20
#define REPL_EXIT_CRITICAL_ERROR 21
const char *repl_exit_message[22] =
{"Goodbye.",
"ERROR: Out of memory while flattening a value.",
"ERROR: Maximum cons depth reached while flattening value.",
"ERROR: Possibly circular value found while flattening.",
"ERROR: Fatal error while flattening value.",
"ERROR: Buffer for flat value is too small.",
"ERROR: Value cannot be flattened.",
"ERROR: An invalid key was found in the environment.",
"ERROR: The event queue is full.",
"ERROR: Invalid value found in environment file.",
"ERROR: Out of memory while processing environment file.",
"ERROR: Invalid value size found in environment file.",
"ERROR: Invalid key found in environment file.",
"ERROR: Zero length key found in environment file.",
"ERROR: Invalid environment file.",
"ERROR: Unable to open environment file.",
"ERROR: Unable to initialize the LispBM runtime system.",
"ERROR: Unable to allocate memory for flat value creation.",
"ERROR: Unable to create a symbol.",
"ERROR: Invalid source file.",
"ERROR: Unable to access symbol string representation.",
"ERROR: Critical Error"
};
#endif

View File

@ -322,6 +322,16 @@ static lbm_value ext_systime(lbm_value *args, lbm_uint argn) {
return lbm_enc_u32(time); return lbm_enc_u32(time);
} }
static lbm_value ext_secs_since(lbm_value *args, lbm_uint argn) {
uint32_t t_now = timestamp();
if (argn != 1 || !lbm_is_number(args[0])) return ENC_SYM_EERROR;
uint32_t t_then = lbm_dec_as_u32(args[0]);
uint32_t diff = t_now - t_then;
return lbm_enc_float((float)diff / 1000000.0f);
}
// Init // Init
int init_exts(void) { int init_exts(void) {
@ -340,6 +350,7 @@ int init_exts(void) {
} }
lbm_add_extension("systime", ext_systime); lbm_add_extension("systime", ext_systime);
lbm_add_extension("secs-since", ext_secs_since);
// Math // Math
lbm_add_extension("rand", ext_rand); lbm_add_extension("rand", ext_rand);