├── LICENSE ├── README.md ├── doc ├── api.md ├── example.md ├── lt.md └── ltstd.md ├── main.c ├── scripts ├── demo.little └── module.little └── src ├── little.c ├── little.h ├── little_std.c └── little_std.h /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Beariish 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # little - tiny bytecode language 2 | little is a _small_, _fast_, _easily embeddable_ language implemented in C. 3 | 4 | --- 5 | ```js 6 | var speak = fn(animal) { 7 | if animal is "cat" { return "meow" } 8 | elseif animal is "dog" { return "woof" } 9 | elseif animal is "mouse" { return "squeak" } 10 | return "???" 11 | } 12 | 13 | var animals = [ "cat", "dog", "mouse", "monkey" ] 14 | for animal in array.each(animals) { 15 | io.print(string.format("%s says %s!", animal, speak(animal))) 16 | } 17 | ``` 18 | --- 19 | ## Feature Overview 20 | * Tiny implementation - core langauge is <2500 sloc in a single .h/.c pair 21 | * Light embedding - compiles down to less than 20kb, 3 API calls to get started 22 | * Reasonably fast for realtime applications 23 | * Low memory footprint with simple mark and sweep garbage collector 24 | * Supports null, numbers, booleans, strings, functions, closures, arrays, tables, and native procedures 25 | * Optional, consise stdlib - an extra ~1000 sloc 26 | * Supports 32- and 64-bit, and will likely compile anywhere! 27 | * Feature-rich C api to integrate and interact with the VM 28 | --- 29 | ## Simple embedding example 30 | ```c 31 | #include "little.h" 32 | #include "little_std.h" 33 | 34 | // this is called if the vm encounters an error, letting us react 35 | void my_error_callback(lt_VM* vm, const char* msg) 36 | { 37 | printf("LT ERROR: %s\n", msg); 38 | } 39 | 40 | int main(char** argv, int argc) 41 | { 42 | lt_VM* vm = lt_open(malloc, free, my_error_callback); // open new VM 43 | ltstd_open_all(vm); // register stdlib 44 | 45 | const char* my_source_code = ... // read source from file/stream/string 46 | 47 | uint16_t n_return = lt_dostring(vm, my_source_code, "my_module") // run code as "my_module" 48 | if(n_return) printf("LT RETURNED: %s", ltstd_tostring(vm, lt_pop(vm))); // if our code returns, print the result 49 | } 50 | ``` 51 | --- 52 | ## Links 53 | * **[Language overview](doc/lt.md)** 54 | * **[Standard library](doc/ltstd.md)** 55 | * **[C API reference](doc/api.md)** 56 | * **[C API examples](doc/example.md)** 57 | --- 58 | ## Known issues 59 | * Recursion is broken, sorry 60 | * just write better code 61 | 62 | --- 63 | ## Potential improvements 64 | * Drop the AST for single pass compiler 65 | * but only if it actually makes the impl smaller 66 | * precomputed goto/jumptable vm instruction dispatch 67 | * copy fewer strings probably 68 | 69 | --- 70 | ## Contribution 71 | Feel free to open an issue or pull request if you feel you have something meaninfgul to add, but keep in mind the language is minimalist by design, so any merging will be very carefully picked 72 | 73 | --- 74 | ## License 75 | Please see [LICENSE](LICENSE) for details -------------------------------------------------------------------------------- /doc/api.md: -------------------------------------------------------------------------------- 1 | # little - C API overview 2 | 3 | ## VM manipulation 4 | ```c 5 | lt_VM* lt_open(lt_AllocFn, lt_FreeFn, lt_ErrorFn); 6 | ``` 7 | Creates a new little VM, allocating itself with the `lt_AllocFn` provided. This and `lt_FreeFn` have the same signatures as `malloc` and `free`, so they make for good defaults. 8 | 9 | `lt_ErrorFn` has the signature `void (*lt_ErrorFn)(lt_VM* vm, const char* message)`, and is called whenever the VM encounters an error. `0` can be passed if desired. 10 | 11 | The defines `LT_STACK_SIZE 256`, `LT_CALLSTACK_SIZE 32` and `LT_DEDUP_TABLE_SIZE 64` can be set prior to including `little.h` to configure VM internals. 12 | 13 | Additionally, the `vm->generate_debug` flag can be set to `0` to disable the generation of debug symbols for traceback, saving some memory. 14 | 15 | --- 16 | ```c 17 | void lt_destroy(lt_VM*); 18 | ``` 19 | Destroys the VM, clearing the keepalive list, collecting all objects, and freeing it with the function provided when opened. 20 | 21 | --- 22 | ```c 23 | void lt_nocollect(lt_VM*, lt_Object*); 24 | ``` 25 | Adds object to the VM's root object set, preventing it from being collected. 26 | 27 | --- 28 | ```c 29 | void lt_resumecollect(lt_VM*, lt_Object*); 30 | ``` 31 | Removes the object from the root set, resuming collection for it. 32 | 33 | --- 34 | ```c 35 | uint32_t lt_collect(lt_VM*); 36 | ``` 37 | Perform a mark-and-sweep collection pass over the VM's heap. 38 | 39 | --- 40 | ```c 41 | void lt_push(lt_VM*, lt_Value); 42 | ``` 43 | Pushes a value to the VM's stack. 44 | 45 | --- 46 | ```c 47 | lt_Value lt_pop(lt_VM*); 48 | ``` 49 | Pops the top value from the VM's stack to the caller 50 | 51 | --- 52 | ```c 53 | lt_Value lt_at(lt_VM*, uint16_t); 54 | ``` 55 | Returns the N'th element of the current stack frame, useful for arg handling in C. 56 | 57 | --- 58 | ```c 59 | void lt_close(lt_VM*, uint8_t); 60 | ``` 61 | Captures and closes over N value on the stack, followed by a function, and pushes a closure to it. 62 | 63 | --- 64 | ```c 65 | lt_Value lt_getupval(lt_VM*, uint8_t); 66 | ``` 67 | Returns the N'th upvalue in the current execution frame. 68 | 69 | --- 70 | ```c 71 | void lt_setupval(lt_VM*, uint8_t, lt_Value); 72 | ``` 73 | Sets the N'th upvalue in the current execution frame. 74 | 75 | --- 76 | ## Execution 77 | 78 | ```c 79 | lt_Value lt_loadstring(lt_VM* vm, const char* source, const char* mod_name); 80 | ``` 81 | Tokenizes, parses, and compiles the source string, passing `mod_name` for debug purposes, and returns the resulting callable chunk. 82 | 83 | --- 84 | ```c 85 | uint32_t lt_dostring(lt_VM* vm, const char* source, const char* mod_name); 86 | ``` 87 | Tokenizes, parses, and compiles the source string, passing `mod_name` for debug purposes. Then executes the resulting chunk, and returns the number of values returns onto the VM stack. 88 | 89 | --- 90 | ```c 91 | lt_Tokenizer lt_tokenize(lt_VM* vm, const char* source, const char* mod_name); 92 | ``` 93 | Tokenizes the passed source string, with `mod_name` for debug purposes, and returns the resulting tokenizer. 94 | 95 | --- 96 | ```c 97 | lt_Parser lt_parse(lt_VM* vm, lt_Tokenizer* tkn); 98 | ``` 99 | Parses the source string tokenized within `tkn`, and returns the resulting parse tree. 100 | 101 | --- 102 | ```c 103 | lt_Value lt_compile(lt_VM* vm, lt_Parser* p); 104 | ``` 105 | Compiles a parse tree into bytecode, returning the resulting callable. 106 | 107 | --- 108 | ```c 109 | void lt_free_parser(lt_VM* vm, lt_Parser* p); 110 | ``` 111 | Destroy a parser and free its memory. 112 | 113 | --- 114 | ```c 115 | void lt_free_tokenizer(lt_VM* vm, lt_Tokenizer* tok); 116 | ``` 117 | Destroy a tokenizer and free its memory. 118 | 119 | --- 120 | ## Error handling 121 | ```c 122 | void lt_error(lt_VM* vm, const char* msg); 123 | ``` 124 | Halts tokenizing, parsing, or executation and calls the VM's error callback with msg. 125 | 126 | --- 127 | ```c 128 | void lt_runtime_error(lt_VM* vm, const char* msg); 129 | ``` 130 | Halts execution, and calls error callback with msg, formatted into a callstack for debugging. 131 | This is the preferred method for native errors. 132 | 133 | --- 134 | ## Value manipulation 135 | 136 | The `LT_IS_NULL(x)`, `LT_IS_NUMBER(x)`, `LT_IS_BOOL(x)`, `LT_IS_TRUE(x)`, `LT_IS_FALSE(x)`, `LT_IS_TRUTHY(x)`, `LT_IS_STRING(x)`, `LT_IS_OBJECT(x)`, `LT_IS_TABLE(x)`, `LT_IS_ARRAY(x)`, `LT_IS_FUNCTION(x)`, `LT_IS_CLOSURE(x)`, `LT_IS_NATIVE(x)`, and `LT_IS_PTR(x)` macros exist to test the type of any given value `x`. 137 | 138 | `LT_VALUE_NULL`, `LT_VALUE_FALSE`, and `LT_VALUE_TRUE` are defined as constants. 139 | 140 | `LT_VALUE_OBJECT(x)` and `LT_GET_OBJECT(x)` exist to help bit manipulate pointers to objects. 141 | 142 | --- 143 | The following methods exist to create values of each type: 144 | ```c 145 | lt_Value lt_make_number(double n); 146 | lt_Value lt_make_string(lt_VM* vm, const char* string); 147 | lt_Value lt_make_table(lt_VM* vm); 148 | lt_Value lt_make_array(lt_VM* vm); 149 | lt_Value lt_make_native(lt_VM* vm, lt_NativeFn fn); 150 | lt_Value lt_make_ptr(lt_VM* vm, void* ptr); 151 | ``` 152 | 153 | Some values can be easily retrieved as well: 154 | ```c 155 | double lt_get_number(lt_Value v); 156 | const char* lt_get_string(lt_VM* vm, lt_Value value); 157 | void* lt_get_ptr(lt_Value ptr); 158 | ``` 159 | 160 | --- 161 | Tables can be manipulated with: 162 | ```c 163 | lt_Value lt_table_set(lt_VM* vm, lt_Value table, lt_Value key, lt_Value val); 164 | lt_Value lt_table_get(lt_VM* vm, lt_Value table, lt_Value key); 165 | uint8_t lt_table_pop(lt_VM* vm, lt_Value table, lt_Value key); 166 | ``` 167 | 168 | --- 169 | Arrays can be manipulated with: 170 | ```c 171 | lt_Value lt_array_push(lt_VM* vm, lt_Value array, lt_Value val); 172 | lt_Value* lt_array_at(lt_Value array, uint32_t idx); 173 | lt_Value lt_array_remove(lt_VM* vm, lt_Value array, uint32_t idx); 174 | uint32_t lt_array_length(lt_Value array); 175 | ``` 176 | 177 | --- 178 | And finally, 179 | ```c 180 | uint8_t lt_equals(lt_Value a, lt_Value b); 181 | ``` 182 | can be used to test two values for equality. -------------------------------------------------------------------------------- /doc/example.md: -------------------------------------------------------------------------------- 1 | # C API examples 2 | 3 | Here are some samples of how to extend the language through the C API. For brevity, these all assume you've already opened a `lt_VM*` by the name vm. 4 | 5 | ## Native function binding 6 | ```c 7 | uint8_t my_add(lt_VM* vm, uint8_t argc) 8 | { 9 | if(argc != 2) lt_runtime_error(vm, "Expected two arguments!"); 10 | lt_Value right = lt_pop(vm); 11 | lt_Value left = lt_pop(vm); 12 | 13 | if(!LT_IS_NUMBER(left) || !LT_IS_NUMBER(right)) 14 | lt_runtime_error(vm, "Invalid types!"); 15 | 16 | lt_push(vm, lt_make_number(lt_get_number(left) + lt_get_number(right)); 17 | return 1; // we have pushed to the stack! 18 | } 19 | 20 | lt_table_set(vm, vm->global, lt_make_string(vm, "add"), lt_make_native(vm, my_add)); 21 | ``` 22 | 23 | ## Native module binding 24 | 25 | ```c 26 | lt_Value my_module = lt_make_table(vm); 27 | 28 | lt_table_set(vm, my_module, lt_make_string(vm, "func1"), lt_make_native(vm, my_func1)); 29 | lt_table_set(vm, my_module, lt_make_string(vm, "func2"), lt_make_native(vm, my_func2)); 30 | lt_table_set(vm, my_module, lt_make_string(vm, "func3"), lt_make_native(vm, my_func3)); 31 | 32 | lt_table_set(vm, vm->global, lt_make_string(vm, "module"), my_module); 33 | ``` -------------------------------------------------------------------------------- /doc/lt.md: -------------------------------------------------------------------------------- 1 | # little - language overview 2 | ## Types 3 | Little supports a set of basic types: 4 | * `null` - represents the absense of a value 5 | * `number` - a double-precision floating point number 6 | * `boolean` - either true or false 7 | * `string` - a reference to an immutable string 8 | * `function` - a little-defined function 9 | * `closure` - any function that captures surrounding values 10 | * `array` - 0-indexed array of values 11 | * `table` - a table of key-value pairs 12 | * `native` - reference to a natively defined C function 13 | * `ptr` - userdata pointer set by C api 14 | 15 | These are grouped into `Value` and `Object` types, which are passed by value and reference respectively 16 | `null`, `number`, `boolean`, and `string` are the `Value` types. String is special in that they are immutable and stored in a global deduplication table, and the actual value passed around is an index into that. 17 | 18 | --- 19 | 20 | ## Language statements 21 | ### var 22 | ```js 23 | var a 24 | var b = 10 25 | var c = (a or 10) + b 26 | ``` 27 | Variables are declared with the `var` keyword. Only a single name is permitted per `var` statement, with an optional expression following the `=` assignment operator. 28 | 29 | --- 30 | ### if 31 | ```js 32 | if a is 100 { ... } 33 | elseif a is 150 { ... } 34 | elseif b is a { ... } 35 | else { ... } 36 | ``` 37 | Branching is done with the `if` statement, followed by an expression to evaluate and then a mandatory set of braces, containing the body to execute. `if`s can be followed by any number of `elseif` statements, and optionally a final `else`statement, 38 | 39 | --- 40 | ### for 41 | ```js 42 | var a = [ 100, 200, 300 ] 43 | for item in array.each(a) { ... } 44 | ``` 45 | `for` loops come in only one flavour in little, requiring a single identifier to be the loop variable, and an expression that evaluates into an iterator function. It will be repeatedly called - and it's result stored in the loop variable - until it evaluates to null. 46 | 47 | --- 48 | ### while 49 | ```js 50 | var a = 0 51 | while a < 10 { a = a + 1 } 52 | ``` 53 | `while` loops continually evaluate their condition and execute their bodies. 54 | 55 | --- 56 | ### break 57 | ```js 58 | while true { break } 59 | ``` 60 | `break` exits a loop early. 61 | 62 | --- 63 | ### return 64 | ```js 65 | return "any expression!" 66 | ``` 67 | `return` exits the current execution frame, and returns a single value to the caller. 68 | 69 | --- 70 | ### assignment 71 | ```js 72 | var a = 10 73 | a = 20 74 | ``` 75 | Any identifier followed by `=` assignment. 76 | 77 | --- 78 | Any top-level statement that doesn't match any of these is instead executed as an `expression` 79 | 80 | --- 81 | ## Language expressions 82 | Expressions consist of all literals and operators. 83 | 84 | --- 85 | ### Literals 86 | * `null` is both a type and a literal value 87 | * `number` literals are any decimal number strings - `123`, `0.5`, `123.123` etc 88 | * `boolean` literals are either `true` or `false` 89 | * `string` literals are any double-quoted strings - `"hello world!"`, `"i love apples"` 90 | * `array` literals are a list of values between brackets - `[ 1, true, null, "banana" ]` 91 | * `table` literals are `key: value` pairs grouped between braces - `{ a: 10 b: 20 c: true }` 92 | * `function` literals are declared with this syntax: `var my_fn = fn(a, b) { return a + b }` 93 | * They are first-class objects, and can only be stored through assignment 94 | * Can be trivially passed as parameters as well 95 | * Parameter list is mandatory, even if empty 96 | ### Operators 97 | The mathematical operators `+`, `-`, `*`, and `/` only work on `number` values 98 | The comparison operators `<`, `<=`, `>`, `>=` also only work with `number`s 99 | The comparison operators `is` and `isnt` work on all types 100 | The logical operators `or`, `and` and `not` compare values based on their `truthiness`, and return their last operand 101 | The index operator `[expression]` works on any `table` and `array` values 102 | The dot operator `.` is syntax sugar for indexing `table`s - `my_table.my_index = 10` 103 | 104 | ### Truthiness 105 | Any `null` or `false` values are considered `falsy`, anything else is logically `true` 106 | -------------------------------------------------------------------------------- /doc/ltstd.md: -------------------------------------------------------------------------------- 1 | # little_std 2 | The little stdlib is divided into a few modules. These can all be loaded separately if desired 3 | 4 | ## io 5 | `io.print(...)` is a printf wrapper that evaluates each argument, `ltstd_tostring`s them, and prints. 6 | `io.clock()` returns the current execution time of the program, in seconds. About millisecond accurate. 7 | 8 | ## math 9 | `math.sin(x)`, `math.cos(x)`, `math.tan(x)`, `math.asin(x)`, `math.acos(x)`, `math.atan(x)`, `math.sinh(x)`, `math.cosh(x)`, `math.tanh(x)`, `math.floor(x)`, `math.ceil(x)`, `math.round(x)`, `math.exp(x)`, `math.log(x)`, `math.log10(x)`, `math.sqrt(x)`, `math.abs(x)`, `math.min(a, b)`, `math.max(a, b)`, `math.pow(a, b)`, and `math.mod(a, b)` are all very simple wrappers around their `math.h` equivalents. 10 | 11 | `math.pi` and `math.e` also both exist as constants. 12 | 13 | ## array 14 | `array.each(x)` returns an iterator function that returns each element in order. 15 | `array.range([start,] end [, step])` returns an iterator function that produces a sequence of numbers. 16 | `array.len(x)` returns the length of an array. 17 | `array.last(x)` returns the last element of an array. 18 | `array.pop(x)` removes, and then returns the last element of an array. 19 | `array.push(array, element)` adds `element` to the back of `array`. 20 | `array.remove(array, index)` cyclicly removes the element at `index`. This does not preserve order. 21 | 22 | ## string 23 | `string.from(x)` converts argument into a string representation. 24 | `string.concat(...)` concatenates arguments in order. 25 | `string.len(x)` returns the length of `x`. 26 | `string.sub(str, start [, length])` creates a substring of `str`, `length` is the remainder of the string if left out. 27 | `string.format(format, ...)` takes a printf-style format string and a list of arguments to insert. 28 | 29 | ## gc 30 | 31 | `gc.collect()` performs a collection sweep, and returns the number of objects freed. 32 | `gc.addroot(x)` adds object `x` to the gc's rootset, preventing it and everything it references from being collected. 33 | `gc.removeroot(x)` removes `x` from the rootset. -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "src/little.h" 5 | #include "src/little_std.h" 6 | 7 | void error(lt_VM* vm, const char* msg) 8 | { 9 | printf("LT ERROR: %s\n", msg); 10 | } 11 | 12 | int main(int argc, char** argv) 13 | { 14 | // Load program 15 | if (argc != 2) 16 | { 17 | printf("Usage: little FILENAME\n"); 18 | return 0; 19 | } 20 | FILE *fp = fopen(argv[1], "rb"); 21 | if (!fp) 22 | { 23 | printf("ERROR: Failed to open '%s'\n", argv[1]); 24 | return 0; 25 | } 26 | static char text[1 << 20]; 27 | fread(text, 1, sizeof(text), fp); 28 | fclose(fp); 29 | 30 | // Init VM and run program 31 | lt_VM* vm = lt_open(malloc, free, error); 32 | ltstd_open_all(vm); 33 | 34 | uint32_t nreturn = lt_dostring(vm, text, "module"); 35 | 36 | while (nreturn-- > 0) 37 | { 38 | printf("Returned: %s\n", ltstd_tostring(vm, lt_pop(vm))); 39 | } 40 | 41 | lt_destroy(vm); 42 | 43 | return 0; 44 | } -------------------------------------------------------------------------------- /scripts/demo.little: -------------------------------------------------------------------------------- 1 | 2 | var speak = fn(animal) { 3 | if animal is "cat" { return "meow" } 4 | elseif animal is "dog" { return "woof" } 5 | elseif animal is "mouse" { return "squeak" } 6 | return "???" 7 | } 8 | 9 | var animals = [ "cat", "dog", "mouse", "monkey" ] 10 | for animal in array.each(animals) { 11 | io.print(string.format("%s says %s!", animal, speak(animal))) 12 | } 13 | 14 | var module = io.require("scripts/module.little") 15 | module("Called from another file!") 16 | -------------------------------------------------------------------------------- /scripts/module.little: -------------------------------------------------------------------------------- 1 | io.print("This should only appear once!") 2 | 3 | return fn(x) { 4 | io.print("Hello I am ", x) 5 | } -------------------------------------------------------------------------------- /src/little.c: -------------------------------------------------------------------------------- 1 | #include "little.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static lt_Value LT_NULL = LT_VALUE_NULL; 10 | 11 | typedef struct { 12 | uint64_t hash; 13 | char* string; 14 | lt_Value value; 15 | uint32_t refcount; 16 | uint32_t len; 17 | } lt_StringDedupEntry; 18 | 19 | typedef enum { 20 | LT_OP_NOP, 21 | 22 | LT_OP_PUSH, LT_OP_DUP, 23 | 24 | LT_OP_PUSHS, LT_OP_PUSHC, LT_OP_PUSHN, LT_OP_PUSHT, LT_OP_PUSHF, 25 | 26 | LT_OP_ADD, LT_OP_SUB, LT_OP_MUL, LT_OP_DIV, LT_OP_NEG, 27 | LT_OP_EQ, LT_OP_NEQ, LT_OP_GT, LT_OP_GTE, 28 | LT_OP_AND, LT_OP_OR, LT_OP_NOT, 29 | 30 | LT_OP_LOAD, LT_OP_STORE, 31 | LT_OP_LOADUP, LT_OP_STOREUP, 32 | 33 | LT_OP_CLOSE, LT_OP_CALL, 34 | 35 | LT_OP_MAKET, LT_OP_MAKEA, LT_OP_SETT, LT_OP_GETT, LT_OP_GETG, 36 | 37 | LT_OP_JMP, LT_OP_JMPC, LT_OP_JMPN, 38 | 39 | LT_OP_RET, 40 | } lt_OpCode; 41 | 42 | uint64_t static MurmurOAAT64(const char* key) 43 | { 44 | uint64_t h = 525201411107845655ull; 45 | for (; *key; ++key) { 46 | h ^= *key; 47 | h *= 0x5bd1e9955bd1e995; 48 | h ^= h >> 47; 49 | } 50 | return h; 51 | } 52 | 53 | typedef union { 54 | double flt; 55 | uint64_t bits; 56 | } _lt_conversion_union; 57 | 58 | lt_Buffer lt_buffer_new(uint32_t element_size) 59 | { 60 | lt_Buffer buf; 61 | buf.element_size = element_size; 62 | buf.capacity = 0; 63 | buf.length = 0; 64 | buf.data = 0; 65 | 66 | return buf; 67 | } 68 | 69 | void lt_buffer_destroy(lt_VM* vm, lt_Buffer* buf) 70 | { 71 | if (buf->data != 0) vm->free(buf->data); 72 | buf->data = 0; 73 | buf->length = 0; 74 | buf->capacity = 0; 75 | } 76 | 77 | static uint8_t lt_buffer_push(lt_VM* vm, lt_Buffer* buf, void* element) 78 | { 79 | uint8_t has_allocated = 0; 80 | if (buf->length + 1 > buf->capacity) 81 | { 82 | has_allocated = 1; 83 | 84 | void* new_buffer = vm->alloc(buf->element_size * (buf->capacity + 16)); 85 | 86 | if (buf->data != 0) 87 | { 88 | memcpy(new_buffer, buf->data, buf->element_size * buf->capacity); 89 | free(buf->data); 90 | } 91 | 92 | buf->data = new_buffer; 93 | buf->capacity += 16; 94 | } 95 | 96 | memcpy((uint8_t*)buf->data + buf->element_size * buf->length, element, buf->element_size); 97 | buf->length++; 98 | 99 | return has_allocated; 100 | } 101 | 102 | static void* lt_buffer_at(lt_Buffer* buf, uint32_t idx) 103 | { 104 | return (uint8_t*)buf->data + buf->element_size * idx; 105 | } 106 | 107 | static void* lt_buffer_last(lt_Buffer* buf) 108 | { 109 | return lt_buffer_at(buf, buf->length - 1); 110 | } 111 | 112 | static void lt_buffer_cycle(lt_Buffer* buf, uint32_t idx) 113 | { 114 | memcpy(lt_buffer_at(buf, idx), lt_buffer_last(buf), buf->element_size); 115 | buf->length--; 116 | } 117 | 118 | static void lt_buffer_pop(lt_Buffer* buf) 119 | { 120 | buf->length--; 121 | } 122 | 123 | lt_Value lt_make_number(double n) 124 | { 125 | _lt_conversion_union u; 126 | u.flt = n; 127 | return (lt_Value)u.bits; 128 | } 129 | 130 | double lt_get_number(lt_Value v) 131 | { 132 | _lt_conversion_union u; 133 | u.bits = v; 134 | return (double)u.flt; 135 | } 136 | 137 | #define VALTONUM(x) ((union { uint64_t u; double n; }) { x }.n) 138 | 139 | static void _lt_tokenize_error(lt_VM* vm, const char* module, uint16_t line, uint16_t col, const char* message) 140 | { 141 | char sprint_buf[128]; 142 | sprintf_s(sprint_buf, 128, "%s|%d:%d: %s", module, line, col, message); 143 | lt_error(vm, sprint_buf); 144 | } 145 | 146 | static void _lt_parse_error(lt_VM* vm, const char* module, lt_Token* t, const char* message) 147 | { 148 | char sprint_buf[128]; 149 | sprintf_s(sprint_buf, 128, "%s|%d:%d: %s", module, t->line, t->col, message); 150 | lt_error(vm, sprint_buf); 151 | } 152 | 153 | static lt_DebugInfo* _lt_get_debuginfo(lt_Object* obj) 154 | { 155 | switch (obj->type) 156 | { 157 | case LT_OBJECT_CHUNK: return obj->chunk.debug; 158 | case LT_OBJECT_FN: return obj->fn.debug; 159 | case LT_OBJECT_CLOSURE: return LT_GET_OBJECT(obj->closure.function)->fn.debug; 160 | } 161 | 162 | return 0; 163 | } 164 | 165 | static lt_DebugLoc _lt_get_location(lt_DebugInfo* info, uint32_t pc) 166 | { 167 | if (info) return *(lt_DebugLoc*)lt_buffer_at(&info->locations, pc); 168 | return (lt_DebugLoc){ 0, 0 }; 169 | } 170 | 171 | void lt_runtime_error(lt_VM* vm, const char* message) 172 | { 173 | char sprint_buf[1024]; 174 | 175 | lt_Frame* topmost = &vm->callstack[vm->depth - 1]; 176 | lt_DebugInfo* info = _lt_get_debuginfo(topmost->callee); 177 | lt_DebugLoc loc = _lt_get_location(info, topmost->ip - (lt_Op*)topmost->code->data); 178 | 179 | const char* name = ""; 180 | if (info) name = info->module_name; 181 | 182 | uint32_t len = sprintf_s(sprint_buf, 1024, "%s|%d:%d: %s\ntraceback:", name, loc.line, loc.col, message); 183 | for (uint32_t i = vm->depth - 1; i >= 0; --i) 184 | { 185 | lt_Frame* frame = &vm->callstack[i]; 186 | lt_DebugInfo* info = _lt_get_debuginfo(frame->callee); 187 | lt_DebugLoc loc = _lt_get_location(info, frame->ip - (lt_Op*)frame->code->data); 188 | 189 | const char* name = ""; 190 | if (info) name = info->module_name; 191 | len = sprintf_s(sprint_buf + len, 1024 - len, "\n(%s|%d:%d)", name, loc.line, loc.col); 192 | } 193 | 194 | lt_error(vm, sprint_buf); 195 | } 196 | 197 | lt_Value lt_make_string(lt_VM* vm, const char* string) 198 | { 199 | uint32_t len = (uint32_t)strlen(string); 200 | uint64_t hash = MurmurOAAT64(string); 201 | uint16_t bucket = hash % LT_DEDUP_TABLE_SIZE; 202 | 203 | lt_Buffer* buf = vm->strings + bucket; 204 | if (buf->element_size == 0) *buf = lt_buffer_new(sizeof(lt_StringDedupEntry)); 205 | 206 | int32_t first_empty = -1; 207 | for (uint32_t i = 0; i < buf->length; i++) 208 | { 209 | lt_StringDedupEntry* entry = lt_buffer_at(buf, i); 210 | if (entry->hash == hash) 211 | { 212 | return entry->value; 213 | } 214 | else if (entry->hash == 0 && first_empty == -1) first_empty = i; 215 | } 216 | 217 | lt_StringDedupEntry new_entry; 218 | new_entry.hash = hash; 219 | new_entry.len = len; 220 | new_entry.refcount = 0; 221 | 222 | new_entry.string = vm->alloc(len + 1); 223 | memcpy(new_entry.string, string, len); 224 | new_entry.string[len] = 0; 225 | 226 | uint32_t index = 0; 227 | if (first_empty != -1) 228 | { 229 | index = first_empty; 230 | lt_StringDedupEntry* e = lt_buffer_at(buf, first_empty); 231 | new_entry.value = (LT_NAN_MASK | LT_TYPE_STRING) | (bucket << 24) | (index & 0xFFFFFF); 232 | memcpy(e, &new_entry, sizeof(lt_StringDedupEntry)); 233 | return new_entry.value; 234 | } 235 | else 236 | { 237 | lt_buffer_push(vm, buf, &new_entry); 238 | lt_StringDedupEntry* e = lt_buffer_at(buf, buf->length - 1); 239 | index = buf->length - 1; 240 | e->value = (LT_NAN_MASK | LT_TYPE_STRING) | (bucket << 24) | (index & 0xFFFFFF); 241 | return e->value; 242 | } 243 | } 244 | 245 | const char* lt_get_string(lt_VM* vm, lt_Value value) 246 | { 247 | uint16_t bucket = (uint16_t)((value & 0xFFFFFF000000) >> 24); 248 | uint32_t index = value & 0xFFFFFF; 249 | return ((lt_StringDedupEntry*)lt_buffer_at(vm->strings + bucket, index))->string; 250 | } 251 | 252 | static void _lt_reference_string(lt_VM* vm, lt_Value value) 253 | { 254 | uint16_t bucket = (uint16_t)((value & 0xFFFFFF000000) >> 24); 255 | uint32_t index = value & 0xFFFFFF; 256 | ((lt_StringDedupEntry*)lt_buffer_at(vm->strings + bucket, index))->refcount++; 257 | } 258 | 259 | static uint8_t faststrcmp(const char* a, uint64_t a_len, const char* b, uint64_t b_len) 260 | { 261 | if (a_len != b_len) return 0; 262 | for (int i = 0; i < a_len; ++i) 263 | { 264 | if (*(a + i) != *(b + i)) return 0; 265 | } 266 | 267 | return 1; 268 | } 269 | 270 | uint8_t lt_equals(lt_Value a, lt_Value b) 271 | { 272 | if (LT_IS_NUMBER(a) != LT_IS_NUMBER(b) || (a & LT_TYPE_MASK) != (b & LT_TYPE_MASK)) return 0; 273 | switch (a & LT_TYPE_MASK) 274 | { 275 | case LT_TYPE_NULL: 276 | case LT_TYPE_BOOL: 277 | case LT_TYPE_STRING: 278 | return a == b; 279 | 280 | case LT_TYPE_OBJECT: { 281 | lt_Object* obja = LT_GET_OBJECT(a); 282 | lt_Object* objb = LT_GET_OBJECT(b); 283 | if (obja->type != objb->type) return 0; 284 | 285 | switch (obja->type) 286 | { 287 | case LT_OBJECT_CHUNK: 288 | case LT_OBJECT_CLOSURE: 289 | case LT_OBJECT_FN: 290 | case LT_OBJECT_TABLE: 291 | case LT_OBJECT_NATIVEFN: 292 | return obja == objb; 293 | } 294 | } break; 295 | } 296 | 297 | return 0; 298 | } 299 | 300 | lt_Tokenizer lt_tokenize(lt_VM* vm, const char* source, const char* mod_name) 301 | { 302 | lt_Tokenizer t; 303 | t.module = mod_name; 304 | t.is_valid = 0; 305 | t.source = source; 306 | t.token_buffer = lt_buffer_new(sizeof(lt_Token)); 307 | t.identifier_buffer = lt_buffer_new(sizeof(lt_Identifier)); 308 | t.literal_buffer = lt_buffer_new(sizeof(lt_Literal)); 309 | 310 | if (!setjmp(*(jmp_buf*)vm->error_buf)) 311 | { 312 | const char* current = source; 313 | uint16_t line = 1, col = 0; 314 | 315 | #define PUSH_TOKEN(new_type) { \ 316 | lt_Token _t; _t.type = new_type; _t.line = line; _t.col = col++; _t.idx = 0; \ 317 | lt_buffer_push(vm, &t.token_buffer, &_t); current++; found = 1;\ 318 | }; 319 | 320 | while (*current) 321 | { 322 | uint8_t found = 0; 323 | switch (*current) 324 | { 325 | case ' ': case '\t': { col++; current++; } found = 1; break; 326 | case '\n': { col = 0; line++; current++; } found = 1; break; 327 | case '\r': { current++; } found = 1; break; 328 | case ';': { while (*current++ != '\n'); col = 1; line++; found = 1; } break; 329 | case '.': PUSH_TOKEN(LT_TOKEN_PERIOD) break; 330 | case ',': PUSH_TOKEN(LT_TOKEN_COMMA) break; 331 | case ':': PUSH_TOKEN(LT_TOKEN_COLON) break; 332 | case '(': PUSH_TOKEN(LT_TOKEN_OPENPAREN) break; 333 | case ')': PUSH_TOKEN(LT_TOKEN_CLOSEPAREN) break; 334 | case '[': PUSH_TOKEN(LT_TOKEN_OPENBRACKET) break; 335 | case ']': PUSH_TOKEN(LT_TOKEN_CLOSEBRACKET) break; 336 | case '{': PUSH_TOKEN(LT_TOKEN_OPENBRACE) break; 337 | case '}': PUSH_TOKEN(LT_TOKEN_CLOSEBRACE) break; 338 | case '+': PUSH_TOKEN(LT_TOKEN_PLUS) break; 339 | case '-': PUSH_TOKEN(LT_TOKEN_MINUS) break; 340 | case '*': PUSH_TOKEN(LT_TOKEN_MULTIPLY) break; 341 | case '/': PUSH_TOKEN(LT_TOKEN_DIVIDE) break; 342 | case '=': PUSH_TOKEN(LT_TOKEN_ASSIGN) break; 343 | } 344 | 345 | if (!found) 346 | { 347 | if (*current == '>') 348 | { 349 | if (*(current + 1) == '=') 350 | { 351 | current++; 352 | PUSH_TOKEN(LT_TOKEN_GTE); 353 | } 354 | else PUSH_TOKEN(LT_TOKEN_GT); 355 | } 356 | else if (*current == '<') 357 | { 358 | if (*(current + 1) == '=') 359 | { 360 | current++; 361 | PUSH_TOKEN(LT_TOKEN_LTE); 362 | } 363 | else PUSH_TOKEN(LT_TOKEN_LT); 364 | } 365 | else if (*current == '"') 366 | { 367 | const char* start = ++current; 368 | while (*current++ != '"') if (*current == '\n') { col = 0; line++; } 369 | 370 | uint32_t length = (uint32_t)(current - start - 1); 371 | 372 | lt_Literal newlit; 373 | newlit.type = LT_TOKEN_STRING_LITERAL; 374 | newlit.string = vm->alloc(length + 1); 375 | strncpy_s(newlit.string, length + 1, start, length); 376 | newlit.string[length] = 0; 377 | 378 | lt_buffer_push(vm, &t.literal_buffer, &newlit); 379 | 380 | lt_Token tok; 381 | tok.type = LT_TOKEN_STRING_LITERAL; 382 | tok.line = line; 383 | tok.col = col; col += length; 384 | tok.idx = t.literal_buffer.length - 1; 385 | lt_buffer_push(vm, &t.token_buffer, &tok); 386 | } 387 | else if (isdigit(*current)) 388 | { 389 | const char* start = current; 390 | uint8_t has_decimal = 0; 391 | 392 | while ((isalnum(*current) && !isalpha(*current)) || *current == '.') 393 | { 394 | if (*current == '.') 395 | { 396 | if (has_decimal) _lt_tokenize_error(vm, t.module, line, col, "Can't have multiple decimals in number literal!"); 397 | has_decimal = 1; 398 | } 399 | 400 | current++; 401 | } 402 | 403 | uint32_t length = (uint32_t)(current - start); 404 | char* end = 0; 405 | double number = strtod(start, &end); 406 | 407 | if (end != current) _lt_tokenize_error(vm, t.module, line, col, "Failed to parse number!"); 408 | 409 | lt_Literal newlit; 410 | newlit.type = LT_TOKEN_NUMBER_LITERAL; 411 | newlit.number = number; 412 | 413 | lt_buffer_push(vm, &t.literal_buffer, &newlit); 414 | 415 | lt_Token tok; 416 | tok.type = LT_TOKEN_NUMBER_LITERAL; 417 | tok.line = line; 418 | tok.col = col; col += length; 419 | tok.idx = t.literal_buffer.length - 1; 420 | lt_buffer_push(vm, &t.token_buffer, &tok); 421 | } 422 | else if (isalpha(*current) || *current == '_') 423 | { 424 | const char* start = current; 425 | uint8_t search = 1; 426 | while (search) 427 | { 428 | current++; 429 | if (!isalnum(*current) && *current != '_') 430 | { 431 | search = 0; 432 | } 433 | } 434 | 435 | uint16_t length = (uint16_t)(current - start); 436 | 437 | #define PUSH_STR_TOKEN(name, new_type) \ 438 | if(faststrcmp(name, sizeof(name) - 1, start, length)) { \ 439 | lt_Token _t; _t.type = new_type; _t.line = line; _t.col = col; col += length; _t.idx = 0; \ 440 | lt_buffer_push(vm, &t.token_buffer, &_t); found = 1; } 441 | 442 | PUSH_STR_TOKEN("fn", LT_TOKEN_FN) 443 | else PUSH_STR_TOKEN("var", LT_TOKEN_VAR) 444 | else PUSH_STR_TOKEN("if", LT_TOKEN_IF) 445 | else PUSH_STR_TOKEN("else", LT_TOKEN_ELSE) 446 | else PUSH_STR_TOKEN("elseif", LT_TOKEN_ELSEIF) 447 | else PUSH_STR_TOKEN("for", LT_TOKEN_FOR) 448 | else PUSH_STR_TOKEN("in", LT_TOKEN_IN) 449 | else PUSH_STR_TOKEN("while", LT_TOKEN_WHILE) 450 | else PUSH_STR_TOKEN("break", LT_TOKEN_BREAK) 451 | else PUSH_STR_TOKEN("return", LT_TOKEN_RETURN) 452 | else PUSH_STR_TOKEN("is", LT_TOKEN_EQUALS) 453 | else PUSH_STR_TOKEN("isnt", LT_TOKEN_NOTEQUALS) 454 | else PUSH_STR_TOKEN("and", LT_TOKEN_AND) 455 | else PUSH_STR_TOKEN("or", LT_TOKEN_OR) 456 | else PUSH_STR_TOKEN("not", LT_TOKEN_NOT) 457 | else PUSH_STR_TOKEN("true", LT_TOKEN_TRUE_LITERAL) 458 | else PUSH_STR_TOKEN("false", LT_TOKEN_FALSE_LITERAL) 459 | else PUSH_STR_TOKEN("null", LT_TOKEN_NULL_LITERAL) 460 | 461 | if (!found) 462 | { 463 | for (uint32_t i = 0; i < t.identifier_buffer.length; i++) 464 | { 465 | lt_Identifier* id = lt_buffer_at(&t.identifier_buffer, i); 466 | if (faststrcmp(start, length, id->name, strlen(id->name))) 467 | { 468 | found = 1; 469 | id->num_references++; 470 | 471 | lt_Token tok; 472 | tok.type = LT_TOKEN_IDENTIFIER; 473 | tok.line = line; 474 | tok.col = col; col += length; 475 | tok.idx = i; 476 | lt_buffer_push(vm, &t.token_buffer, &tok); 477 | break; 478 | } 479 | } 480 | 481 | if (!found) 482 | { 483 | lt_Identifier newid; 484 | newid.num_references = 1; 485 | newid.name = vm->alloc(length + 1); 486 | strncpy_s(newid.name, length + 1, start, length); 487 | newid.name[length] = 0; 488 | 489 | lt_buffer_push(vm, &t.identifier_buffer, &newid); 490 | 491 | lt_Token tok; 492 | tok.type = LT_TOKEN_IDENTIFIER; 493 | tok.line = line; 494 | tok.col = col; col += length; 495 | tok.idx = t.identifier_buffer.length - 1; 496 | lt_buffer_push(vm, &t.token_buffer, &tok); 497 | } 498 | } 499 | } 500 | else _lt_tokenize_error(vm, t.module, line, col, "Unrecognized token!"); 501 | } 502 | } 503 | 504 | lt_Token tok; 505 | tok.type = LT_TOKEN_END; 506 | tok.line = line; 507 | tok.col = col; 508 | lt_buffer_push(vm, &t.token_buffer, &tok); 509 | 510 | t.is_valid = 1; 511 | } 512 | return t; 513 | } 514 | 515 | lt_AstNode* _lt_get_node_of_type(lt_VM* vm, lt_Token* current, lt_Parser* p, lt_AstNodeType type) 516 | { 517 | lt_AstNode* new_node = vm->alloc(sizeof(lt_AstNode)); 518 | memset(new_node, 0, sizeof(lt_AstNode)); 519 | new_node->type = type; 520 | new_node->loc.line = current->line; 521 | new_node->loc.col = current->col; 522 | 523 | lt_buffer_push(vm, &p->ast_nodes, &new_node); 524 | return *(void**)lt_buffer_last(&p->ast_nodes); 525 | } 526 | 527 | uint32_t _lt_tokens_equal(lt_Token* a, lt_Token* b) 528 | { 529 | return (a->type == LT_TOKEN_IDENTIFIER && b->type == LT_TOKEN_IDENTIFIER && a->idx == b->idx); 530 | } 531 | 532 | uint16_t _lt_make_local(lt_VM* vm, lt_Scope* scope, lt_Token* t) 533 | { 534 | lt_Scope* current = scope; 535 | 536 | for (uint32_t i = 0; i < current->locals.length; ++i) 537 | { 538 | if (_lt_tokens_equal((lt_Token*)lt_buffer_at(¤t->locals, i), t)) return i; 539 | } 540 | 541 | lt_buffer_push(vm, ¤t->locals, t); 542 | return current->locals.length - 1; 543 | } 544 | 545 | 546 | #define UPVAL_BIT 0x07000000 547 | #define NOT_FOUND ((uint32_t)-1) 548 | 549 | uint32_t _lt_find_local(lt_VM* vm, lt_Scope* scope, lt_Token* t) 550 | { 551 | lt_Scope* current = scope; 552 | 553 | for (uint32_t i = 0; i < current->locals.length; ++i) 554 | if (_lt_tokens_equal((lt_Token*)lt_buffer_at(¤t->locals, i), t)) return i; 555 | 556 | for (uint32_t i = 0; i < current->upvals.length; ++i) 557 | if (_lt_tokens_equal((lt_Token*)lt_buffer_at(¤t->upvals, i), t)) return i | UPVAL_BIT; 558 | 559 | lt_Scope* test = current->last; 560 | while (test) 561 | { 562 | uint8_t found = 0; 563 | for (uint32_t i = 0; i < test->locals.length; ++i) 564 | { 565 | if (_lt_tokens_equal((lt_Token*)lt_buffer_at(&test->locals, i), t)) { found = 1; break; } 566 | } 567 | 568 | if(!found) 569 | for (uint32_t i = 0; i < test->upvals.length; ++i) 570 | { 571 | if (_lt_tokens_equal((lt_Token*)lt_buffer_at(&test->upvals, i), t)) { found = 1; break; } 572 | } 573 | 574 | if (found) 575 | { 576 | lt_buffer_push(vm, ¤t->upvals, t); 577 | return (current->upvals.length - 1) | UPVAL_BIT; 578 | } 579 | 580 | test = test->last; 581 | } 582 | 583 | return NOT_FOUND; 584 | } 585 | 586 | lt_Token* _lt_parse_expression(lt_VM* vm, lt_Parser* p, lt_Token* start, lt_AstNode* dst); 587 | lt_Scope* _lt_parse_block(lt_VM* vm, lt_Parser* p, lt_Token* start, lt_Buffer* dst, uint8_t expects_terminator, uint8_t makes_scope, lt_Token** argnames) 588 | { 589 | if (makes_scope) 590 | { 591 | lt_Scope* new_scope = vm->alloc(sizeof(lt_Scope)); 592 | new_scope->last = p->current; 593 | p->current = new_scope; 594 | 595 | new_scope->start = start; 596 | new_scope->end = start; 597 | 598 | new_scope->locals = lt_buffer_new(sizeof(lt_Token)); 599 | new_scope->upvals = lt_buffer_new(sizeof(lt_Token)); 600 | 601 | if (argnames) while (*argnames) _lt_make_local(vm, p->current, *argnames++); 602 | } 603 | 604 | lt_Token* current = start; 605 | 606 | #define PEEK() (current + 1) 607 | #define NEXT() (last = current, current++) 608 | 609 | while (current->type != LT_TOKEN_END) 610 | { 611 | switch (current->type) 612 | { 613 | case LT_TOKEN_CLOSEBRACE: 614 | if (expects_terminator) { current++; goto end_block; } 615 | _lt_parse_error(vm, p->tkn->module, current, "Unexpected closing brace!"); 616 | case LT_TOKEN_END: 617 | _lt_parse_error(vm, p->tkn->module, current, "Unexpected end of file!"); 618 | case LT_TOKEN_IF: { 619 | lt_AstNode* if_statement = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_IF); 620 | current++; 621 | lt_AstNode* expr = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_EMPTY); 622 | current = _lt_parse_expression(vm, p, current, expr); 623 | 624 | if (current->type != LT_TOKEN_OPENBRACE) _lt_parse_error(vm, p->tkn->module, current, "Expeceted open brace to follow if expression!"); 625 | current++; 626 | 627 | lt_Buffer body = lt_buffer_new(sizeof(lt_AstNode*)); 628 | _lt_parse_block(vm, p, current, &body, 1, 0, 0); 629 | current = p->current->end; 630 | 631 | if_statement->branch.expr = expr; 632 | if_statement->branch.body = body; 633 | if_statement->branch.next = 0; 634 | 635 | lt_AstNode* last = 0; 636 | while (current->type == LT_TOKEN_ELSEIF || current->type == LT_TOKEN_ELSE) 637 | { 638 | 639 | lt_AstNode* node = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_EMPTY); 640 | if (if_statement->branch.next == 0) if_statement->branch.next = node; 641 | if (last) last->branch.next = node; 642 | 643 | if (current->type == LT_TOKEN_ELSEIF) 644 | { 645 | current++; 646 | node->type = LT_AST_NODE_ELSEIF; 647 | 648 | lt_AstNode* expr = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_EMPTY); 649 | current = _lt_parse_expression(vm, p, current, expr); 650 | 651 | node->branch.expr = expr; 652 | } 653 | else 654 | { 655 | current++; 656 | node->type = LT_AST_NODE_ELSE; 657 | } 658 | 659 | if (current->type != LT_TOKEN_OPENBRACE) _lt_parse_error(vm, p->tkn->module, current, "Expected open brace to follow else expression!"); 660 | current++; 661 | 662 | lt_Buffer body = lt_buffer_new(sizeof(lt_AstNode*)); 663 | _lt_parse_block(vm, p, current, &body, 1, 0, 0); 664 | current = p->current->end; 665 | 666 | node->branch.body = body; 667 | 668 | last = node; 669 | } 670 | 671 | lt_buffer_push(vm, dst, &if_statement); 672 | } break; 673 | 674 | case LT_TOKEN_FOR: { 675 | lt_AstNode* for_expr = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_FOR); 676 | current++; // eat for 677 | 678 | lt_Token* ident = current++; 679 | uint16_t iteridx = _lt_make_local(vm, p->current, ident); 680 | 681 | if (current->type != LT_TOKEN_IN) _lt_parse_error(vm, p->tkn->module, current, "Expected 'in' to follow 'for' iterator!"); 682 | current++; 683 | 684 | lt_AstNode* iter_expr = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_EMPTY); 685 | current = _lt_parse_expression(vm, p, current, iter_expr); 686 | 687 | for_expr->loop.identifier = iteridx; 688 | 689 | const char* FOR_ITER_NAME = "__iter"; 690 | uint16_t len = (uint16_t)strlen(FOR_ITER_NAME); 691 | 692 | lt_Identifier newid; 693 | newid.num_references = 1; 694 | newid.name = vm->alloc(len + 1); 695 | strncpy_s(newid.name, len + 1, FOR_ITER_NAME, len); 696 | newid.name[len] = 0; 697 | 698 | lt_buffer_push(vm, &p->tkn->identifier_buffer, &newid); 699 | 700 | lt_Token tok; 701 | tok.type = LT_TOKEN_IDENTIFIER; 702 | tok.line = ident->line; 703 | tok.col = ident->col; 704 | tok.idx = p->tkn->identifier_buffer.length - 1; 705 | 706 | for_expr->loop.closureidx = _lt_make_local(vm, p->current, &tok); 707 | for_expr->loop.iterator = iter_expr; 708 | 709 | if (current->type != LT_TOKEN_OPENBRACE) _lt_parse_error(vm, p->tkn->module, current, "Expected open brace to follow 'for' header!"); 710 | current++; 711 | 712 | lt_Buffer body = lt_buffer_new(sizeof(lt_AstNode*)); 713 | _lt_parse_block(vm, p, current, &body, 1, 0, 0); 714 | current = p->current->end; 715 | for_expr->loop.body = body; 716 | 717 | lt_buffer_push(vm, dst, &for_expr); 718 | } break; 719 | 720 | case LT_TOKEN_WHILE: { 721 | lt_AstNode* while_expr = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_WHILE); 722 | current++; // eat while 723 | 724 | lt_AstNode* iter_expr = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_EMPTY); 725 | current = _lt_parse_expression(vm, p, current, iter_expr); 726 | 727 | while_expr->loop.iterator = iter_expr; 728 | 729 | if (current->type != LT_TOKEN_OPENBRACE) _lt_parse_error(vm, p->tkn->module, current, "Expected open brace to follow 'while' header!"); 730 | current++; 731 | 732 | lt_Buffer body = lt_buffer_new(sizeof(lt_AstNode*)); 733 | _lt_parse_block(vm, p, current, &body, 1, 0, 0); 734 | current = p->current->end; 735 | while_expr->loop.body = body; 736 | 737 | lt_buffer_push(vm, dst, &while_expr); 738 | } break; 739 | 740 | case LT_TOKEN_RETURN: { 741 | lt_AstNode* ret = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_RETURN); 742 | ret->ret.expr = 0; 743 | current++; // eat 'return' 744 | 745 | lt_AstNode* expr = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_EMPTY); 746 | lt_Token* new_current = _lt_parse_expression(vm, p, current, expr); 747 | if (current != new_current) 748 | { 749 | ret->ret.expr = expr; 750 | current = new_current; 751 | } 752 | 753 | lt_buffer_push(vm, dst, &ret); 754 | } break; 755 | case LT_TOKEN_BREAK: { 756 | lt_AstNode* brk = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_BREAK); 757 | current++; // eat 'break' 758 | 759 | lt_buffer_push(vm, dst, &brk); 760 | } 761 | case LT_TOKEN_VAR: { 762 | lt_AstNode* declare = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_DECLARE); 763 | current++; 764 | if (current->type == LT_TOKEN_IDENTIFIER) 765 | { 766 | declare->declare.identifier = current++; 767 | _lt_make_local(vm, p->current, declare->declare.identifier); 768 | } 769 | else _lt_parse_error(vm, p->tkn->module, current, "Expected identifier to follow 'var'!"); 770 | 771 | lt_AstNode* rhs = 0; 772 | if (current->type == LT_TOKEN_ASSIGN) 773 | { 774 | current++; 775 | rhs = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_EMPTY); 776 | current = _lt_parse_expression(vm, p, current, rhs); 777 | } 778 | 779 | declare->declare.expr = rhs; 780 | lt_buffer_push(vm, dst, &declare); 781 | } break; 782 | default: { 783 | lt_AstNode* result = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_EMPTY); 784 | current = _lt_parse_expression(vm, p, current, result); 785 | 786 | if (current->type == LT_TOKEN_ASSIGN) 787 | { 788 | current++; 789 | lt_AstNode* expr = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_EMPTY); 790 | current = _lt_parse_expression(vm, p, current, expr); 791 | 792 | lt_AstNode* lhs = result; 793 | 794 | result = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_ASSIGN); 795 | result->assign.left = lhs; 796 | result->assign.right = expr; 797 | } 798 | 799 | lt_buffer_push(vm, dst, &result); 800 | } break; 801 | } 802 | } 803 | 804 | end_block: 805 | p->current->end = current; 806 | lt_Scope* new_scope = p->current; 807 | 808 | if(makes_scope) p->current = p->current->last; 809 | 810 | return new_scope; 811 | } 812 | 813 | uint8_t _lt_get_prec(lt_TokenType op) 814 | { 815 | switch (op) 816 | { 817 | case LT_TOKEN_NOT: case LT_TOKEN_NEGATE: return 5; 818 | case LT_TOKEN_MULTIPLY: case LT_TOKEN_DIVIDE: return 4; 819 | case LT_TOKEN_PLUS: case LT_TOKEN_MINUS: return 3; 820 | case LT_TOKEN_GT: case LT_TOKEN_GTE: case LT_TOKEN_LT: case LT_TOKEN_LTE: case LT_TOKEN_EQUALS: case LT_TOKEN_NOTEQUALS: return 2; 821 | case LT_TOKEN_AND: case LT_TOKEN_OR: return 1; 822 | } 823 | 824 | return 0; 825 | } 826 | 827 | #define LT_TOKEN_ANY_LITERAL \ 828 | LT_TOKEN_NULL_LITERAL: \ 829 | case LT_TOKEN_FALSE_LITERAL: \ 830 | case LT_TOKEN_TRUE_LITERAL: \ 831 | case LT_TOKEN_NUMBER_LITERAL: \ 832 | case LT_TOKEN_STRING_LITERAL: \ 833 | case LT_TOKEN_FN 834 | 835 | lt_Token* _lt_parse_expression(lt_VM* vm, lt_Parser* p, lt_Token* start, lt_AstNode* dst) 836 | { 837 | uint8_t n_open = 0; 838 | lt_Token* last = 0; 839 | lt_Token* current = start; 840 | 841 | lt_Buffer result = lt_buffer_new(sizeof(lt_AstNode*)); 842 | lt_Buffer operator_stack = lt_buffer_new(sizeof(lt_TokenType)); 843 | 844 | #define PUSH_EXPR_FROM_OP(op) \ 845 | if (op == LT_TOKEN_NOT || op == LT_TOKEN_NEGATE) \ 846 | { \ 847 | lt_AstNode* unaryop = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_UNARYOP); \ 848 | unaryop->unary_op.type = op; \ 849 | lt_buffer_push(vm, &result, &unaryop); \ 850 | } \ 851 | else \ 852 | { \ 853 | lt_AstNode* binaryop = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_BINARYOP); \ 854 | binaryop->binary_op.type = op; \ 855 | lt_buffer_push(vm, &result, &binaryop); \ 856 | } 857 | 858 | #define BREAK_ON_EXPR_BOUNDRY \ 859 | if (last) switch (last->type) \ 860 | { \ 861 | case LT_TOKEN_IDENTIFIER: \ 862 | case LT_TOKEN_CLOSEBRACE: \ 863 | case LT_TOKEN_CLOSEBRACKET: \ 864 | case LT_TOKEN_CLOSEPAREN: \ 865 | case LT_TOKEN_ANY_LITERAL: \ 866 | goto expr_end; \ 867 | } 868 | 869 | while (current->type != LT_TOKEN_END) 870 | { 871 | switch (current->type) 872 | { 873 | case LT_TOKEN_IDENTIFIER: { 874 | BREAK_ON_EXPR_BOUNDRY 875 | 876 | lt_AstNode* ident = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_IDENTIFIER); 877 | ident->identifier.token = NEXT(); 878 | 879 | if (_lt_find_local(vm, p->current, ident->identifier.token) == NOT_FOUND) {} // ERROR! 880 | 881 | lt_buffer_push(vm, &result, &ident); 882 | } break; 883 | case LT_TOKEN_OPENBRACKET: { 884 | uint8_t is_index = last != 0; 885 | if (last) switch(last->type) 886 | { 887 | case LT_TOKEN_CLOSEBRACE: 888 | case LT_TOKEN_CLOSEBRACKET: 889 | case LT_TOKEN_CLOSEPAREN: 890 | case LT_TOKEN_IDENTIFIER: 891 | is_index = 1; 892 | } 893 | 894 | if (is_index) 895 | { 896 | NEXT(); // eat bracket 897 | lt_AstNode* idx_expr = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_EMPTY); 898 | current = _lt_parse_expression(vm, p, current, idx_expr); 899 | if (!PEEK()->type == LT_TOKEN_CLOSEBRACKET) _lt_parse_error(vm, p->tkn->module, current, "Expected closing bracket to follow index expression!"); 900 | NEXT(); 901 | 902 | lt_AstNode* source = *(void**)lt_buffer_last(&result); lt_buffer_pop(&result); 903 | lt_AstNode* index = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_INDEX); 904 | index->index.source = source; 905 | index->index.idx = idx_expr; 906 | lt_buffer_push(vm, &result, &index); 907 | } 908 | else 909 | { 910 | // array literal 911 | NEXT(); // eat bracket 912 | lt_AstNode* arr_expr = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_ARRAY); 913 | arr_expr->array.values = lt_buffer_new(sizeof(lt_AstNode*)); 914 | 915 | while (current->type != LT_TOKEN_CLOSEBRACKET) 916 | { 917 | lt_AstNode* value = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_EMPTY); 918 | current = _lt_parse_expression(vm, p, current, value); 919 | lt_buffer_push(vm, &arr_expr->array.values, &value); 920 | 921 | if (current->type == LT_TOKEN_COMMA) current++; 922 | } 923 | 924 | NEXT(); 925 | lt_buffer_push(vm, &result, &arr_expr); 926 | } 927 | } break; 928 | case LT_TOKEN_PERIOD: { 929 | uint8_t allowed = 0; 930 | if (last) switch (last->type) 931 | { 932 | case LT_TOKEN_CLOSEBRACE: 933 | case LT_TOKEN_CLOSEBRACKET: 934 | case LT_TOKEN_CLOSEPAREN: 935 | case LT_TOKEN_IDENTIFIER: 936 | allowed = 1; 937 | } 938 | 939 | if (!allowed) goto expr_end; 940 | 941 | NEXT(); // eat period 942 | lt_AstNode* idx_expr = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_LITERAL); 943 | if (current->type != LT_TOKEN_IDENTIFIER) _lt_parse_error(vm, p->tkn->module, current, "Expected identifier to follow '.' operator!"); 944 | idx_expr->literal.token = NEXT(); 945 | 946 | lt_AstNode* source = *(void**)lt_buffer_last(&result); lt_buffer_pop(&result); 947 | lt_AstNode* index = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_INDEX); 948 | index->index.source = source; 949 | index->index.idx = idx_expr; 950 | lt_buffer_push(vm, &result, &index); 951 | } break; 952 | 953 | case LT_TOKEN_NUMBER_LITERAL: case LT_TOKEN_NULL_LITERAL: case LT_TOKEN_TRUE_LITERAL: case LT_TOKEN_FALSE_LITERAL: case LT_TOKEN_STRING_LITERAL: { 954 | BREAK_ON_EXPR_BOUNDRY 955 | 956 | lt_AstNode* lit = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_LITERAL); 957 | lit->literal.token = NEXT(); 958 | lt_buffer_push(vm, &result, &lit); 959 | } break; 960 | 961 | case LT_TOKEN_PLUS: case LT_TOKEN_MINUS: 962 | case LT_TOKEN_MULTIPLY: case LT_TOKEN_DIVIDE: 963 | case LT_TOKEN_EQUALS: case LT_TOKEN_NOTEQUALS: 964 | case LT_TOKEN_GT: case LT_TOKEN_GTE: case LT_TOKEN_LT: case LT_TOKEN_LTE: 965 | case LT_TOKEN_AND: case LT_TOKEN_OR: case LT_TOKEN_NOT: { 966 | lt_TokenType optype = current->type; 967 | 968 | if (optype == LT_TOKEN_MINUS) 969 | { 970 | optype = LT_TOKEN_NEGATE; 971 | 972 | if (last) switch (last->type) 973 | { 974 | case LT_TOKEN_ANY_LITERAL: 975 | case LT_TOKEN_IDENTIFIER: 976 | case LT_TOKEN_CLOSEPAREN: 977 | case LT_TOKEN_CLOSEBRACKET: 978 | optype = LT_TOKEN_MINUS; 979 | } 980 | } 981 | 982 | while (operator_stack.length > 0) 983 | { 984 | if (_lt_get_prec(*(lt_TokenType*)lt_buffer_last(&operator_stack)) > _lt_get_prec(optype)) 985 | { 986 | lt_TokenType shunted = *(lt_TokenType*)lt_buffer_last(&operator_stack); 987 | lt_buffer_pop(&operator_stack); 988 | 989 | PUSH_EXPR_FROM_OP(shunted); 990 | } 991 | else break; 992 | } 993 | 994 | lt_buffer_push(vm, &operator_stack, &optype); 995 | NEXT(); 996 | } break; 997 | 998 | case LT_TOKEN_OPENPAREN: { 999 | if (last) switch (last->type) 1000 | { 1001 | case LT_TOKEN_CLOSEPAREN: 1002 | case LT_TOKEN_CLOSEBRACE: 1003 | case LT_TOKEN_IDENTIFIER: 1004 | case LT_TOKEN_CLOSEBRACKET: { 1005 | NEXT(); 1006 | lt_AstNode* callee = *(lt_AstNode**)lt_buffer_last(&result); lt_buffer_pop(&result); 1007 | 1008 | lt_AstNode* call = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_CALL); 1009 | uint8_t nargs = 0; 1010 | 1011 | while (current->type != LT_TOKEN_CLOSEPAREN) 1012 | { 1013 | if (current->type == LT_TOKEN_END) _lt_parse_error(vm, p->tkn->module, current, "Unexpected end of file in expression. (Unclosed parenthesis?)"); 1014 | if (current->type == LT_TOKEN_COMMA) NEXT(); 1015 | 1016 | lt_AstNode* arg = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_EMPTY); 1017 | current = _lt_parse_expression(vm, p, current, arg); 1018 | call->call.args[nargs++] = arg; 1019 | } 1020 | 1021 | call->call.callee = callee; 1022 | lt_buffer_push(vm, &result, &call); 1023 | NEXT(); 1024 | } break; 1025 | default: 1026 | n_open++; 1027 | lt_buffer_push(vm, &operator_stack, ¤t->type); NEXT(); 1028 | } 1029 | } break; 1030 | 1031 | case LT_TOKEN_CLOSEPAREN: { 1032 | if (n_open == 0) goto expr_end; 1033 | NEXT(); 1034 | while (operator_stack.length > 0) 1035 | { 1036 | lt_TokenType back = *(lt_TokenType*)lt_buffer_last(&operator_stack); 1037 | 1038 | if (back == LT_TOKEN_OPENPAREN) break; 1039 | 1040 | lt_buffer_pop(&operator_stack); 1041 | PUSH_EXPR_FROM_OP(back); 1042 | } 1043 | 1044 | if (operator_stack.length == 0) _lt_parse_error(vm, p->tkn->module, current, "Malformed expression!"); 1045 | else 1046 | { 1047 | lt_buffer_pop(&operator_stack); 1048 | n_open--; 1049 | } 1050 | } break; 1051 | 1052 | case LT_TOKEN_OPENBRACE: { 1053 | BREAK_ON_EXPR_BOUNDRY 1054 | 1055 | // any time we see this, assume it's a table lieral. all other braces should be handled at block level 1056 | lt_AstNode* table = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_TABLE); 1057 | table->table.keys = lt_buffer_new(sizeof(lt_AstNode*)); 1058 | table->table.values = lt_buffer_new(sizeof(lt_AstNode*)); 1059 | 1060 | NEXT(); // eat brace 1061 | 1062 | while (current->type != LT_TOKEN_CLOSEBRACE) 1063 | { 1064 | lt_AstNode* key = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_LITERAL); 1065 | key->literal.token = NEXT(); 1066 | 1067 | if (!current->type == LT_TOKEN_COLON) lt_error(vm, "Expected colon to follow table index!"); 1068 | NEXT(); // eat colon 1069 | 1070 | lt_AstNode* value = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_EMPTY); 1071 | current = _lt_parse_expression(vm, p, current, value); 1072 | 1073 | lt_buffer_push(vm, &table->table.keys, &key); 1074 | lt_buffer_push(vm, &table->table.values, &value); 1075 | } 1076 | 1077 | NEXT(); 1078 | lt_buffer_push(vm, &result, &table); 1079 | } break; 1080 | 1081 | case LT_TOKEN_FN: { 1082 | BREAK_ON_EXPR_BOUNDRY 1083 | 1084 | lt_AstNode* func = _lt_get_node_of_type(vm, current, p, LT_AST_NODE_FN); 1085 | NEXT(); 1086 | 1087 | if (current->type != LT_TOKEN_OPENPAREN) _lt_parse_error(vm, p->tkn->module, current, "Expected open parenthesis to follow 'fn'!"); 1088 | current++; 1089 | 1090 | uint8_t nargs = 0; 1091 | while (current->type == LT_TOKEN_IDENTIFIER) 1092 | { 1093 | func->fn.args[nargs++] = current++; 1094 | if (current->type == LT_TOKEN_COMMA) current++; 1095 | } 1096 | 1097 | if (current->type != LT_TOKEN_CLOSEPAREN) _lt_parse_error(vm, p->tkn->module, current, "Expecetd closing parenthesis to follow argument list!"); 1098 | current++; 1099 | 1100 | if (current->type != LT_TOKEN_OPENBRACE) _lt_parse_error(vm, p->tkn->module, current, "Expected open brace to follow argument list!"); 1101 | current++; 1102 | 1103 | lt_Buffer body = lt_buffer_new(sizeof(lt_AstNode*)); 1104 | lt_Scope* fn_scope = _lt_parse_block(vm, p, current, &body, 1, 1, func->fn.args); 1105 | current = fn_scope->end; 1106 | 1107 | func->fn.scope = fn_scope; 1108 | func->fn.body = body; 1109 | lt_buffer_push(vm, &result, &func); 1110 | } break; 1111 | 1112 | default: { 1113 | if (last) goto expr_end; 1114 | else _lt_parse_error(vm, p->tkn->module, current, "Malformed expression!"); 1115 | } 1116 | } 1117 | } 1118 | 1119 | expr_end: 1120 | while (operator_stack.length > 0) 1121 | { 1122 | lt_TokenType back = *(lt_TokenType*)lt_buffer_last(&operator_stack); 1123 | lt_buffer_pop(&operator_stack); 1124 | PUSH_EXPR_FROM_OP(back); 1125 | } 1126 | 1127 | lt_Buffer value_stack = lt_buffer_new(sizeof(lt_AstNode*)); 1128 | 1129 | for (uint32_t i = 0; i < result.length; i++) 1130 | { 1131 | lt_AstNode* current = *(lt_AstNode**)lt_buffer_at(&result, i); 1132 | if (current->type == LT_AST_NODE_BINARYOP) 1133 | { 1134 | lt_AstNode* right = *(lt_AstNode**)lt_buffer_last(&value_stack); lt_buffer_pop(&value_stack); 1135 | lt_AstNode* left = *(lt_AstNode**)lt_buffer_last(&value_stack); lt_buffer_pop(&value_stack); 1136 | 1137 | switch (current->binary_op.type) 1138 | { 1139 | case LT_TOKEN_LT: 1140 | case LT_TOKEN_LTE: 1141 | current->binary_op.type -= 2; 1142 | current->binary_op.left = right; 1143 | current->binary_op.right = left; 1144 | break; 1145 | default: 1146 | current->binary_op.left = left; 1147 | current->binary_op.right = right; 1148 | break; 1149 | } 1150 | } 1151 | else if (current->type == LT_AST_NODE_UNARYOP) 1152 | { 1153 | lt_AstNode* right = *(lt_AstNode**)lt_buffer_last(&value_stack); lt_buffer_pop(&value_stack); 1154 | current->unary_op.expr = right; 1155 | } 1156 | 1157 | lt_buffer_push(vm, &value_stack, ¤t); 1158 | } 1159 | 1160 | if (value_stack.length > 0) 1161 | { 1162 | memcpy(dst, *(void**)lt_buffer_at(&value_stack, 0), sizeof(lt_AstNode)); 1163 | } 1164 | 1165 | lt_buffer_destroy(vm, &result); 1166 | lt_buffer_destroy(vm, &operator_stack); 1167 | lt_buffer_destroy(vm, &value_stack); 1168 | 1169 | return current; 1170 | } 1171 | 1172 | lt_Parser lt_parse(lt_VM* vm, lt_Tokenizer* tkn) 1173 | { 1174 | lt_Parser p; 1175 | p.is_valid = 0; 1176 | 1177 | if (!setjmp(*(jmp_buf*)vm->error_buf)) 1178 | { 1179 | p.current = 0; 1180 | p.tkn = tkn; 1181 | p.ast_nodes = lt_buffer_new(sizeof(lt_AstNode*)); 1182 | p.root = _lt_get_node_of_type(vm, (lt_Token*)tkn->token_buffer.data, &p, LT_AST_NODE_CHUNK); 1183 | p.root->chunk.body = lt_buffer_new(sizeof(lt_AstNode*)); 1184 | 1185 | lt_Scope* file_scope = _lt_parse_block(vm, &p, tkn->token_buffer.data, &p.root->chunk.body, 0, 1, 0); 1186 | 1187 | p.root->chunk.scope = file_scope; 1188 | p.is_valid = 1; 1189 | } 1190 | 1191 | return p; 1192 | } 1193 | 1194 | lt_VM* lt_open(lt_AllocFn alloc, lt_FreeFn free, lt_ErrorFn error) 1195 | { 1196 | lt_VM* vm = alloc(sizeof(lt_VM)); 1197 | memset(vm, 0, sizeof(lt_VM)); 1198 | 1199 | vm->top = vm->stack; 1200 | 1201 | vm->alloc = alloc; 1202 | vm->free = free; 1203 | vm->error = error; 1204 | 1205 | vm->heap = lt_buffer_new(sizeof(lt_Object*)); 1206 | vm->keepalive = lt_buffer_new(sizeof(lt_Object*)); 1207 | 1208 | vm->error_buf = malloc(sizeof(jmp_buf)); 1209 | vm->generate_debug = 1; 1210 | 1211 | vm->global = LT_VALUE_OBJECT(lt_allocate(vm, LT_OBJECT_TABLE)); 1212 | lt_nocollect(vm, LT_GET_OBJECT(vm->global)); 1213 | return vm; 1214 | } 1215 | 1216 | void lt_destroy(lt_VM* vm) 1217 | { 1218 | lt_buffer_destroy(vm, &vm->keepalive); 1219 | lt_collect(vm); 1220 | vm->free(vm); 1221 | } 1222 | 1223 | lt_Object* lt_allocate(lt_VM* vm, lt_ObjectType type) 1224 | { 1225 | lt_Object* obj = vm->alloc(sizeof(lt_Object)); 1226 | memset(obj, 0, sizeof(lt_Object)); 1227 | obj->type = type; 1228 | 1229 | lt_buffer_push(vm, &vm->heap, &obj); 1230 | 1231 | return obj; 1232 | } 1233 | 1234 | void lt_free(lt_VM* vm, uint32_t heapidx) 1235 | { 1236 | lt_Object* obj = *(lt_Object**)lt_buffer_at(&vm->heap, heapidx); 1237 | 1238 | switch (obj->type) 1239 | { 1240 | case LT_OBJECT_CHUNK: { 1241 | lt_buffer_destroy(vm, &obj->chunk.code); 1242 | lt_buffer_destroy(vm, &obj->chunk.constants); 1243 | } break; 1244 | case LT_OBJECT_CLOSURE: { 1245 | lt_buffer_destroy(vm, &obj->closure.captures); 1246 | } break; 1247 | case LT_OBJECT_FN: { 1248 | lt_buffer_destroy(vm, &obj->fn.code); 1249 | lt_buffer_destroy(vm, &obj->fn.constants); 1250 | } break; 1251 | case LT_OBJECT_TABLE: { 1252 | for (uint8_t i = 0; i < 16; ++i) 1253 | lt_buffer_destroy(vm, obj->table.buckets + i); 1254 | } break; 1255 | case LT_OBJECT_ARRAY: { 1256 | lt_buffer_destroy(vm, &obj->array); 1257 | } break; 1258 | case LT_OBJECT_PTR: { 1259 | vm->free(obj->ptr); 1260 | } break; 1261 | } 1262 | 1263 | lt_buffer_cycle(&vm->heap, heapidx); 1264 | vm->free(obj); 1265 | } 1266 | 1267 | void lt_nocollect(lt_VM* vm, lt_Object* obj) 1268 | { 1269 | lt_buffer_push(vm, &vm->keepalive, &obj); 1270 | } 1271 | 1272 | void lt_resumecollect(lt_VM* vm, lt_Object* obj) 1273 | { 1274 | for (uint32_t i = 0; i < vm->keepalive.length; i++) 1275 | { 1276 | if ((*(lt_Object**)lt_buffer_at(&vm->keepalive, i)) == obj) 1277 | { 1278 | lt_buffer_cycle(&vm->keepalive, i); 1279 | return; 1280 | } 1281 | } 1282 | } 1283 | 1284 | #define MARK(x) (x->markbit = 1) 1285 | #define CLEAR(x) (x->markbit = 0) 1286 | 1287 | void lt_sweep(lt_VM* vm, lt_Object* obj); 1288 | 1289 | void lt_sweep_v(lt_VM* vm, lt_Value val) 1290 | { 1291 | if (LT_IS_OBJECT(val)) lt_sweep(vm, LT_GET_OBJECT(val)); 1292 | else if (LT_IS_STRING(val)) _lt_reference_string(vm, val); 1293 | } 1294 | 1295 | void lt_sweep(lt_VM* vm, lt_Object* obj) 1296 | { 1297 | CLEAR(obj); 1298 | switch (obj->type) 1299 | { 1300 | case LT_OBJECT_CHUNK: { 1301 | for (uint32_t i = 0; i < obj->chunk.constants.length; ++i) 1302 | { 1303 | lt_sweep_v(vm, *(lt_Value*)lt_buffer_at(&obj->chunk.constants, i)); 1304 | } 1305 | } break; 1306 | case LT_OBJECT_CLOSURE: { 1307 | lt_sweep_v(vm, obj->closure.function); 1308 | for (uint32_t i = 0; i < obj->closure.captures.length; ++i) 1309 | { 1310 | lt_sweep_v(vm, *(lt_Value*)lt_buffer_at(&obj->closure.captures, i)); 1311 | } 1312 | } 1313 | case LT_OBJECT_FN: { 1314 | for (uint32_t i = 0; i < obj->fn.constants.length; ++i) 1315 | { 1316 | lt_sweep_v(vm, *(lt_Value*)lt_buffer_at(&obj->fn.constants, i)); 1317 | } 1318 | } break; 1319 | case LT_OBJECT_TABLE: { 1320 | for (uint16_t i = 0; i < 16; ++i) 1321 | { 1322 | lt_Buffer* bucket = obj->table.buckets + i; 1323 | for (uint32_t j = 0; j < bucket->length; ++j) 1324 | { 1325 | lt_sweep_v(vm, ((lt_TablePair*)lt_buffer_at(bucket, j))->key); 1326 | lt_sweep_v(vm, ((lt_TablePair*)lt_buffer_at(bucket, j))->value); 1327 | } 1328 | } 1329 | } break; 1330 | case LT_OBJECT_ARRAY: { 1331 | for (uint32_t j = 0; j < obj->array.length; ++j) 1332 | { 1333 | lt_sweep_v(vm, ((lt_TablePair*)lt_buffer_at(&obj->array, j))->key); 1334 | } 1335 | } break; 1336 | } 1337 | } 1338 | 1339 | uint32_t lt_collect(lt_VM* vm) 1340 | { 1341 | uint32_t num_collected = 0; 1342 | 1343 | for (uint32_t i = 0; i < vm->heap.length; ++i) 1344 | { 1345 | lt_Object* obj = *(lt_Object**)lt_buffer_at(&vm->heap, i); 1346 | MARK(obj); 1347 | } 1348 | 1349 | for (uint32_t i = 0; i < LT_DEDUP_TABLE_SIZE; i++) 1350 | { 1351 | for (uint32_t j = 0; j < vm->strings[i].length; ++j) 1352 | { 1353 | lt_StringDedupEntry* e = lt_buffer_at(vm->strings + i, j); 1354 | e->refcount = 0; 1355 | } 1356 | } 1357 | 1358 | for (uint32_t i = 0; i < vm->keepalive.length; ++i) 1359 | { 1360 | lt_sweep(vm, *(lt_Object**)lt_buffer_at(&vm->keepalive, i)); 1361 | } 1362 | 1363 | for (uint32_t i = 0; i < vm->heap.length; ++i) 1364 | { 1365 | lt_Object* obj = *(lt_Object**)lt_buffer_at(&vm->heap, i); 1366 | if (obj->markbit) 1367 | { 1368 | lt_free(vm, i--); 1369 | num_collected++; 1370 | } 1371 | } 1372 | 1373 | for (uint32_t i = 0; i < LT_DEDUP_TABLE_SIZE; i++) 1374 | { 1375 | for (uint32_t j = 0; j < vm->strings[i].length; ++j) 1376 | { 1377 | lt_StringDedupEntry* e = lt_buffer_at(vm->strings + i, j); 1378 | if (e->refcount == 0 && e->hash != 0) 1379 | { 1380 | vm->free(e->string); 1381 | e->hash = 0; // mark for reopen 1382 | } 1383 | } 1384 | } 1385 | 1386 | return num_collected; 1387 | } 1388 | 1389 | void lt_push(lt_VM* vm, lt_Value val) 1390 | { 1391 | (*vm->top++ = (val)); 1392 | } 1393 | 1394 | lt_Value lt_pop(lt_VM* vm) 1395 | { 1396 | return (*(--vm->top)); 1397 | } 1398 | 1399 | lt_Value lt_at(lt_VM* vm, uint32_t idx) 1400 | { 1401 | return *(vm->current->start + idx); 1402 | } 1403 | 1404 | void lt_close(lt_VM* vm, uint8_t count) 1405 | { 1406 | lt_Object* closure = lt_allocate(vm, LT_OBJECT_CLOSURE); 1407 | closure->closure.captures = lt_buffer_new(sizeof(lt_Value)); 1408 | for (int i = 0; i < count; i++) 1409 | { 1410 | lt_Value v = lt_pop(vm); 1411 | lt_buffer_push(vm, &closure->closure.captures, &v); 1412 | } 1413 | closure->closure.function = lt_pop(vm); 1414 | lt_push(vm, LT_VALUE_OBJECT(closure)); 1415 | } 1416 | 1417 | lt_Value lt_getupval(lt_VM* vm, uint8_t idx) 1418 | { 1419 | if (vm->current->upvals == 0) return LT_VALUE_NULL; 1420 | return *(lt_Value*)lt_buffer_at(vm->current->upvals, idx); 1421 | } 1422 | 1423 | void lt_setupval(lt_VM* vm, uint8_t idx, lt_Value val) 1424 | { 1425 | if (vm->current->upvals == 0) return; 1426 | *(lt_Value*)lt_buffer_at(vm->current->upvals, idx) = val; 1427 | } 1428 | 1429 | uint16_t _lt_exec(lt_VM* vm, lt_Value callable, uint8_t argc); 1430 | 1431 | uint16_t lt_exec(lt_VM* vm, lt_Value callable, uint8_t argc) 1432 | { 1433 | if (!setjmp(*(jmp_buf*)vm->error_buf)) 1434 | { 1435 | return _lt_exec(vm, callable, argc); 1436 | } 1437 | else 1438 | { 1439 | vm->depth = 0; 1440 | vm->top = vm->stack; 1441 | return 0; 1442 | } 1443 | } 1444 | 1445 | void lt_error(lt_VM* vm, const char* msg) 1446 | { 1447 | if (vm->error) vm->error(vm, msg); 1448 | longjmp(vm->error_buf, 1); 1449 | } 1450 | 1451 | uint16_t _lt_exec(lt_VM* vm, lt_Value callable, uint8_t argc) 1452 | { 1453 | if (!LT_IS_OBJECT(callable)) return 0; 1454 | 1455 | lt_Object* callee = LT_GET_OBJECT(callable); 1456 | 1457 | lt_Frame* frame = &vm->callstack[vm->depth++]; 1458 | memset(frame, 0, sizeof(lt_Frame)); 1459 | vm->current = frame; 1460 | 1461 | frame->callee = callee; 1462 | frame->start = vm->top - argc; 1463 | 1464 | switch (callee->type) 1465 | { 1466 | case LT_OBJECT_CHUNK: { 1467 | frame->code = &callee->chunk.code; 1468 | frame->constants = &callee->chunk.constants; 1469 | } break; 1470 | case LT_OBJECT_FN: { 1471 | frame->code = &callee->fn.code; 1472 | frame->constants = &callee->fn.constants; 1473 | } break; 1474 | case LT_OBJECT_CLOSURE: { 1475 | lt_Object* fn = LT_GET_OBJECT(callee->closure.function); 1476 | frame->upvals = &callee->closure.captures; 1477 | if (fn->type == LT_OBJECT_FN) 1478 | { 1479 | frame->code = &fn->fn.code; 1480 | frame->constants = &fn->fn.constants; 1481 | } 1482 | else 1483 | { 1484 | uint8_t n_return = fn->native(vm, argc); 1485 | 1486 | --vm->depth; 1487 | vm->current = vm->depth > 0 ? &vm->callstack[vm->depth - 1] : 0; 1488 | return n_return; 1489 | } 1490 | } break; 1491 | case LT_OBJECT_NATIVEFN: { 1492 | uint8_t n_return = callee->native(vm, argc); 1493 | 1494 | --vm->depth; 1495 | vm->current = vm->depth > 0 ? &vm->callstack[vm->depth - 1] : 0; 1496 | return n_return; 1497 | } break; 1498 | } 1499 | 1500 | lt_Value retval; 1501 | lt_Value* local_start = vm->stack + argc; 1502 | lt_Op* ip = (lt_Op*)frame->code->data; 1503 | frame->ip = &ip; 1504 | #undef NEXT 1505 | #define NEXT { ip++; goto inst_loop; } 1506 | 1507 | #define TOP (*(vm->top - 1)) 1508 | #define PUSH(x) (*vm->top++ = (x)) 1509 | #define POP() (*(--vm->top)) 1510 | 1511 | inst_loop: 1512 | switch (ip->op) 1513 | { 1514 | case LT_OP_NOP: NEXT; 1515 | case LT_OP_PUSH: for (int i = 0; i < ip->arg; ++i) PUSH(LT_VALUE_NULL); NEXT; 1516 | case LT_OP_DUP: PUSH(TOP); NEXT; 1517 | case LT_OP_PUSHC: PUSH(*(lt_Value*)lt_buffer_at(frame->constants, ip->arg)); NEXT; 1518 | case LT_OP_PUSHN: PUSH(LT_VALUE_NULL); NEXT; 1519 | case LT_OP_PUSHT: PUSH(LT_VALUE_TRUE); NEXT; 1520 | case LT_OP_PUSHF: PUSH(LT_VALUE_FALSE); NEXT; 1521 | 1522 | case LT_OP_MAKET: { 1523 | lt_Value t = LT_VALUE_OBJECT(lt_allocate(vm, LT_OBJECT_TABLE)); 1524 | for (uint32_t i = 0; i < (uint32_t)ip->arg; ++i) 1525 | { 1526 | lt_Value value = POP(); 1527 | lt_Value key = POP(); 1528 | lt_table_set(vm, t, key, value); 1529 | } 1530 | PUSH(t); 1531 | } NEXT; 1532 | 1533 | case LT_OP_MAKEA: { 1534 | lt_Value a = LT_VALUE_OBJECT(lt_allocate(vm, LT_OBJECT_ARRAY)); 1535 | for (uint32_t i = 0; i < (uint32_t)ip->arg; ++i) 1536 | { 1537 | lt_Value value = POP(); 1538 | lt_array_push(vm, a, value); 1539 | } 1540 | PUSH(a); 1541 | } NEXT; 1542 | 1543 | case LT_OP_SETT: { 1544 | lt_Value value = POP(); 1545 | lt_Value key = POP(); 1546 | lt_Value t = POP(); 1547 | if (LT_IS_TABLE(t)) lt_table_set(vm, t, key, value); 1548 | else if (LT_IS_ARRAY(t)) *lt_array_at(t, (uint32_t)lt_get_number(key)) = value; 1549 | } NEXT; 1550 | 1551 | case LT_OP_GETT: { 1552 | lt_Value key = POP(); 1553 | lt_Value t = POP(); 1554 | 1555 | if (LT_IS_TABLE(t)) PUSH(lt_table_get(vm, t, key)); 1556 | else if (LT_IS_ARRAY(t)) PUSH(*lt_array_at(t, (uint32_t)lt_get_number(key))); 1557 | else PUSH(LT_VALUE_NULL); 1558 | } NEXT; 1559 | 1560 | case LT_OP_GETG: PUSH(lt_table_get(vm, vm->global, POP())); NEXT; 1561 | 1562 | case LT_OP_ADD: TOP = (lt_make_number(VALTONUM(POP()) + VALTONUM(TOP))); NEXT; 1563 | case LT_OP_SUB: TOP = (lt_make_number(VALTONUM(POP()) - VALTONUM(TOP))); NEXT; 1564 | case LT_OP_MUL: TOP = (lt_make_number(VALTONUM(POP()) * VALTONUM(TOP))); NEXT; 1565 | case LT_OP_DIV: TOP = (lt_make_number(VALTONUM(POP()) / VALTONUM(TOP))); NEXT; 1566 | 1567 | case LT_OP_EQ: TOP = (lt_equals(POP(), TOP) ? LT_VALUE_TRUE : LT_VALUE_FALSE); NEXT; 1568 | case LT_OP_NEQ: TOP = (lt_equals(POP(), TOP) ? LT_VALUE_FALSE : LT_VALUE_TRUE); NEXT; 1569 | 1570 | case LT_OP_GT: TOP = (VALTONUM(POP()) > VALTONUM(TOP) ? LT_VALUE_TRUE : LT_VALUE_FALSE); NEXT; 1571 | case LT_OP_GTE: TOP = (VALTONUM(POP()) >= VALTONUM(TOP) ? LT_VALUE_TRUE : LT_VALUE_FALSE); NEXT; 1572 | 1573 | case LT_OP_NEG: TOP = (lt_make_number(VALTONUM(TOP) * -1.0)); NEXT; 1574 | 1575 | case LT_OP_AND: PUSH(LT_IS_TRUTHY(POP()) && LT_IS_TRUTHY(POP()) ? LT_VALUE_TRUE : LT_VALUE_FALSE); NEXT; 1576 | 1577 | case LT_OP_OR: { 1578 | lt_Value left = POP(); 1579 | lt_Value right = POP(); 1580 | if (LT_IS_TRUTHY(left)) PUSH(left); 1581 | else if (LT_IS_TRUTHY(right)) PUSH(right); 1582 | else PUSH(LT_VALUE_FALSE); 1583 | } NEXT; 1584 | 1585 | case LT_OP_NOT: TOP = (LT_IS_TRUTHY(TOP) ? LT_VALUE_FALSE : LT_VALUE_TRUE); NEXT; 1586 | 1587 | case LT_OP_LOAD: PUSH(local_start[ip->arg]); NEXT; 1588 | case LT_OP_STORE: local_start[ip->arg] = POP(); NEXT; 1589 | 1590 | case LT_OP_LOADUP: PUSH(*(lt_Value*)lt_buffer_at(frame->upvals, ip->arg)); NEXT; 1591 | case LT_OP_STOREUP: *(lt_Value*)lt_buffer_at(frame->upvals, ip->arg) = POP(); NEXT; 1592 | 1593 | case LT_OP_CLOSE: { 1594 | lt_Object* closure = lt_allocate(vm, LT_OBJECT_CLOSURE); 1595 | closure->closure.captures = lt_buffer_new(sizeof(lt_Value)); 1596 | for (int i = 0; i < ip->arg; i++) 1597 | { 1598 | lt_buffer_push(vm, &closure->closure.captures, &POP()); 1599 | } 1600 | closure->closure.function = POP(); 1601 | PUSH(LT_VALUE_OBJECT(closure)); 1602 | } NEXT; 1603 | 1604 | case LT_OP_CALL: _lt_exec(vm, POP(), (uint8_t)ip->arg); NEXT; 1605 | 1606 | case LT_OP_JMP: ip += ip->arg; NEXT; 1607 | case LT_OP_JMPC: { 1608 | lt_Value cond = POP(); 1609 | if (!LT_IS_TRUTHY(cond)) ip += ip->arg; 1610 | } NEXT; 1611 | case LT_OP_JMPN: if (POP() == LT_VALUE_NULL) ip += ip->arg; NEXT; 1612 | 1613 | case LT_OP_RET: 1614 | if (ip->arg) retval = POP(); 1615 | vm->top = frame->start; 1616 | --vm->depth; 1617 | vm->current = vm->depth > 0 ? &vm->callstack[vm->depth - 1] : 0; 1618 | if (ip->arg) PUSH(retval); 1619 | return ip->arg; 1620 | 1621 | default: lt_runtime_error(vm, "VM encountered unknown opcode!"); 1622 | } 1623 | 1624 | return vm->top - (frame->start + argc); 1625 | } 1626 | 1627 | #define OP(op) { lt_Op op = { LT_OP_##op, 0 }; lt_buffer_push(vm, code_body, &op); if(debug) { lt_buffer_push(vm, debug, &node->loc); } } 1628 | #define OPARG(op, arg) { lt_Op op = { LT_OP_##op, arg }; lt_buffer_push(vm, code_body, &op); if(debug) { lt_buffer_push(vm, debug, &node->loc); } } 1629 | 1630 | uint16_t _lt_push_constant(lt_VM* vm, lt_Buffer* constants, lt_Value constant) 1631 | { 1632 | for (uint32_t i = 0; i < constants->length; i++) 1633 | { 1634 | if ((*(lt_Value*)lt_buffer_at(constants, i)) == constant) return i; 1635 | } 1636 | 1637 | lt_buffer_push(vm, constants, &constant); 1638 | return constants->length - 1; 1639 | } 1640 | 1641 | static void _lt_compile_body(lt_VM* vm, lt_Parser* p, const char* name, lt_Buffer* debug, lt_Buffer* ast_body, lt_Scope* scope, lt_Buffer* code_body, lt_Buffer* constants); 1642 | static void _lt_compile_node(lt_VM* vm, lt_Parser* p, const char* name, lt_Buffer* debug, lt_AstNode* node, lt_Scope* scope, lt_Buffer* code_body, lt_Buffer* constants); 1643 | 1644 | static void _lt_compile_index(lt_VM* vm, lt_Parser* p, const char* name, lt_Buffer* debug, lt_AstNode* node, lt_Scope* scope, lt_Buffer* code_body, lt_Buffer* constants) 1645 | { 1646 | _lt_compile_node(vm, p, name, debug, node->index.source, scope, code_body, constants); 1647 | _lt_compile_node(vm, p, name, debug, node->index.idx, scope, code_body, constants); 1648 | } 1649 | 1650 | static void _lt_compile_node(lt_VM* vm, lt_Parser* p, const char* name, lt_Buffer* debug, lt_AstNode* node, lt_Scope* scope, lt_Buffer* code_body, lt_Buffer* constants) 1651 | { 1652 | switch (node->type) 1653 | { 1654 | case LT_AST_NODE_LITERAL: { 1655 | lt_Token* t = node->literal.token; 1656 | switch (t->type) 1657 | { 1658 | case LT_TOKEN_NULL_LITERAL: OP(PUSHN); break; 1659 | case LT_TOKEN_TRUE_LITERAL: OP(PUSHT); break; 1660 | case LT_TOKEN_FALSE_LITERAL: OP(PUSHF); break; 1661 | case LT_TOKEN_NUMBER_LITERAL: 1662 | case LT_TOKEN_STRING_LITERAL: { 1663 | lt_Value con; 1664 | lt_Literal* l = lt_buffer_at(&p->tkn->literal_buffer, t->idx); 1665 | if (l->type == LT_TOKEN_NUMBER_LITERAL) con = LT_VALUE_NUMBER(l->number); 1666 | else 1667 | { 1668 | con = lt_make_string(vm, l->string); 1669 | } 1670 | 1671 | uint16_t idx = _lt_push_constant(vm, constants, con); 1672 | OPARG(PUSHC, idx); 1673 | } break; 1674 | case LT_TOKEN_IDENTIFIER: { 1675 | lt_Identifier* i = lt_buffer_at(&p->tkn->identifier_buffer, t->idx); 1676 | lt_Value val = lt_make_string(vm, i->name); 1677 | OPARG(PUSHC, _lt_push_constant(vm, constants, val)); 1678 | } break; 1679 | } 1680 | } break; 1681 | case LT_AST_NODE_BREAK: { 1682 | OPARG(JMP, 0); 1683 | } break; 1684 | case LT_AST_NODE_TABLE: { 1685 | uint16_t size = node->table.keys.length; 1686 | 1687 | for (int i = 0; i < size; ++i) 1688 | { 1689 | lt_AstNode* key = *(lt_AstNode**)lt_buffer_at(&node->table.keys, i); 1690 | lt_AstNode* value = *(lt_AstNode**)lt_buffer_at(&node->table.values, i); 1691 | _lt_compile_node(vm, p, name, debug, key, scope, code_body, constants); 1692 | _lt_compile_node(vm, p, name, debug, value, scope, code_body, constants); 1693 | } 1694 | 1695 | OPARG(MAKET, size); 1696 | } break; 1697 | 1698 | case LT_AST_NODE_ARRAY: { 1699 | uint16_t size = node->table.keys.length; 1700 | 1701 | for (int i = size - 1; i >= 0; --i) 1702 | { 1703 | lt_AstNode* value = *(lt_AstNode**)lt_buffer_at(&node->array.values, i); 1704 | _lt_compile_node(vm, p, name, debug, value, scope, code_body, constants); 1705 | } 1706 | 1707 | OPARG(MAKEA, size); 1708 | } break; 1709 | 1710 | case LT_AST_NODE_IDENTIFIER: { 1711 | uint32_t idx = _lt_find_local(vm, scope, node->identifier.token); 1712 | if (idx == NOT_FOUND) { 1713 | lt_Identifier* i = lt_buffer_at(&p->tkn->identifier_buffer, node->identifier.token->idx); 1714 | lt_Value val = lt_make_string(vm, i->name); 1715 | OPARG(PUSHC, _lt_push_constant(vm, constants, val)); 1716 | OP(GETG); 1717 | } 1718 | else if ((idx & UPVAL_BIT) == UPVAL_BIT) 1719 | { 1720 | OPARG(LOADUP, idx & 0xFFFF); 1721 | } 1722 | else 1723 | { 1724 | OPARG(LOAD, idx & 0xFFFF); 1725 | } 1726 | } break; 1727 | 1728 | case LT_AST_NODE_INDEX: { 1729 | _lt_compile_index(vm, p, name, debug, node, scope, code_body, constants); 1730 | OP(GETT); 1731 | } break; 1732 | 1733 | case LT_AST_NODE_BINARYOP: { 1734 | _lt_compile_node(vm, p, name, debug, node->binary_op.right, scope, code_body, constants); 1735 | _lt_compile_node(vm, p, name, debug, node->binary_op.left, scope, code_body, constants); 1736 | switch (node->binary_op.type) 1737 | { 1738 | case LT_TOKEN_PLUS: OP(ADD); break; 1739 | case LT_TOKEN_MINUS: OP(SUB); break; 1740 | case LT_TOKEN_MULTIPLY: OP(MUL); break; 1741 | case LT_TOKEN_DIVIDE: OP(DIV); break; 1742 | case LT_TOKEN_AND: OP(AND); break; 1743 | case LT_TOKEN_OR: OP(OR); break; 1744 | case LT_TOKEN_EQUALS: OP(EQ); break; 1745 | case LT_TOKEN_NOTEQUALS: OP(NEQ); break; 1746 | case LT_TOKEN_GT: OP(GT); break; 1747 | case LT_TOKEN_GTE: OP(GTE); break; 1748 | } 1749 | } break; 1750 | 1751 | case LT_AST_NODE_UNARYOP: { 1752 | _lt_compile_node(vm, p, name, debug, node->unary_op.expr, scope, code_body, constants); 1753 | switch (node->unary_op.type) 1754 | { 1755 | case LT_TOKEN_NEGATE: OP(NEG); break; 1756 | case LT_TOKEN_NOT: OP(NOT); break; 1757 | } 1758 | } break; 1759 | 1760 | case LT_AST_NODE_DECLARE: { 1761 | uint16_t idx = _lt_make_local(vm, scope, node->declare.identifier); 1762 | if (node->declare.expr) 1763 | { 1764 | _lt_compile_node(vm, p, name, debug, node->declare.expr, scope, code_body, constants); 1765 | OPARG(STORE, idx); 1766 | } 1767 | } break; 1768 | 1769 | case LT_AST_NODE_ASSIGN: { 1770 | lt_AstNode* target = node->assign.left; 1771 | if (target->type == LT_AST_NODE_IDENTIFIER) 1772 | { 1773 | _lt_compile_node(vm, p, name, debug, node->assign.right, scope, code_body, constants); 1774 | uint32_t idx = _lt_find_local(vm, scope, target->identifier.token); 1775 | if (idx == NOT_FOUND) _lt_parse_error(vm, name, target->identifier.token, "Can't find local to assign to!"); 1776 | else if ((idx & UPVAL_BIT) == UPVAL_BIT) OPARG(STOREUP, idx & 0xFFFF) 1777 | else OPARG(STORE, idx & 0xFFFF); 1778 | } 1779 | else if (target->type == LT_AST_NODE_INDEX) 1780 | { 1781 | _lt_compile_index(vm, p, name, debug, target, scope, code_body, constants); 1782 | _lt_compile_node(vm, p, name, debug, node->assign.right, scope, code_body, constants); 1783 | OP(SETT); 1784 | } 1785 | } break; 1786 | 1787 | case LT_AST_NODE_FN: { 1788 | lt_Object* fn = lt_allocate(vm, LT_OBJECT_FN); 1789 | 1790 | uint8_t narg = 0; 1791 | lt_Token** arg = node->fn.args; 1792 | while (*arg) { narg++; arg++; } 1793 | 1794 | fn->fn.arity = narg; 1795 | fn->fn.code = lt_buffer_new(sizeof(lt_Op)); 1796 | fn->fn.constants = lt_buffer_new(sizeof(lt_Value)); 1797 | if (vm->generate_debug) 1798 | { 1799 | fn->fn.debug = vm->alloc(sizeof(lt_DebugInfo)); 1800 | fn->fn.debug->locations = lt_buffer_new(sizeof(lt_DebugLoc)); 1801 | fn->fn.debug->module_name = name; 1802 | } 1803 | 1804 | lt_Op op = { LT_OP_PUSH, 0 }; 1805 | lt_buffer_push(vm, &fn->fn.code, &op); 1806 | 1807 | _lt_compile_body(vm, p, name, &fn->fn.debug->locations, &node->fn.body, node->fn.scope, &fn->fn.code, &fn->fn.constants); 1808 | 1809 | lt_Op op2 = { LT_OP_RET, 0 }; 1810 | lt_buffer_push(vm, &fn->fn.code, &op2); 1811 | 1812 | ((lt_Op*)lt_buffer_at(&fn->fn.code, 0))->arg = node->fn.scope->locals.length; 1813 | 1814 | lt_Value as_val = LT_VALUE_OBJECT(fn); 1815 | uint16_t idx = _lt_push_constant(vm, constants, as_val); 1816 | OPARG(PUSHC, idx); 1817 | 1818 | if (node->fn.scope->upvals.length > 0) 1819 | { 1820 | lt_Buffer* upvals = &node->fn.scope->upvals; 1821 | // this is actually a closure 1822 | 1823 | for (int i = upvals->length - 1; i >= 0; i--) 1824 | { 1825 | uint32_t idx = _lt_find_local(vm, scope, (lt_Token*)lt_buffer_at(upvals, i)); 1826 | if ((idx & UPVAL_BIT) == UPVAL_BIT) OPARG(LOADUP, idx & 0xFFFF) 1827 | else OPARG(LOAD, idx & 0xFFFF); 1828 | } 1829 | 1830 | OPARG(CLOSE, upvals->length); 1831 | } 1832 | } break; 1833 | 1834 | case LT_AST_NODE_CALL: { 1835 | lt_AstNode** arg = node->call.args; 1836 | uint8_t narg = 0; 1837 | while (*arg) 1838 | { 1839 | narg++; 1840 | _lt_compile_node(vm, p, name, debug, *arg++, scope, code_body, constants); 1841 | } 1842 | 1843 | _lt_compile_node(vm, p, name, debug, node->call.callee, scope, code_body, constants); 1844 | OPARG(CALL, narg); 1845 | } break; 1846 | 1847 | case LT_AST_NODE_RETURN: { 1848 | if (node->ret.expr) 1849 | { 1850 | _lt_compile_node(vm, p, name, debug, node->ret.expr, scope, code_body, constants); 1851 | OPARG(RET, 1); 1852 | } 1853 | else OP(RET); 1854 | } break; 1855 | 1856 | #define REG_JMP() branch_stack[n_branches++] = code_body->length; OP(NOP); 1857 | 1858 | case LT_AST_NODE_IF: { 1859 | uint8_t n_branches = 0; 1860 | uint32_t branch_stack[32]; 1861 | 1862 | _lt_compile_node(vm, p, name, debug, node->branch.expr, scope, code_body, constants); 1863 | uint32_t jidx = code_body->length; 1864 | OP(JMPC); 1865 | 1866 | _lt_compile_body(vm, p, name, debug, &node->branch.body, scope, code_body, constants); 1867 | REG_JMP(); 1868 | 1869 | ((lt_Op*)lt_buffer_at(code_body, jidx))->arg = code_body->length - jidx - 1; 1870 | 1871 | uint8_t has_elseif = 0, has_else = 0; 1872 | 1873 | lt_AstNode* next = node->branch.next; 1874 | while (next) 1875 | { 1876 | if (next->type == LT_AST_NODE_ELSEIF) 1877 | { 1878 | has_elseif = 1; 1879 | 1880 | if (has_else) lt_error(vm, "'else' must be last in if-chain!"); 1881 | 1882 | _lt_compile_node(vm, p, name, debug, next->branch.expr, scope, code_body, constants); 1883 | uint32_t jidx = code_body->length; 1884 | OP(JMPC); 1885 | 1886 | _lt_compile_body(vm, p, name, debug, &next->branch.body, scope, code_body, constants); 1887 | REG_JMP(); 1888 | 1889 | ((lt_Op*)lt_buffer_at(code_body, jidx))->arg = code_body->length - jidx - 1; 1890 | } 1891 | else 1892 | { 1893 | has_else = 1; 1894 | 1895 | _lt_compile_body(vm, p, name, debug, &next->branch.body, scope, code_body, constants); 1896 | } 1897 | 1898 | next = next->branch.next; 1899 | } 1900 | 1901 | if (has_elseif || has_else) 1902 | { 1903 | for (uint32_t i = 0; i < n_branches; i++) 1904 | { 1905 | uint32_t loc = branch_stack[i]; 1906 | *((lt_Op*)lt_buffer_at(code_body, loc)) = (lt_Op) { LT_OP_JMP, code_body->length - loc - 1 }; 1907 | } 1908 | } 1909 | } break; 1910 | 1911 | case LT_AST_NODE_FOR: { 1912 | _lt_compile_node(vm, p, name, debug, node->loop.iterator, scope, code_body, constants); 1913 | OPARG(STORE, node->loop.closureidx); 1914 | 1915 | uint32_t loop_header = code_body->length; 1916 | OPARG(LOAD, node->loop.closureidx); 1917 | OPARG(CALL, 0); 1918 | OPARG(STORE, node->loop.identifier); 1919 | OPARG(LOAD, node->loop.identifier); 1920 | uint32_t loop_start = code_body->length; 1921 | OPARG(JMPN, 0); 1922 | 1923 | _lt_compile_body(vm, p, name, debug, &node->loop.body, scope, code_body, constants); 1924 | OPARG(JMP, loop_header - code_body->length - 1); 1925 | 1926 | lt_Op* cond = lt_buffer_at(code_body, loop_start); 1927 | cond->arg = code_body->length - loop_start - 1; 1928 | 1929 | for (uint32_t i = loop_start; i < code_body->length; ++i) 1930 | { 1931 | lt_Op* current = lt_buffer_at(code_body, i); 1932 | if (current->op == LT_OP_JMP && current->arg == 0) 1933 | current->arg = code_body->length - i - 1; 1934 | } 1935 | } break; 1936 | 1937 | case LT_AST_NODE_WHILE: { 1938 | uint32_t loop_header = code_body->length; 1939 | _lt_compile_node(vm, p, name, debug, node->loop.iterator, scope, code_body, constants); 1940 | uint32_t loop_start = code_body->length; 1941 | OPARG(JMPC, 0); 1942 | 1943 | _lt_compile_body(vm, p, name, debug, &node->loop.body, scope, code_body, constants); 1944 | OPARG(JMP, loop_header - code_body->length - 1); 1945 | 1946 | lt_Op* cond = lt_buffer_at(code_body, loop_start); 1947 | cond->arg = code_body->length - loop_start - 1; 1948 | 1949 | for (uint32_t i = loop_start; i < code_body->length; ++i) 1950 | { 1951 | lt_Op* current = lt_buffer_at(code_body, i); 1952 | if (current->op == LT_OP_JMP && current->arg == 0) 1953 | current->arg = code_body->length - i - 1; 1954 | } 1955 | } break; 1956 | } 1957 | } 1958 | 1959 | static void _lt_compile_body(lt_VM* vm, lt_Parser* p, const char* name, lt_Buffer* debug, lt_Buffer* ast_body, lt_Scope* scope, lt_Buffer* code_body, lt_Buffer* constants) 1960 | { 1961 | for (uint32_t i = 0; i < ast_body->length; i++) 1962 | { 1963 | lt_AstNode* node = *(lt_AstNode**)lt_buffer_at(ast_body, i); 1964 | _lt_compile_node(vm, p, name, debug, node, scope, code_body, constants); 1965 | } 1966 | } 1967 | 1968 | lt_Value lt_compile(lt_VM* vm, lt_Parser* p) 1969 | { 1970 | lt_Object* chunk = lt_allocate(vm, LT_OBJECT_CHUNK); 1971 | lt_nocollect(vm, chunk); 1972 | 1973 | chunk->chunk.code = lt_buffer_new(sizeof(lt_Op)); 1974 | chunk->chunk.constants = lt_buffer_new(sizeof(lt_Value)); 1975 | 1976 | if(p->tkn->module) 1977 | { 1978 | uint32_t len = (uint32_t)strlen(p->tkn->module); 1979 | chunk->chunk.name = vm->alloc(len + 1); 1980 | memcpy(chunk->chunk.name, p->tkn->module, len); 1981 | chunk->chunk.name[len] = 0; 1982 | } 1983 | 1984 | if (vm->generate_debug) 1985 | { 1986 | chunk->chunk.debug = vm->alloc(sizeof(lt_DebugInfo)); 1987 | chunk->chunk.debug->locations = lt_buffer_new(sizeof(lt_DebugLoc)); 1988 | chunk->chunk.debug->module_name = chunk->chunk.name; 1989 | } 1990 | 1991 | lt_Op op = { LT_OP_PUSH, 0 }; 1992 | lt_buffer_push(vm, &chunk->chunk.code, &op); 1993 | 1994 | _lt_compile_body(vm, p, chunk->chunk.name, &chunk->chunk.debug->locations, &p->root->chunk.body, p->root->chunk.scope, &chunk->chunk.code, &chunk->chunk.constants); 1995 | 1996 | lt_Op op2 = { LT_OP_RET, 0 }; 1997 | lt_buffer_push(vm, &chunk->chunk.code, &op2); 1998 | 1999 | ((lt_Op*)lt_buffer_at(&chunk->chunk.code, 0))->arg = p->root->chunk.scope->locals.length; 2000 | 2001 | lt_Value as_val = LT_VALUE_OBJECT(chunk); 2002 | return as_val; 2003 | } 2004 | 2005 | void lt_free_scope(lt_VM* vm, lt_Scope* scope) 2006 | { 2007 | lt_buffer_destroy(vm, &scope->locals); 2008 | lt_buffer_destroy(vm, &scope->upvals); 2009 | } 2010 | 2011 | void lt_free_parser(lt_VM* vm, lt_Parser* p) 2012 | { 2013 | for (uint32_t i = 0; i < p->ast_nodes.length; i++) 2014 | { 2015 | lt_AstNode* entry = *(lt_AstNode**)lt_buffer_at(&p->ast_nodes, i); 2016 | 2017 | switch (entry->type) 2018 | { 2019 | case LT_AST_NODE_CHUNK: lt_buffer_destroy(vm, &entry->chunk.body); lt_free_scope(vm, entry->chunk.scope); break; 2020 | case LT_AST_NODE_TABLE: lt_buffer_destroy(vm, &entry->table.keys); lt_buffer_destroy(vm, &entry->table.values); break; 2021 | case LT_AST_NODE_FN: /*lt_buffer_destroy(vm, &entry->fn.body);*/ lt_free_scope(vm, entry->fn.scope); break; 2022 | case LT_AST_NODE_IF: case LT_AST_NODE_ELSEIF: case LT_AST_NODE_ELSE: lt_buffer_destroy(vm, &entry->branch.body); break; 2023 | } 2024 | 2025 | vm->free(entry); 2026 | } 2027 | 2028 | lt_buffer_destroy(vm, &p->ast_nodes); 2029 | } 2030 | 2031 | void lt_free_tokenizer(lt_VM* vm, lt_Tokenizer* tok) 2032 | { 2033 | lt_buffer_destroy(vm, &tok->token_buffer); 2034 | 2035 | for (uint32_t i = 0; i < tok->identifier_buffer.length; ++i) 2036 | vm->free(((lt_Identifier*)lt_buffer_at(&tok->identifier_buffer, i))->name); 2037 | lt_buffer_destroy(vm, &tok->identifier_buffer); 2038 | 2039 | 2040 | for (uint32_t i = 0; i < tok->literal_buffer.length; ++i) 2041 | { 2042 | lt_Literal* lit = lt_buffer_at(&tok->literal_buffer, i); 2043 | if (lit->type == LT_TOKEN_STRING_LITERAL) 2044 | { 2045 | vm->free(lit->string); 2046 | } 2047 | } 2048 | lt_buffer_destroy(vm, &tok->literal_buffer); 2049 | } 2050 | 2051 | lt_Value lt_loadstring(lt_VM* vm, const char* source, const char* mod_name) 2052 | { 2053 | lt_Tokenizer tok = lt_tokenize(vm, source, mod_name); 2054 | if (!tok.is_valid) 2055 | { 2056 | lt_free_tokenizer(vm, &tok); 2057 | return LT_VALUE_NULL; 2058 | } 2059 | 2060 | lt_Parser p = lt_parse(vm, &tok); 2061 | if (!p.is_valid) 2062 | { 2063 | lt_free_parser(vm, &p); 2064 | return LT_VALUE_NULL; 2065 | } 2066 | 2067 | lt_Value c = lt_compile(vm, &p); 2068 | 2069 | lt_free_parser(vm, &p); 2070 | lt_free_tokenizer(vm, &tok); 2071 | 2072 | return c; 2073 | } 2074 | 2075 | uint32_t lt_dostring(lt_VM* vm, const char* source, const char* mod_name) 2076 | { 2077 | lt_Value callable = lt_loadstring(vm, source, mod_name); 2078 | return callable == LT_VALUE_NULL ? 0 : lt_exec(vm, callable, 0); 2079 | } 2080 | 2081 | #define HASH(x) (LT_IS_OBJECT(x) ? ((x >> 2) % 16) : (x % 16)) 2082 | 2083 | lt_TablePair* _lt_table_index(lt_VM* vm, lt_Value table, lt_Value key, uint8_t alloc) 2084 | { 2085 | uint8_t bucket = HASH(key); 2086 | lt_Buffer* buf = LT_GET_OBJECT(table)->table.buckets + bucket; 2087 | if (alloc && buf->element_size == 0) *buf = lt_buffer_new(sizeof(lt_TablePair)); 2088 | 2089 | for (uint32_t i = 0; i < buf->length; i++) 2090 | { 2091 | lt_TablePair* p = lt_buffer_at(buf, i); 2092 | if (lt_equals(p->key, key)) 2093 | { 2094 | return p; 2095 | } 2096 | } 2097 | 2098 | return 0; 2099 | } 2100 | 2101 | lt_Value lt_make_table(lt_VM* vm) 2102 | { 2103 | return LT_VALUE_OBJECT(lt_allocate(vm, LT_OBJECT_TABLE)); 2104 | } 2105 | 2106 | lt_Value lt_table_set(lt_VM* vm, lt_Value table, lt_Value key, lt_Value val) 2107 | { 2108 | if (!LT_IS_TABLE(table)) return LT_VALUE_NULL; 2109 | lt_TablePair* p = _lt_table_index(vm, table, key, 1); 2110 | if (p) 2111 | { 2112 | p->value = val; 2113 | return val; 2114 | } 2115 | 2116 | uint8_t bucket = HASH(key); 2117 | lt_Buffer* buf = LT_GET_OBJECT(table)->table.buckets + bucket; 2118 | lt_TablePair newpair = { key, val }; 2119 | lt_buffer_push(vm, buf, &newpair); 2120 | return val; 2121 | } 2122 | 2123 | lt_Value lt_table_get(lt_VM* vm, lt_Value table, lt_Value key) 2124 | { 2125 | lt_TablePair* p = _lt_table_index(vm, table, key, 0); 2126 | if (p) return p->value; 2127 | return LT_VALUE_NULL; 2128 | } 2129 | 2130 | uint8_t lt_table_pop(lt_VM* vm, lt_Value table, lt_Value key) 2131 | { 2132 | return lt_table_set(vm, table, key, LT_VALUE_NULL) == LT_VALUE_NULL; 2133 | } 2134 | 2135 | lt_Value lt_make_array(lt_VM* vm) 2136 | { 2137 | return LT_VALUE_OBJECT(lt_allocate(vm, LT_OBJECT_ARRAY)); 2138 | } 2139 | 2140 | lt_Value lt_array_push(lt_VM* vm, lt_Value array, lt_Value val) 2141 | { 2142 | if (!LT_IS_ARRAY(array)) return LT_VALUE_NULL; 2143 | lt_Object* arr = LT_GET_OBJECT(array); 2144 | if (arr->array.element_size == 0) arr->array = lt_buffer_new(sizeof(lt_Value)); 2145 | lt_buffer_push(vm, &arr->array, &val); 2146 | return val; 2147 | } 2148 | 2149 | lt_Value* lt_array_at(lt_Value array, uint32_t idx) 2150 | { 2151 | if (!LT_IS_ARRAY(array)) return <_NULL; 2152 | lt_Object* arr = LT_GET_OBJECT(array); 2153 | return lt_buffer_at(&arr->array, idx); 2154 | } 2155 | 2156 | lt_Value lt_array_remove(lt_VM* vm, lt_Value array, uint32_t idx) 2157 | { 2158 | if (!LT_IS_ARRAY(array)) return LT_VALUE_NULL; 2159 | lt_Object* arr = LT_GET_OBJECT(array); 2160 | lt_Value old = *(lt_Value*)lt_buffer_at(&arr->array, idx); 2161 | lt_buffer_cycle(&arr->array, idx); 2162 | return old; 2163 | } 2164 | 2165 | uint32_t lt_array_length(lt_Value array) 2166 | { 2167 | if (!LT_IS_ARRAY(array)) return 0; 2168 | lt_Object* arr = LT_GET_OBJECT(array); 2169 | return arr->array.length; 2170 | } 2171 | 2172 | lt_Value lt_make_native(lt_VM* vm, lt_NativeFn fn) 2173 | { 2174 | lt_Object* obj = lt_allocate(vm, LT_OBJECT_NATIVEFN); 2175 | obj->native = fn; 2176 | return LT_VALUE_OBJECT(obj); 2177 | } 2178 | 2179 | lt_Value lt_make_ptr(lt_VM* vm, void* ptr) 2180 | { 2181 | lt_Object* obj = lt_allocate(vm, LT_OBJECT_PTR); 2182 | obj->ptr = ptr; 2183 | return LT_VALUE_OBJECT(obj); 2184 | } 2185 | 2186 | void* lt_get_ptr(lt_Value ptr) 2187 | { 2188 | lt_Object* obj = LT_GET_OBJECT(ptr); 2189 | return obj->ptr; 2190 | } 2191 | -------------------------------------------------------------------------------- /src/little.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef uint64_t lt_Value; 6 | 7 | // IEEE 756 DOUBLE S[Exponent-][Mantissa------------------------------------------] 8 | #define LT_SIGN_BIT (0b1000000000000000000000000000000000000000000000000000000000000000) 9 | #define LT_EXPONENT (0b0111111111110000000000000000000000000000000000000000000000000000) 10 | #define LT_QNAN_BIT (0b0000000000001000000000000000000000000000000000000000000000000000) 11 | #define LT_TYPE_MASK (0b0000000000000111000000000000000000000000000000000000000000000000) 12 | #define LT_VALUE_MASK (0b0000000000000000111111111111111111111111111111111111111111111111) 13 | 14 | #define LT_NAN_MASK (LT_EXPONENT | LT_QNAN_BIT) 15 | 16 | #define LT_TYPE_NULL (0b0000000000000011000000000000000000000000000000000000000000000000) 17 | #define LT_TYPE_BOOL (0b0000000000000001000000000000000000000000000000000000000000000000) 18 | #define LT_TYPE_STRING (0b0000000000000010000000000000000000000000000000000000000000000000) 19 | #define LT_TYPE_OBJECT (0b0000000000000101000000000000000000000000000000000000000000000000) 20 | 21 | #define LT_VALUE_NULL ((lt_Value)(LT_NAN_MASK | LT_TYPE_NULL)) 22 | #define LT_VALUE_FALSE ((lt_Value)(LT_NAN_MASK | LT_TYPE_BOOL)) 23 | #define LT_VALUE_TRUE ((lt_Value)(LT_NAN_MASK | (LT_TYPE_BOOL | 1))) 24 | #define LT_VALUE_NUMBER(x) ((lt_Value)(lt_make_number((double)x))) 25 | #define LT_VALUE_OBJECT(x) ((lt_Value)(LT_NAN_MASK | (LT_TYPE_OBJECT | (uint64_t)x))) 26 | 27 | #define LT_IS_NUMBER(x) (((x) & LT_NAN_MASK) != LT_NAN_MASK) 28 | #define LT_IS_NULL(x) ((x) == LT_VALUE_NULL) 29 | #define LT_IS_BOOL(x) (x == LT_VALUE_TRUE || x == LT_VALUE_FALSE) 30 | #define LT_IS_TRUE(x) (x == LT_VALUE_TRUE) 31 | #define LT_IS_FALSE(x) (x == LT_VALUE_FALSE) 32 | #define LT_IS_TRUTHY(x) (!(x == LT_VALUE_FALSE || x == LT_VALUE_NULL)) 33 | #define LT_IS_STRING(x) (!LT_IS_NUMBER(x) && (x & LT_TYPE_MASK) == LT_TYPE_STRING) 34 | #define LT_IS_OBJECT(x) (!LT_IS_NUMBER(x) && (x & LT_TYPE_MASK) == LT_TYPE_OBJECT) 35 | #define LT_IS_TABLE(x) (LT_IS_OBJECT(x) && LT_GET_OBJECT(x)->type == LT_OBJECT_TABLE) 36 | #define LT_IS_ARRAY(x) (LT_IS_OBJECT(x) && LT_GET_OBJECT(x)->type == LT_OBJECT_ARRAY) 37 | #define LT_IS_FUNCTION(x) (LT_IS_OBJECT(x) && LT_GET_OBJECT(x)->type == LT_OBJECT_FN) 38 | #define LT_IS_CLOSURE(x) (LT_IS_OBJECT(x) && LT_GET_OBJECT(x)->type == LT_OBJECT_CLOSURE) 39 | #define LT_IS_NATIVE(x) (LT_IS_OBJECT(x) && LT_GET_OBJECT(x)->type == LT_OBJECT_NATIVEFN) 40 | #define LT_IS_PTR(x) (LT_IS_OBJECT(x) && LT_GET_OBJECT(x)->type == LT_OBJECT_PTR) 41 | 42 | #define LT_GET_NUMBER(x) lt_get_number(x) 43 | #define LT_GET_STRING(vm, x) lt_get_string(vm, x) 44 | #define LT_GET_OBJECT(x) ((lt_Object*)(x & LT_VALUE_MASK)) 45 | 46 | typedef enum { 47 | LT_TOKEN_TRUE_LITERAL, 48 | LT_TOKEN_FALSE_LITERAL, 49 | LT_TOKEN_STRING_LITERAL, 50 | LT_TOKEN_NULL_LITERAL, 51 | LT_TOKEN_NUMBER_LITERAL, 52 | 53 | LT_TOKEN_IDENTIFIER, 54 | 55 | LT_TOKEN_PERIOD, 56 | LT_TOKEN_COMMA, 57 | LT_TOKEN_COLON, 58 | 59 | LT_TOKEN_OPENPAREN, 60 | LT_TOKEN_CLOSEPAREN, 61 | 62 | LT_TOKEN_OPENBRACKET, 63 | LT_TOKEN_CLOSEBRACKET, 64 | 65 | LT_TOKEN_OPENBRACE, 66 | LT_TOKEN_CLOSEBRACE, 67 | 68 | LT_TOKEN_FN, 69 | LT_TOKEN_BREAK, 70 | LT_TOKEN_VAR, 71 | LT_TOKEN_IF, 72 | LT_TOKEN_ELSE, 73 | LT_TOKEN_ELSEIF, 74 | LT_TOKEN_FOR, 75 | LT_TOKEN_IN, 76 | LT_TOKEN_WHILE, 77 | LT_TOKEN_RETURN, 78 | 79 | LT_TOKEN_PLUS, 80 | LT_TOKEN_MINUS, 81 | LT_TOKEN_NEGATE, 82 | LT_TOKEN_MULTIPLY, 83 | LT_TOKEN_DIVIDE, 84 | LT_TOKEN_ASSIGN, 85 | LT_TOKEN_EQUALS, 86 | LT_TOKEN_NOTEQUALS, 87 | LT_TOKEN_GT, 88 | LT_TOKEN_GTE, 89 | LT_TOKEN_LT, 90 | LT_TOKEN_LTE, 91 | LT_TOKEN_AND, 92 | LT_TOKEN_OR, 93 | LT_TOKEN_NOT, 94 | 95 | LT_TOKEN_END, 96 | } lt_TokenType; 97 | 98 | typedef struct { 99 | lt_TokenType type; 100 | uint16_t line, col, idx; 101 | } lt_Token; 102 | 103 | typedef struct { 104 | void* data; 105 | uint32_t length, capacity, element_size; 106 | } lt_Buffer; 107 | 108 | typedef struct { 109 | lt_TokenType type; 110 | union { 111 | char* string; 112 | double number; 113 | }; 114 | } lt_Literal; 115 | 116 | typedef struct { 117 | char* name; 118 | uint32_t num_references; 119 | } lt_Identifier; 120 | 121 | typedef struct { 122 | lt_Buffer token_buffer; 123 | lt_Buffer literal_buffer; 124 | lt_Buffer identifier_buffer; 125 | 126 | const char* source; 127 | const char* module; 128 | 129 | uint8_t is_valid; 130 | } lt_Tokenizer; 131 | 132 | typedef enum { 133 | LT_AST_NODE_ERROR, 134 | LT_AST_NODE_EMPTY, 135 | LT_AST_NODE_CHUNK, 136 | 137 | LT_AST_NODE_LITERAL, 138 | LT_AST_NODE_TABLE, 139 | LT_AST_NODE_ARRAY, 140 | LT_AST_NODE_IDENTIFIER, 141 | LT_AST_NODE_INDEX, 142 | LT_AST_NODE_BINARYOP, 143 | LT_AST_NODE_UNARYOP, 144 | LT_AST_NODE_DECLARE, 145 | LT_AST_NODE_ASSIGN, 146 | LT_AST_NODE_FN, 147 | LT_AST_NODE_CALL, 148 | LT_AST_NODE_RETURN, 149 | LT_AST_NODE_IF, 150 | LT_AST_NODE_ELSE, 151 | LT_AST_NODE_ELSEIF, 152 | LT_AST_NODE_FOR, 153 | LT_AST_NODE_WHILE, 154 | LT_AST_NODE_BREAK, 155 | } lt_AstNodeType; 156 | 157 | struct lt_AstNode; 158 | struct lt_Scope; 159 | 160 | typedef struct 161 | { 162 | uint16_t line, col; 163 | } lt_DebugLoc; 164 | 165 | typedef struct 166 | { 167 | const char* module_name; 168 | lt_Buffer locations; 169 | } lt_DebugInfo; 170 | 171 | typedef struct lt_AstNode { 172 | lt_AstNodeType type; 173 | lt_DebugLoc loc; 174 | 175 | union { 176 | struct { 177 | char* name; 178 | lt_Buffer body; 179 | struct lt_Scope* scope; 180 | } chunk; 181 | 182 | struct { 183 | lt_Token* token; 184 | } literal; 185 | 186 | struct { 187 | lt_Buffer keys; 188 | lt_Buffer values; 189 | } table; 190 | 191 | struct { 192 | lt_Buffer values; 193 | } array; 194 | 195 | struct { 196 | lt_Token* token; 197 | } identifier; 198 | 199 | struct { 200 | struct lt_AstNode* source; 201 | struct lt_AstNode* idx; 202 | } index; 203 | 204 | struct { 205 | lt_TokenType type; 206 | struct lt_AstNode* left; 207 | struct lt_AstNode* right; 208 | } binary_op; 209 | 210 | struct { 211 | lt_TokenType type; 212 | struct lt_AstNode* expr; 213 | } unary_op; 214 | 215 | struct { 216 | lt_Token* identifier; 217 | struct lt_AstNode* expr; 218 | } declare; 219 | 220 | struct { 221 | struct lt_AstNode* left; 222 | struct lt_AstNode* right; 223 | } assign; 224 | 225 | struct { 226 | lt_Token* args[16]; 227 | struct lt_Scope* scope; 228 | lt_Buffer body; 229 | } fn; 230 | 231 | struct { 232 | struct lt_AstNode* callee; 233 | struct lt_AstNode* args[16]; 234 | } call; 235 | 236 | struct { 237 | struct lt_AstNode* expr; 238 | } ret; 239 | 240 | struct { 241 | struct lt_AstNode* expr; 242 | lt_Buffer body; 243 | struct lt_AstNode* next; 244 | } branch; 245 | 246 | struct { 247 | uint16_t identifier, closureidx; 248 | struct lt_AstNode* iterator; 249 | lt_Buffer body; 250 | } loop; 251 | }; 252 | } lt_AstNode; 253 | 254 | typedef struct lt_Scope { 255 | struct lt_Scope* last; 256 | 257 | lt_Token* start; 258 | lt_Buffer locals; 259 | lt_Buffer upvals; 260 | lt_Token* end; 261 | } lt_Scope; 262 | 263 | typedef struct { 264 | lt_Buffer ast_nodes; 265 | lt_AstNode* root; 266 | 267 | lt_Tokenizer* tkn; 268 | lt_Scope* current; 269 | 270 | uint8_t is_valid; 271 | } lt_Parser; 272 | 273 | typedef struct { 274 | lt_Value key, value; 275 | } lt_TablePair; 276 | 277 | typedef struct { 278 | lt_Buffer buckets[16]; 279 | } lt_Table; 280 | 281 | typedef enum { 282 | LT_OBJECT_CHUNK, 283 | LT_OBJECT_FN, 284 | LT_OBJECT_CLOSURE, 285 | LT_OBJECT_TABLE, 286 | LT_OBJECT_ARRAY, 287 | LT_OBJECT_NATIVEFN, 288 | LT_OBJECT_PTR, 289 | } lt_ObjectType; 290 | 291 | struct lt_VM; 292 | 293 | typedef uint8_t(*lt_NativeFn)(struct lt_VM* vm, uint8_t argc); 294 | 295 | typedef struct { 296 | lt_ObjectType type; 297 | 298 | union 299 | { 300 | struct 301 | { 302 | lt_Buffer code; 303 | lt_Buffer constants; 304 | char* name; 305 | lt_DebugInfo* debug; 306 | } chunk; 307 | 308 | struct 309 | { 310 | uint8_t arity; 311 | lt_Buffer code; 312 | lt_Buffer constants; 313 | lt_DebugInfo* debug; 314 | } fn; 315 | 316 | struct 317 | { 318 | char* string; 319 | uint16_t len; 320 | } string; 321 | 322 | struct 323 | { 324 | lt_Value function; 325 | lt_Buffer captures; 326 | } closure; 327 | 328 | 329 | lt_Table table; 330 | lt_Buffer array; 331 | lt_NativeFn native; 332 | void* ptr; 333 | }; 334 | 335 | uint8_t markbit : 1; 336 | } lt_Object; 337 | 338 | typedef struct { 339 | uint8_t op; 340 | int8_t arg; 341 | } lt_Op; 342 | 343 | typedef struct lt_Frame { 344 | lt_Object* callee; 345 | lt_Buffer* code; 346 | lt_Buffer* constants; 347 | lt_Buffer* upvals; 348 | lt_Value* start; 349 | lt_Op** ip; 350 | } lt_Frame; 351 | 352 | typedef void* (*lt_AllocFn)(size_t); 353 | typedef void (*lt_FreeFn)(void*); 354 | typedef void (*lt_ErrorFn)(struct lt_VM* vm, const char*); 355 | 356 | #ifndef LT_STACK_SIZE 357 | #define LT_STACK_SIZE 256 358 | #endif 359 | 360 | #ifndef LT_CALLSTACK_SIZE 361 | #define LT_CALLSTACK_SIZE 32 362 | #endif 363 | 364 | #ifndef LT_DEDUP_TABLE_SIZE 365 | #define LT_DEDUP_TABLE_SIZE 64 366 | #endif 367 | 368 | typedef struct { 369 | lt_Buffer heap; 370 | lt_Buffer keepalive; 371 | 372 | lt_Value* top; 373 | lt_Value stack[LT_STACK_SIZE]; 374 | 375 | uint16_t depth; 376 | lt_Frame callstack[LT_CALLSTACK_SIZE]; 377 | lt_Frame* current; 378 | 379 | lt_Buffer strings[LT_DEDUP_TABLE_SIZE]; 380 | 381 | lt_Value global; 382 | 383 | lt_AllocFn alloc; 384 | lt_FreeFn free; 385 | lt_ErrorFn error; 386 | 387 | void* error_buf; 388 | uint8_t generate_debug; 389 | } lt_VM; 390 | 391 | lt_VM* lt_open(lt_AllocFn alloc, lt_FreeFn free, lt_ErrorFn error); 392 | void lt_destroy(lt_VM* vm); 393 | 394 | lt_Buffer lt_buffer_new(uint32_t element_size); 395 | void lt_buffer_destroy(lt_VM* vm, lt_Buffer* buf); 396 | 397 | lt_Object* lt_allocate(lt_VM* vm, lt_ObjectType type); 398 | void lt_free(lt_VM* vm, uint32_t heapidx); 399 | 400 | void lt_nocollect(lt_VM* vm, lt_Object* obj); 401 | void lt_resumecollect(lt_VM* vm, lt_Object* obj); 402 | uint32_t lt_collect(lt_VM* vm); 403 | 404 | void lt_push(lt_VM* vm, lt_Value val); 405 | lt_Value lt_pop(lt_VM* vm); 406 | lt_Value lt_at(lt_VM* vm, uint32_t idx); 407 | 408 | void lt_close(lt_VM* vm, uint8_t count); 409 | lt_Value lt_getupval(lt_VM* vm, uint8_t idx); 410 | void lt_setupval(lt_VM* vm, uint8_t idx, lt_Value val); 411 | 412 | uint16_t lt_exec(lt_VM* vm, lt_Value callable, uint8_t argc); 413 | void lt_error(lt_VM* vm, const char* msg); 414 | void lt_runtime_error(lt_VM* vm, const char* message); 415 | 416 | lt_Tokenizer lt_tokenize(lt_VM* vm, const char* source, const char* mod_name); 417 | lt_Parser lt_parse(lt_VM* vm, lt_Tokenizer* tkn); 418 | lt_Value lt_compile(lt_VM* vm, lt_Parser* p); 419 | 420 | void lt_free_parser(lt_VM* vm, lt_Parser* p); 421 | void lt_free_tokenizer(lt_VM* vm, lt_Tokenizer* tok); 422 | 423 | lt_Value lt_loadstring(lt_VM* vm, const char* source, const char* mod_name); 424 | uint32_t lt_dostring(lt_VM* vm, const char* source, const char* mod_name); 425 | 426 | lt_Value lt_make_number(double n); 427 | double lt_get_number(lt_Value v); 428 | 429 | lt_Value lt_make_string(lt_VM* vm, const char* string); 430 | const char* lt_get_string(lt_VM* vm, lt_Value value); 431 | 432 | uint8_t lt_equals(lt_Value a, lt_Value b); 433 | 434 | lt_Value lt_make_table(lt_VM* vm); 435 | lt_Value lt_table_set(lt_VM* vm, lt_Value table, lt_Value key, lt_Value val); 436 | lt_Value lt_table_get(lt_VM* vm, lt_Value table, lt_Value key); 437 | uint8_t lt_table_pop(lt_VM* vm, lt_Value table, lt_Value key); 438 | 439 | lt_Value lt_make_array(lt_VM* vm); 440 | lt_Value lt_array_push(lt_VM* vm, lt_Value array, lt_Value val); 441 | lt_Value* lt_array_at(lt_Value array, uint32_t idx); 442 | lt_Value lt_array_remove(lt_VM* vm, lt_Value array, uint32_t idx); 443 | uint32_t lt_array_length(lt_Value array); 444 | 445 | lt_Value lt_make_native(lt_VM* vm, lt_NativeFn fn); 446 | lt_Value lt_make_ptr(lt_VM* vm, void* ptr); 447 | void* lt_get_ptr(lt_Value ptr); -------------------------------------------------------------------------------- /src/little_std.c: -------------------------------------------------------------------------------- 1 | #include "little_std.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | void ltstd_open_all(lt_VM* vm) 11 | { 12 | ltstd_open_io(vm); 13 | ltstd_open_math(vm); 14 | ltstd_open_array(vm); 15 | ltstd_open_string(vm); 16 | ltstd_open_gc(vm); 17 | } 18 | 19 | char* ltstd_tostring(lt_VM* vm, lt_Value val) 20 | { 21 | char scratch[256]; 22 | uint8_t len = 0; 23 | 24 | if (LT_IS_NUMBER(val)) len = sprintf_s(scratch, 256, "%f", LT_GET_NUMBER(val)); 25 | if (LT_IS_NULL(val)) len = sprintf_s(scratch, 256, "null"); 26 | if (LT_IS_TRUE(val)) len = sprintf_s(scratch, 256, "true"); 27 | if (LT_IS_FALSE(val)) len = sprintf_s(scratch, 256, "false"); 28 | if (LT_IS_STRING(val)) len = sprintf_s(scratch, 256, "%s", lt_get_string(vm, val));; 29 | 30 | if (LT_IS_OBJECT(val)) 31 | { 32 | lt_Object* obj = LT_GET_OBJECT(val); 33 | switch (obj->type) 34 | { 35 | case LT_OBJECT_CHUNK: len = sprintf_s(scratch, 256, "chunk 0x%llx", (uintptr_t)obj); break; 36 | case LT_OBJECT_CLOSURE: len = sprintf_s(scratch, 256, "closure 0x%llx | %d upvals", (uintptr_t)LT_GET_OBJECT(obj->closure.function), obj->closure.captures.length); break; 37 | case LT_OBJECT_FN: len = sprintf_s(scratch, 256, "function 0x%llx", (uintptr_t)obj); break; 38 | case LT_OBJECT_TABLE: len = sprintf_s(scratch, 256, "table 0x%llx", (uintptr_t)obj); break; 39 | case LT_OBJECT_ARRAY: len = sprintf_s(scratch, 256, "array | %d", lt_array_length(val)); break; 40 | case LT_OBJECT_NATIVEFN: len = sprintf_s(scratch, 256, "native 0x%llx", (uintptr_t)obj); break; 41 | } 42 | } 43 | 44 | char* str = vm->alloc(len + 1); 45 | memcpy(str, scratch, len); 46 | str[len] = 0; 47 | 48 | return str; 49 | } 50 | 51 | static uint8_t _lt_print(lt_VM* vm, uint8_t argc) 52 | { 53 | for (int16_t i = argc - 1; i >= 0; --i) 54 | { 55 | char* str = ltstd_tostring(vm, *(vm->top - 1 - i)); 56 | printf("%s", str); 57 | vm->free(str); 58 | 59 | if (i > 0) printf(" "); 60 | } 61 | 62 | for (int16_t i = argc - 1; i >= 0; --i) lt_pop(vm); 63 | 64 | printf("\n"); 65 | return 0; 66 | } 67 | 68 | static uint8_t _lt_clock(lt_VM* vm, uint8_t argc) 69 | { 70 | if (argc != 0) lt_runtime_error(vm, "Expected no arguments to io.clock!"); 71 | 72 | clock_t time = clock(); 73 | double in_seconds = (double)time / (double)CLOCKS_PER_SEC; 74 | lt_push(vm, lt_make_number(in_seconds)); 75 | 76 | return 1; 77 | } 78 | 79 | static lt_Value req_table_string; 80 | 81 | static uint8_t _lt_require(lt_VM* vm, uint8_t argc) 82 | { 83 | if (argc != 1) lt_runtime_error(vm, "Expected path argument to io.require!"); 84 | lt_Value reqtable = lt_table_get(vm, vm->global, req_table_string); 85 | if (LT_IS_NULL(reqtable)) 86 | { 87 | reqtable = lt_make_table(vm); 88 | lt_table_set(vm, vm->global, req_table_string, reqtable); 89 | } 90 | 91 | lt_Value path = lt_pop(vm); 92 | lt_Value result = lt_table_get(vm, reqtable, path); 93 | if (!LT_IS_NULL(result)) 94 | { 95 | lt_push(vm, result); 96 | return 1; 97 | } 98 | 99 | FILE* fp = fopen(lt_get_string(vm, path), "rb"); 100 | if (!fp) lt_runtime_error(vm, "Failed to open file for require!"); 101 | 102 | static char text[1 << 20]; 103 | fread(text, 1, sizeof(text), fp); 104 | fclose(fp); 105 | 106 | uint32_t n_results = lt_dostring(vm, text, lt_get_string(vm, path)); 107 | if (n_results == 1) 108 | { 109 | result = lt_pop(vm); 110 | lt_table_set(vm, reqtable, path, result); 111 | lt_push(vm, result); 112 | return 1; 113 | } 114 | else 115 | { 116 | lt_table_set(vm, reqtable, path, LT_VALUE_TRUE); 117 | return 0; 118 | } 119 | } 120 | 121 | #define LT_SIMPLE_MATH_FN(name) \ 122 | static uint8_t _lt_##name(lt_VM* vm, uint8_t argc) \ 123 | { \ 124 | if (argc != 1) lt_runtime_error(vm, "Expected one argument to math." #name "!"); \ 125 | lt_Value arg = lt_pop(vm); \ 126 | if (!LT_IS_NUMBER(arg)) lt_runtime_error(vm, "Expected argument to math." #name " to be number!"); \ 127 | \ 128 | lt_push(vm, LT_VALUE_NUMBER(name(LT_GET_NUMBER(arg)))); \ 129 | return 1; \ 130 | } 131 | 132 | LT_SIMPLE_MATH_FN(sin); 133 | LT_SIMPLE_MATH_FN(cos); 134 | LT_SIMPLE_MATH_FN(tan); 135 | 136 | LT_SIMPLE_MATH_FN(sinh); 137 | LT_SIMPLE_MATH_FN(cosh); 138 | LT_SIMPLE_MATH_FN(tanh); 139 | 140 | LT_SIMPLE_MATH_FN(asin); 141 | LT_SIMPLE_MATH_FN(acos); 142 | LT_SIMPLE_MATH_FN(atan); 143 | 144 | LT_SIMPLE_MATH_FN(round); 145 | LT_SIMPLE_MATH_FN(ceil); 146 | LT_SIMPLE_MATH_FN(floor); 147 | 148 | LT_SIMPLE_MATH_FN(exp); 149 | LT_SIMPLE_MATH_FN(log); 150 | LT_SIMPLE_MATH_FN(log10); 151 | LT_SIMPLE_MATH_FN(sqrt); 152 | LT_SIMPLE_MATH_FN(fabs); 153 | 154 | #define LT_BINARY_MATH_FN(name) \ 155 | static uint8_t _lt_##name(lt_VM* vm, uint8_t argc) \ 156 | { \ 157 | if (argc != 2) lt_runtime_error(vm, "Expected two arguments to math." #name "!"); \ 158 | lt_Value arg1 = lt_pop(vm); \ 159 | lt_Value arg2 = lt_pop(vm); \ 160 | if (!LT_IS_NUMBER(arg1) || !LT_IS_NUMBER(arg2)) lt_runtime_error(vm, "Expected argument to math." #name " to be number!"); \ 161 | \ 162 | lt_push(vm, LT_VALUE_NUMBER(name(LT_GET_NUMBER(arg1), LT_GET_NUMBER(arg2)))); \ 163 | return 1; \ 164 | } 165 | 166 | LT_BINARY_MATH_FN(fmin); 167 | LT_BINARY_MATH_FN(fmax); 168 | LT_BINARY_MATH_FN(pow); 169 | LT_BINARY_MATH_FN(fmod); 170 | 171 | static uint8_t _lt_array_next(lt_VM* vm, uint8_t argc) 172 | { 173 | lt_Value current = lt_getupval(vm, 1); 174 | lt_Value arr = lt_getupval(vm, 0); 175 | 176 | uint32_t idx = lt_get_number(current); 177 | lt_Value to_return = idx >= lt_array_length(arr) ? LT_VALUE_NULL : *lt_array_at(arr, idx); 178 | 179 | lt_setupval(vm, 1, LT_VALUE_NUMBER(idx + 1)); 180 | lt_push(vm, to_return); 181 | 182 | return 1; 183 | } 184 | 185 | static uint8_t _lt_array_each(lt_VM* vm, uint8_t argc) 186 | { 187 | if (argc != 1) lt_runtime_error(vm, "Expected one argument to array.each!"); 188 | 189 | lt_Value arr = lt_pop(vm); 190 | 191 | if (!LT_IS_ARRAY(arr)) lt_runtime_error(vm, "Expected argument to array.each to be array!"); 192 | 193 | uint32_t len = lt_array_length(arr); 194 | 195 | lt_push(vm, lt_make_native(vm, _lt_array_next)); 196 | lt_push(vm, LT_VALUE_NUMBER(0)); 197 | lt_push(vm, arr); 198 | lt_close(vm, 2); 199 | 200 | return 1; 201 | } 202 | 203 | static uint8_t _lt_range_iter(lt_VM* vm, uint8_t argc) 204 | { 205 | lt_Value start = lt_getupval(vm, 2); 206 | lt_Value end = lt_getupval(vm, 1); 207 | lt_Value step = lt_getupval(vm, 0); 208 | 209 | if (lt_get_number(start) >= lt_get_number(end)) { lt_push(vm, LT_VALUE_NULL); return 1; } 210 | 211 | lt_setupval(vm, 2, lt_make_number(lt_get_number(start) + lt_get_number(step))); 212 | 213 | lt_push(vm, start); 214 | return 1; 215 | } 216 | 217 | static uint8_t _lt_range(lt_VM* vm, uint8_t argc) 218 | { 219 | lt_Value start = LT_VALUE_NUMBER(0); 220 | lt_Value end = LT_VALUE_NUMBER(0); 221 | lt_Value step = LT_VALUE_NUMBER(1); 222 | 223 | if (argc == 1) 224 | { 225 | end = lt_pop(vm); 226 | } 227 | else if (argc == 2) 228 | { 229 | end = lt_pop(vm); 230 | start = lt_pop(vm); 231 | } 232 | else if (argc == 3) 233 | { 234 | step = lt_pop(vm); 235 | end = lt_pop(vm); 236 | start = lt_pop(vm); 237 | } 238 | else lt_runtime_error(vm, "Expected 1-3 args for array.range([start,] end [, step]!"); 239 | 240 | if (!LT_IS_NUMBER(start) || !LT_IS_NUMBER(end) || !LT_IS_NUMBER(step)) 241 | lt_runtime_error(vm, "Expected all arguments to array.range to be numbers!"); 242 | 243 | lt_push(vm, lt_make_native(vm, _lt_range_iter)); 244 | lt_push(vm, start); 245 | lt_push(vm, end); 246 | lt_push(vm, step); 247 | lt_close(vm, 3); 248 | 249 | return 1; 250 | } 251 | 252 | static uint8_t _lt_array_len(lt_VM* vm, uint8_t argc) 253 | { 254 | if (argc != 1) lt_runtime_error(vm, "Expected one argument to array.len!"); 255 | lt_Value arr = lt_pop(vm); 256 | if (!LT_IS_ARRAY(arr)) lt_runtime_error(vm, "Expected argument to array.len to be array!"); 257 | 258 | lt_push(vm, lt_make_number(lt_array_length(arr))); 259 | return 1; 260 | } 261 | 262 | static uint8_t _lt_array_pop(lt_VM* vm, uint8_t argc) 263 | { 264 | if (argc != 1) lt_runtime_error(vm, "Expected one argument to array.pop!"); 265 | lt_Value arr = lt_pop(vm); 266 | if (!LT_IS_ARRAY(arr)) lt_runtime_error(vm, "Expected argument to array.pop to be array!"); 267 | 268 | lt_push(vm, lt_array_remove(vm, arr, lt_array_length(arr) - 1)); 269 | return 1; 270 | } 271 | 272 | static uint8_t _lt_array_last(lt_VM* vm, uint8_t argc) 273 | { 274 | if (argc != 1) lt_runtime_error(vm, "Expected one argument to array.last!"); 275 | lt_Value arr = lt_pop(vm); 276 | if (!LT_IS_ARRAY(arr)) lt_runtime_error(vm, "Expected argument to array.last to be array!"); 277 | 278 | lt_push(vm, lt_array_at(arr, lt_array_length(arr) - 1)); 279 | return 1; 280 | } 281 | 282 | static uint8_t _lt_array_push(lt_VM* vm, uint8_t argc) 283 | { 284 | if (argc != 2) lt_runtime_error(vm, "Expected two arguments to array.push!"); 285 | lt_Value arr = lt_pop(vm); 286 | lt_Value val = lt_pop(vm); 287 | if (!LT_IS_ARRAY(arr)) lt_runtime_error(vm, "Expected first argument to array.push to be array!"); 288 | 289 | lt_array_push(vm, arr, val); 290 | return 0; 291 | } 292 | 293 | static uint8_t _lt_array_remove(lt_VM* vm, uint8_t argc) 294 | { 295 | if (argc != 2) lt_runtime_error(vm, "Expected two arguments to array.remove!"); 296 | lt_Value arr = lt_pop(vm); 297 | lt_Value idx = lt_pop(vm); 298 | if (!LT_IS_ARRAY(arr)) lt_runtime_error(vm, "Expected first argument to array.remove to be array!"); 299 | if (!LT_IS_NUMBER(idx)) lt_runtime_error(vm, "Expected second argument to array.remove to be number!"); 300 | 301 | lt_array_remove(vm, arr, (uint32_t)lt_get_number(idx)); 302 | return 0; 303 | } 304 | 305 | static uint8_t _lt_gc_collect(lt_VM* vm, uint8_t argc) 306 | { 307 | if (argc != 0) lt_runtime_error(vm, "Expected no arguments to gc.collect!"); 308 | uint32_t num_collected = lt_collect(vm); 309 | lt_push(vm, LT_VALUE_NUMBER((double)num_collected)); 310 | return 1; 311 | } 312 | 313 | static uint8_t _lt_gc_addroot(lt_VM* vm, uint8_t argc) 314 | { 315 | if (argc != 1) lt_runtime_error(vm, "Expected one argument to gc.addroot!"); 316 | lt_Value val = lt_pop(vm); 317 | if (!LT_IS_OBJECT(val)) lt_runtime_error(vm, "Expected argument to gc.addroot to be object!"); 318 | lt_Object* obj = LT_GET_OBJECT(val); 319 | lt_nocollect(vm, obj); 320 | return 0; 321 | } 322 | 323 | static uint8_t _lt_gc_removeroot(lt_VM* vm, uint8_t argc) 324 | { 325 | if (argc != 1) lt_runtime_error(vm, "Expected one argument to gc.removeroot!"); 326 | lt_Value val = lt_pop(vm); 327 | if (!LT_IS_OBJECT(val)) lt_runtime_error(vm, "Expected argument to gc.removeroot to be object!"); 328 | lt_Object* obj = LT_GET_OBJECT(val); 329 | lt_resumecollect(vm, obj); 330 | return 0; 331 | } 332 | 333 | static uint8_t _lt_string_from(lt_VM* vm, uint8_t argc) 334 | { 335 | if (argc != 1) lt_runtime_error(vm, "Expected one argument to string.from!"); 336 | lt_Value val = lt_pop(vm); 337 | char* temp = ltstd_tostring(vm, val); 338 | lt_Value str = lt_make_string(vm, temp); 339 | vm->free(temp); 340 | lt_push(vm, str); 341 | return 1; 342 | } 343 | 344 | static uint8_t _lt_string_concat(lt_VM* vm, uint8_t argc) 345 | { 346 | if (argc < 2) lt_runtime_error(vm, "Expected at least two arguments to string.concat!"); 347 | 348 | char* accum = 0; uint32_t len = 0; 349 | 350 | for (int32_t i = argc - 1; i >= 0; --i) 351 | { 352 | lt_Value val = *(vm->top - 1 - i); 353 | if (!LT_IS_STRING(val)) lt_runtime_error(vm, "Non-string argument to string.concat!"); 354 | uint32_t oldlen = len; 355 | const char* str = lt_get_string(vm, val); 356 | 357 | char* oldaccum = accum; 358 | len += strlen(str); 359 | 360 | accum = vm->alloc(len + 1); 361 | if (oldaccum) 362 | { 363 | memcpy(accum, oldaccum, oldlen); 364 | vm->free(oldaccum); 365 | } 366 | 367 | memcpy(accum + oldlen, str, len - oldlen); 368 | accum[len] = 0; 369 | } 370 | 371 | lt_push(vm, lt_make_string(vm, accum)); 372 | vm->free(accum); 373 | 374 | return 1; 375 | } 376 | 377 | static uint8_t _lt_string_len(lt_VM* vm, uint8_t argc) 378 | { 379 | if (argc != 1) lt_runtime_error(vm, "Expected one argument to string.len!"); 380 | lt_Value val = lt_pop(vm); 381 | if (!LT_IS_STRING(val)) lt_runtime_error(vm, "Non-string argument to string.len!"); 382 | lt_push(vm, LT_VALUE_NUMBER(strlen(lt_get_string(vm, val)))); 383 | return 1; 384 | } 385 | 386 | static uint8_t _lt_string_sub(lt_VM* vm, uint8_t argc) 387 | { 388 | if (argc < 2) lt_runtime_error(vm, "Expected at least two arguments to string.sub!"); 389 | 390 | lt_Value len = LT_VALUE_NULL; 391 | if (argc == 3) len = lt_pop(vm); 392 | 393 | lt_Value start = lt_pop(vm); 394 | lt_Value str = lt_pop(vm); 395 | 396 | if (!LT_IS_STRING(str)) lt_runtime_error(vm, "Non-string argument to string.sub!"); 397 | if (!LT_IS_NUMBER(start)) lt_runtime_error(vm, "Non-number starting point to string.sub!"); 398 | 399 | const char* cstr = lt_get_string(vm, str); 400 | 401 | if (!LT_IS_NUMBER(len)) 402 | { 403 | len = LT_VALUE_NUMBER(strlen(cstr) - start); 404 | } 405 | 406 | char* newstr = vm->alloc(LT_GET_NUMBER(len) + 1); 407 | memcpy(newstr, cstr + start, len); 408 | 409 | lt_push(vm, lt_make_string(vm, newstr)); 410 | vm->free(newstr); 411 | return 1; 412 | } 413 | 414 | static uint8_t _lt_string_format(lt_VM* vm, uint8_t argc) 415 | { 416 | if (argc < 1) lt_runtime_error(vm, "Expected at least a template string to string.format!"); 417 | lt_Value val = *(vm->top - argc); 418 | if (!LT_IS_STRING(val)) lt_runtime_error(vm, "Non-string argument to string.format!"); 419 | 420 | char output[1024]; 421 | char fmtbuf[32]; 422 | uint16_t o_idx = 0; 423 | 424 | const char* format = lt_get_string(vm, val); 425 | uint8_t current_arg = 1; 426 | 427 | while (*format) 428 | { 429 | if (*format == '%') 430 | { 431 | if (*(format + 1) == '%') 432 | { 433 | output[o_idx++] = '%'; 434 | format += 2; 435 | } 436 | else 437 | { 438 | uint8_t fmtloc = 0; 439 | fmtbuf[fmtloc++] = *format++; 440 | scan_format: switch (*format) 441 | { 442 | case 'd': case 'i': { 443 | fmtbuf[fmtloc++] = *format++; fmtbuf[fmtloc] = 0; 444 | o_idx += sprintf_s(output + o_idx, 1024 - o_idx, fmtbuf, (int32_t)LT_GET_NUMBER(*(vm->top - argc + current_arg++))); 445 | } break; 446 | case 'o': case 'u': case 'x': case 'X': { 447 | fmtbuf[fmtloc++] = *format++; fmtbuf[fmtloc] = 0; 448 | o_idx += sprintf_s(output + o_idx, 1024 - o_idx, fmtbuf, (uint32_t)LT_GET_NUMBER(*(vm->top - argc + current_arg++))); 449 | } break; 450 | case 'e': case 'E': case 'f': case 'g': case 'G': { 451 | fmtbuf[fmtloc++] = *format++; fmtbuf[fmtloc] = 0; 452 | o_idx += sprintf_s(output + o_idx, 1024 - o_idx, fmtbuf, LT_GET_NUMBER(*(vm->top - argc + current_arg++))); 453 | } break; 454 | case 's': { 455 | fmtbuf[fmtloc++] = *format++; fmtbuf[fmtloc] = 0; 456 | o_idx += sprintf_s(output + o_idx, 1024 - o_idx, fmtbuf, lt_get_string(vm, *(vm->top - argc + current_arg++))); 457 | } break; 458 | default: 459 | fmtbuf[fmtloc++] = *format++; 460 | goto scan_format; 461 | break; 462 | } 463 | } 464 | } 465 | else 466 | { 467 | output[o_idx++] = *format++; 468 | } 469 | } 470 | 471 | output[o_idx] = 0; 472 | lt_push(vm, lt_make_string(vm, output)); 473 | return 1; 474 | } 475 | 476 | static uint8_t _lt_string_typeof(lt_VM* vm, uint8_t argc) 477 | { 478 | if (argc != 1) lt_runtime_error(vm, "Expected one argument to string.typeof!"); 479 | lt_Value val = lt_pop(vm); 480 | if (LT_IS_NULL(val)) lt_push(vm, lt_make_string(vm, "null")); 481 | else if (LT_IS_NUMBER(val)) lt_push(vm, lt_make_string(vm, "number")); 482 | else if (LT_IS_BOOL(val)) lt_push(vm, lt_make_string(vm, "boolean")); 483 | else if (LT_IS_STRING(val)) lt_push(vm, lt_make_string(vm, "string")); 484 | else if (LT_IS_FUNCTION(val)) lt_push(vm, lt_make_string(vm, "function")); 485 | else if (LT_IS_CLOSURE(val)) lt_push(vm, lt_make_string(vm, "closure")); 486 | else if (LT_IS_ARRAY(val)) lt_push(vm, lt_make_string(vm, "array")); 487 | else if (LT_IS_TABLE(val)) lt_push(vm, lt_make_string(vm, "table")); 488 | else if (LT_IS_NATIVE(val)) lt_push(vm, lt_make_string(vm, "native")); 489 | else if (LT_IS_PTR(val)) lt_push(vm, lt_make_string(vm, "ptr")); 490 | return 1; 491 | } 492 | 493 | void ltstd_open_io(lt_VM* vm) 494 | { 495 | req_table_string = lt_make_string(vm, "__require"); 496 | 497 | lt_Value t = lt_make_table(vm); 498 | lt_table_set(vm, t, lt_make_string(vm, "print"), lt_make_native(vm, _lt_print)); 499 | lt_table_set(vm, t, lt_make_string(vm, "clock"), lt_make_native(vm, _lt_clock)); 500 | lt_table_set(vm, t, lt_make_string(vm, "require"), lt_make_native(vm, _lt_require)); 501 | 502 | lt_table_set(vm, vm->global, lt_make_string(vm, "io"), t); 503 | } 504 | 505 | void ltstd_open_math(lt_VM* vm) 506 | { 507 | lt_Value t = lt_make_table(vm); 508 | lt_table_set(vm, t, lt_make_string(vm, "sin"), lt_make_native(vm, _lt_sin)); 509 | lt_table_set(vm, t, lt_make_string(vm, "cos"), lt_make_native(vm, _lt_cos)); 510 | lt_table_set(vm, t, lt_make_string(vm, "tan"), lt_make_native(vm, _lt_tan)); 511 | 512 | lt_table_set(vm, t, lt_make_string(vm, "asin"), lt_make_native(vm, _lt_asin)); 513 | lt_table_set(vm, t, lt_make_string(vm, "acos"), lt_make_native(vm, _lt_acos)); 514 | lt_table_set(vm, t, lt_make_string(vm, "atan"), lt_make_native(vm, _lt_atan)); 515 | 516 | lt_table_set(vm, t, lt_make_string(vm, "sinh"), lt_make_native(vm, _lt_sinh)); 517 | lt_table_set(vm, t, lt_make_string(vm, "cosh"), lt_make_native(vm, _lt_cosh)); 518 | lt_table_set(vm, t, lt_make_string(vm, "tanh"), lt_make_native(vm, _lt_tanh)); 519 | 520 | lt_table_set(vm, t, lt_make_string(vm, "floor"), lt_make_native(vm, _lt_floor)); 521 | lt_table_set(vm, t, lt_make_string(vm, "ceil"), lt_make_native(vm, _lt_ceil)); 522 | lt_table_set(vm, t, lt_make_string(vm, "round"), lt_make_native(vm, _lt_round)); 523 | 524 | lt_table_set(vm, t, lt_make_string(vm, "exp"), lt_make_native(vm, _lt_exp)); 525 | lt_table_set(vm, t, lt_make_string(vm, "log"), lt_make_native(vm, _lt_log)); 526 | lt_table_set(vm, t, lt_make_string(vm, "log10"), lt_make_native(vm, _lt_log10)); 527 | lt_table_set(vm, t, lt_make_string(vm, "sqrt"), lt_make_native(vm, _lt_sqrt)); 528 | lt_table_set(vm, t, lt_make_string(vm, "abs"), lt_make_native(vm, _lt_fabs)); 529 | 530 | lt_table_set(vm, t, lt_make_string(vm, "min"), lt_make_native(vm, _lt_fmin)); 531 | lt_table_set(vm, t, lt_make_string(vm, "max"), lt_make_native(vm, _lt_fmax)); 532 | lt_table_set(vm, t, lt_make_string(vm, "pow"), lt_make_native(vm, _lt_pow)); 533 | lt_table_set(vm, t, lt_make_string(vm, "mod"), lt_make_native(vm, _lt_fmod)); 534 | 535 | lt_table_set(vm, t, lt_make_string(vm, "pi"), LT_VALUE_NUMBER(3.14159265358979323846)); 536 | lt_table_set(vm, t, lt_make_string(vm, "e"), LT_VALUE_NUMBER(2.71828182845904523536)); 537 | 538 | lt_table_set(vm, vm->global, lt_make_string(vm, "math"), t); 539 | } 540 | 541 | void ltstd_open_array(lt_VM* vm) 542 | { 543 | lt_Value t = lt_make_table(vm); 544 | lt_table_set(vm, t, lt_make_string(vm, "each"), lt_make_native(vm, _lt_array_each)); 545 | lt_table_set(vm, t, lt_make_string(vm, "range"), lt_make_native(vm, _lt_range)); 546 | 547 | lt_table_set(vm, t, lt_make_string(vm, "len"), lt_make_native(vm, _lt_array_len)); 548 | lt_table_set(vm, t, lt_make_string(vm, "last"), lt_make_native(vm, _lt_array_last)); 549 | lt_table_set(vm, t, lt_make_string(vm, "pop"), lt_make_native(vm, _lt_array_pop)); 550 | lt_table_set(vm, t, lt_make_string(vm, "push"), lt_make_native(vm, _lt_array_push)); 551 | lt_table_set(vm, t, lt_make_string(vm, "remove"), lt_make_native(vm, _lt_array_remove)); 552 | 553 | lt_table_set(vm, vm->global, lt_make_string(vm, "array"), t); 554 | } 555 | 556 | void ltstd_open_string(lt_VM* vm) 557 | { 558 | lt_Value t = lt_make_table(vm); 559 | 560 | lt_table_set(vm, t, lt_make_string(vm, "from"), lt_make_native(vm, _lt_string_from)); 561 | lt_table_set(vm, t, lt_make_string(vm, "concat"), lt_make_native(vm, _lt_string_concat)); 562 | lt_table_set(vm, t, lt_make_string(vm, "len"), lt_make_native(vm, _lt_string_len)); 563 | lt_table_set(vm, t, lt_make_string(vm, "sub"), lt_make_native(vm, _lt_string_sub)); 564 | lt_table_set(vm, t, lt_make_string(vm, "format"), lt_make_native(vm, _lt_string_format)); 565 | 566 | lt_table_set(vm, vm->global, lt_make_string(vm, "string"), t); 567 | } 568 | 569 | void ltstd_open_gc(lt_VM* vm) 570 | { 571 | lt_Value t = lt_make_table(vm); 572 | 573 | lt_table_set(vm, t, lt_make_string(vm, "collect"), lt_make_native(vm, _lt_gc_collect)); 574 | lt_table_set(vm, t, lt_make_string(vm, "addroot"), lt_make_native(vm, _lt_gc_addroot)); 575 | lt_table_set(vm, t, lt_make_string(vm, "removeroot"), lt_make_native(vm, _lt_gc_removeroot)); 576 | 577 | lt_table_set(vm, vm->global, lt_make_string(vm, "gc"), t); 578 | } 579 | -------------------------------------------------------------------------------- /src/little_std.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "little.h" 4 | 5 | void ltstd_open_all(lt_VM* vm); 6 | 7 | char* ltstd_tostring(lt_VM* vm, lt_Value val); 8 | 9 | void ltstd_open_io(lt_VM* vm); 10 | void ltstd_open_math(lt_VM* vm); 11 | void ltstd_open_array(lt_VM* vm); 12 | void ltstd_open_string(lt_VM* vm); 13 | void ltstd_open_gc(lt_VM* vm); 14 | --------------------------------------------------------------------------------