Espruino Interpreter Internals
Please see the Performance section first for a rough overview, and for the practical implications of the implementation.
Note: There's also a forum thread with some more in-depth answers to questions about the interpreter.
Compilation
Espruino's Makefile calls a few different Python scripts that precompute various things:
- Parse information on the board and chip, and create:
- A
platform_config
file - with information about the current system (how many of each peripheral, what pin buttons + LEDs are on, etc) - A
pininfo
source file - listing what peripherals are on what pins - Board Documentation (if required)
- A
- Parse all the
jswrap_
files (looking for JSON-formatted comments above each function), and create:- A
jswrapper.c
file containing a symbol table and calls to the functions that need to be executed - API Documentation (if required)
- A
Parsing
There is no bytecode, so execution happens inside the parser in jsparse.c
The parser is a hand-written recursive-descent parser, and code to be executed is executed directly from variables (see below), not from a C-style string or flat buffer.
Variable Storage
Variables are usually stored in fixed-size 16 byte blocks. In small devices this can get down to 12 bytes (10 bit addresses) and on PCs it's 32 bytes (32 bit addresses).
This has several implications:
- Free variables are stored in a linked list, so memory allocation and deallocation is
O(1)
- Garbage collection passes are fast
- Memory fragmentation is not a problem
- Malloc would generally have a 4 byte allocation overhead for each memory block - we can do without this.
Each block has optional links to children and siblings, which allows a tree structure to be built. However in many cases these references aren't needed and can be used to store other data.
What follows are the basic variable bytes. Note that NAME_INT_INT, NAME_INT_BOOL, and NAME_STRING_INT follow the same pattern as NAME_STR/NAME_INT - they just use child
to store a value rather than a reference:
16 byte JsVars
( > 1024 variables, JsVars for 32 bit refs are similar )
Offset | Name | STRING | STR_EXT | NAME_STR | NAME_INT | INT | DOUBLE | OBJ/FUNC/ARRAY | ARRAYBUFFER |
---|---|---|---|---|---|---|---|---|---|
0 - 3 | varData | data | data | data | data | data | data | nativePtr | size |
4 - 5 | next | data | data | next | next | - | data | argTypes | format |
6 - 7 | prev | data | data | prev | prev | - | data | argTypes | format |
8 - 9 | first | data | data | child | child | - | - | first | stringPtr |
10-11 | refs | refs | data | refs | refs | refs | refs | refs | refs |
12-13 | last | nextPtr | nextPtr | nextPtr | - | - | - | last | - |
14-15 | Flags | Flags | Flags | Flags | Flags | Flags | Flags | Flags | Flags |
12 byte JsVars
( where < 1024 variables )
10 bit addresses are used, with the extra bits being stored in a field called pack
and the flags
variable
Offset | Name | STRING | STR_EXT | NAME_STR | NAME_INT | INT | DOUBLE | OBJ/FUNC/ARRAY | ARRAYBUFFER |
---|---|---|---|---|---|---|---|---|---|
0 - 3 | varData | data | data | data | data | data | data | nativePtr | size |
4 | next | data | data | next | next | - | data | argTypes | format |
5 | prev | data | data | prev | prev | - | data | argTypes | format |
6 | first | data | data | child | child | - | data | first | stringPtr |
7 | pack | pack | data | pack | pack | pack | data | pack | pack |
8 | refs | refs | data | refs | refs | refs | refs | refs | refs |
9 | last | nextPtr | nextPtr | nextPtr | - | - | - | last | - |
10-11 | Flags | Flags | Flags | Flags | Flags | Flags | Flags | Flags | Flags |
This page is auto-generated from GitHub. If you see any mistakes or have suggestions, please let us know.