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
|
@ -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");
|
||||||
|
|
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 16 KiB |
259
doc/lbmref.md
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
101
repl/repl.c
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
@ -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);
|
||||||
|
|