├── .gitignore ├── .vscode ├── launch.json └── settings.json ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── README.md ├── build-wasm.sh ├── scripts ├── anon-func.lua ├── closure.lua ├── closure2.lua ├── closure3.lua ├── closure4.lua ├── concat.lua ├── curry.lua ├── error.lua ├── ex.lua ├── fib-global.lua ├── fib.lua ├── for-up.lua ├── for.lua ├── gc.lua ├── global.lua ├── goto.lua ├── grouping.lua ├── hoist.lua ├── if.lua ├── local-curry.lua ├── loop.lua ├── metacall.lua ├── oop-error.lua ├── oop.lua ├── redefine.lua ├── return.lua ├── return2.lua ├── return3.lua ├── scope.lua ├── scope2.lua ├── scope3.lua ├── scope4.lua ├── silt.lua ├── silt2.lua ├── simple.lua ├── t.lua ├── table.lua └── test.lua ├── src ├── archive │ ├── compiler_old.rs │ ├── environment.rs │ ├── expression.rs │ ├── parser.rs │ ├── resolver.rs │ └── statement.rs ├── chunk.rs ├── code.rs ├── compiler.rs ├── error.rs ├── function.rs ├── interpreter.rs ├── lexer.rs ├── lib.rs ├── lua.rs ├── main.rs ├── prelude.rs ├── standard.rs ├── table.rs ├── token.rs ├── types.rs ├── userdata.rs └── value.rs └── wasm-tester └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug unit tests in library 'silt_lua'", 11 | "cargo": { 12 | "args": [ 13 | "test", 14 | "--no-run", 15 | "--lib", 16 | "--package=silt_lua" 17 | ], 18 | "filter": { 19 | "name": "silt_lua", 20 | "kind": "lib" 21 | } 22 | }, 23 | "args": [], 24 | "cwd": "${workspaceFolder}" 25 | }, 26 | { 27 | "type": "lldb", 28 | "request": "launch", 29 | "name": "Debug executable 'silt_lua_cli'", 30 | "cargo": { 31 | "args": [ 32 | "build", 33 | "--bin=silt_lua_cli", 34 | "--package=silt-lua", 35 | "--features=dev-out" 36 | ], 37 | "filter": { 38 | "name": "silt_lua_cli", 39 | "kind": "bin" 40 | } 41 | }, 42 | "args": [], 43 | "cwd": "${workspaceFolder}" 44 | }, 45 | { 46 | "type": "lldb", 47 | "request": "launch", 48 | "name": "Debug unit tests in executable 'silt_lua_cli'", 49 | "cargo": { 50 | "args": [ 51 | "test", 52 | "--no-run", 53 | "--bin=silt_lua_cli", 54 | "--package=silt_lua" 55 | ], 56 | "filter": { 57 | "name": "silt_lua_cli", 58 | "kind": "bin" 59 | } 60 | }, 61 | "args": [], 62 | "cwd": "${workspaceFolder}" 63 | } 64 | ] 65 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": ["UPVALUE"] 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v0.1.1 2 | 3 | - Fixed a huge bug with scope and some missing control flow logic with `if` statements 4 | - Reworked numeric `for` statements with new OpCode to properly scope the iterator so upvalues close at the end of each iteration. Generic `for` still WIP! 5 | - test coverage baked into the lib test modules now. Covers some basic and some complex features including closures, but more is still needed 6 | 7 | ## v0.1 8 | 9 | - First release! 10 | - Basic lua syntax compliance 11 | - Stack based, reference counted memory management 12 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ahash" 7 | version = "0.8.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" 10 | dependencies = [ 11 | "cfg-if", 12 | "once_cell", 13 | "version_check", 14 | ] 15 | 16 | [[package]] 17 | name = "allocator-api2" 18 | version = "0.2.14" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "c4f263788a35611fba42eb41ff811c5d0360c58b97402570312a350736e2542e" 21 | 22 | [[package]] 23 | name = "bumpalo" 24 | version = "3.13.0" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" 27 | 28 | [[package]] 29 | name = "cfg-if" 30 | version = "1.0.0" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 33 | 34 | [[package]] 35 | name = "hashbrown" 36 | version = "0.14.0" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" 39 | dependencies = [ 40 | "ahash", 41 | "allocator-api2", 42 | ] 43 | 44 | [[package]] 45 | name = "log" 46 | version = "0.4.20" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" 49 | 50 | [[package]] 51 | name = "once_cell" 52 | version = "1.18.0" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" 55 | 56 | [[package]] 57 | name = "proc-macro2" 58 | version = "1.0.66" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" 61 | dependencies = [ 62 | "unicode-ident", 63 | ] 64 | 65 | [[package]] 66 | name = "quote" 67 | version = "1.0.33" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" 70 | dependencies = [ 71 | "proc-macro2", 72 | ] 73 | 74 | [[package]] 75 | name = "silt-lua" 76 | version = "0.1.1" 77 | dependencies = [ 78 | "hashbrown", 79 | "wasm-bindgen", 80 | ] 81 | 82 | [[package]] 83 | name = "syn" 84 | version = "2.0.29" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" 87 | dependencies = [ 88 | "proc-macro2", 89 | "quote", 90 | "unicode-ident", 91 | ] 92 | 93 | [[package]] 94 | name = "unicode-ident" 95 | version = "1.0.11" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" 98 | 99 | [[package]] 100 | name = "version_check" 101 | version = "0.9.4" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 104 | 105 | [[package]] 106 | name = "wasm-bindgen" 107 | version = "0.2.87" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" 110 | dependencies = [ 111 | "cfg-if", 112 | "wasm-bindgen-macro", 113 | ] 114 | 115 | [[package]] 116 | name = "wasm-bindgen-backend" 117 | version = "0.2.87" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" 120 | dependencies = [ 121 | "bumpalo", 122 | "log", 123 | "once_cell", 124 | "proc-macro2", 125 | "quote", 126 | "syn", 127 | "wasm-bindgen-shared", 128 | ] 129 | 130 | [[package]] 131 | name = "wasm-bindgen-macro" 132 | version = "0.2.87" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" 135 | dependencies = [ 136 | "quote", 137 | "wasm-bindgen-macro-support", 138 | ] 139 | 140 | [[package]] 141 | name = "wasm-bindgen-macro-support" 142 | version = "0.2.87" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" 145 | dependencies = [ 146 | "proc-macro2", 147 | "quote", 148 | "syn", 149 | "wasm-bindgen-backend", 150 | "wasm-bindgen-shared", 151 | ] 152 | 153 | [[package]] 154 | name = "wasm-bindgen-shared" 155 | version = "0.2.87" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" 158 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "silt-lua" 3 | version = "0.1.1" 4 | authors = ["MakeAvoy "] 5 | repository="https://github.com/auxnon/silt-lua" 6 | license ="MIT" 7 | edition = "2021" 8 | keywords=["lua","interpreter","language","vm","script"] 9 | description="A pure rust Lua interpreter and virtual machine" 10 | categories=["compilers","config"] 11 | exclude=["./build-wasm.sh","./.vscode/launch.json","./wasm-tester/index.html"] 12 | 13 | [lib] 14 | name = "silt_lua" 15 | path = "src/lib.rs" 16 | crate-type = ["cdylib","lib"] 17 | 18 | [[bin]] 19 | name = "silt_lua_cli" 20 | path = "src/main.rs" 21 | 22 | [profile.release] 23 | strip = true # Automatically strip symbols from the binary. 24 | opt-level = "z" # Optimize for size. 25 | lto = true 26 | panic ="abort" 27 | 28 | 29 | [profile.wasm] 30 | inherits="release" 31 | 32 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 33 | [features] 34 | default=[] 35 | silt=["bang","under-number","global","implicit-return","short-declare"] 36 | under-number=[] 37 | bang=[] 38 | global=[] 39 | short-declare=[] 40 | implicit-return=[] 41 | dev-out=[] 42 | 43 | [dependencies] 44 | hashbrown = "0.14" 45 | wasm-bindgen="0.2.87" 46 | # rustc-hash="1.1.0" 47 | # ahash="0.8.3" 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Silt-Lua, a Lua superset interpreter in 100% rust 2 | 3 | This project was originally created to answer a problem with the current rust landscape in lacking a complete lua interpreter solution written in 100% rust. That may not necessarily be true anymore. In fact, you probably want Kyren's [piccolo](https://github.com/kyren/piccolo), which is a much more robust Lua interpreter with less overhead. This project also uses Kyren's [gc-arena](https://github.com/kyren/gc-arena) for garbage collection. Even still, the existing implementations at the time the project started were missing crucial features and optimizations to satisfy the requirements of my closed source project [Petrichor64](https://makeavoy.itch.io/petrichor64). I considered shelving the project but I have too much I'd like to extend the language with, plus writng an interpreter is just plain fun. 4 | 5 | Core focus of the library is a basic interpreter with minor overhead and relative speed comparison to standard lua. The goal is for a perfect wasm32-unknown-unknown target build. No emscripten necessary! UserData will mirror traits used by the mlua and rlua crates to allow easy drop in. 6 | 7 | Secondary goals are CLI, LSP, and as much standard library compliance as possible. There's also a concern for safety, as a number of unsafe code is present in the VM. In the future a safe and unsafe version of the VM will be hidden under a feature flag, on the assumption the unsafe version will operate marginally faster. Exact benchmarks will have to be determined. 8 | 9 | There's also desire to add some custom non-lua syntax pulling syntactic sugar from other languages, such as allowing for `!=`, typing, and maybe even arrow functions. This superset of lua will fall under a feature flag and by default be disabled as who really wants my opinionated concept of a programming language? This superset satisfies personal requirements but I'm open to requests if an interest is shown. Even if this superset is enabled it will not conflict with standard lua. 10 | 11 | This library has been written from the ground up with observations of the lua language and documentation. Source code has not been referenced so naturally the VM will always have some noticeable differences that will hopefully be ironed out eventually. This includes the byte code, as lua now operates under wordcode to work with it's register based VM. Feel free to submit an issue for anything particularly glaring. This project is a learning exercise so there is a good deal of naive approaches I'm taking to make this a reality. 12 | 13 | ## Limitations 14 | 15 | - Built as a stack based VM, not register based, this will change eventually. 16 | - Multiple returns is still WIP 17 | - Currently lacks a true garbage collector for the time being, some objects use basic reference counting but nothing sophisticated. This means it's **currently possible to create memory leaks in the VM with self-referencing structures liked linked lists**, but simple scripts and config loading should have no issues. Memory "leaks" will still drop when the VM instance is dropped. 18 | - The stack is not well maintained yet. Aside from lack of a complete safety guarantee at this time, some values MAY remain on the stack after being popped. These values are not forgotten and can be overwrote and dropped. Extensive testing is still needed. Despite this there's a program that peaks in usage will keep that memory it's full run. This is being looked into as the feature flags for safety levels are added. 19 | - Metamethods are still WIP 20 | - Tables use a hashmap and although mixing indexed values with non will generate correctly, using `#` will give the actual hashmap length, not the "consecutive length" that lua does. 21 | - Standard library is only `print` and `clock` function for testing. Feel free to utilize `register_native_function` on the VM instance to fill any gaps for now 22 | 23 | ## WebAssembly 24 | 25 | A simple wasm module example can be compiled via wasm-pack. Print calls a global jprintln function if one exists. A live example can be viewed at [MakeAvoy.com](https://MakeAvoy.com/#code) 26 | 27 | ## Optional Additions (feature flags) 28 | 29 | Keep in mind these may be polarizing and an LSP will flag them as an error 30 | 31 | - `"bang"` Bang usage ! for not or not equal (~=) can be used if you're hard pressed to not use them like I am. They do not replace not or ~=, only act as builtin aliases 32 | - `"under-number"` Numbers can include underscores which are ignored characters used for readability, borrowed right from rust 33 | - `"short-declare"` Stolen right from Go you can now declare a local variable with `:=` such as `a := 2` 34 | - `"implicit-return"` Blocks and statements will implicitly return the last value on the stack unless ending in a `;` 35 | - Top of file flags like --!local force implicit declaration to assign to the current scope instead of at the global level. You can still always declare globals anywhere via the keyword "global", python style 36 | - Anonymous arrow functions of the -> (C# style) are supported `func_name =param -> param+1` in addition to this arrow functions have implicit returns. The last value on the stack is always returned. Regular functions without a `return` keyword will return nil as before. 37 | - Incrementors like `+=` `-=` `*=` `/=` 38 | 39 | ## Examples 40 | 41 | ```rust 42 | 43 | let source_in = r#" 44 | do 45 | local d=5 46 | function sum() 47 | local a=1 48 | local b=2 49 | local c=3 50 | return a+b+c+d+8 51 | end 52 | 53 | return sum() 54 | end 55 | "#; 56 | 57 | let mut vm = Lua::new(); 58 | vm.load_standard_library(); 59 | match vm.run(source_in) { 60 | Ok(value) => { 61 | println!(">> {}", value); 62 | assert_eq!(value, Value:Integer(19)); 63 | } 64 | Err(e) => { 65 | e.iter().for_each(|e| println!("!!Err: {}", e)); 66 | } 67 | } 68 | ``` 69 | -------------------------------------------------------------------------------- /build-wasm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | wasm-pack build --target web -------------------------------------------------------------------------------- /scripts/anon-func.lua: -------------------------------------------------------------------------------- 1 | 2 | function thrice(fn) 3 | for i = 1, 3 do 4 | fn(i) 5 | end 6 | end 7 | 8 | thrice(function(i) print(i) end) -------------------------------------------------------------------------------- /scripts/closure.lua: -------------------------------------------------------------------------------- 1 | function create_counter() 2 | local count = 0 3 | local function f() 4 | count = count + 1 5 | print(count) 6 | return count 7 | end 8 | return f 9 | end 10 | 11 | create_counter() 12 | 13 | local counter = create_counter() 14 | counter() 15 | counter() 16 | -------------------------------------------------------------------------------- /scripts/closure2.lua: -------------------------------------------------------------------------------- 1 | count2 = 0 2 | function create_counter() 3 | local count = 0 4 | local function d() 5 | count = count + 1 6 | count2 = count2 + 1 7 | print(count .. ":" .. count2) 8 | return count 9 | end 10 | return d 11 | end 12 | 13 | function caller() 14 | local counter = create_counter() 15 | local count2 = 10 16 | counter() 17 | counter() 18 | end 19 | 20 | local counter = create_counter() 21 | counter() 22 | counter() 23 | -------------------------------------------------------------------------------- /scripts/closure3.lua: -------------------------------------------------------------------------------- 1 | do 2 | local a = "global" 3 | function f1() 4 | print(a) 5 | end 6 | 7 | f1() 8 | a = "block" 9 | f1() 10 | end 11 | -------------------------------------------------------------------------------- /scripts/closure4.lua: -------------------------------------------------------------------------------- 1 | function outer() 2 | local x = "value" 3 | local function middle() 4 | local function inner() 5 | print(x) 6 | end 7 | print("created inner closure") 8 | return inner 9 | end 10 | 11 | print("created middle closure") 12 | return middle 13 | end 14 | 15 | a = outer() 16 | b = a() 17 | b() 18 | -------------------------------------------------------------------------------- /scripts/concat.lua: -------------------------------------------------------------------------------- 1 | return "h" .. 1 + 3 2 | -------------------------------------------------------------------------------- /scripts/curry.lua: -------------------------------------------------------------------------------- 1 | function a() 2 | return function() 3 | print "a" 4 | end 5 | end 6 | 7 | a()() -------------------------------------------------------------------------------- /scripts/error.lua: -------------------------------------------------------------------------------- 1 | 2 | function test1() 3 | print("test1") 4 | end 5 | 6 | function test2() 7 | print("test21") 8 | "a" 9 | print("test22") 10 | end 11 | "b" 12 | 13 | test1() 14 | test2() -------------------------------------------------------------------------------- /scripts/ex.lua: -------------------------------------------------------------------------------- 1 | do 2 | local a = 1 3 | local b = 2 4 | print("Sum of " .. a .. " + " .. b .. " = " .. a + b) 5 | print("Int or float types " .. 1 + 2.5 / 3) 6 | print("String inference works, '2'+2=" .. "2" + 2) 7 | local function closure() 8 | local c = 0 9 | local function nested() 10 | c = c + a 11 | return "Closures work " .. c 12 | end 13 | 14 | return nested 15 | end 16 | 17 | local iterate = closure() 18 | print(iterate()) 19 | print(iterate()) 20 | print("You can also return values to the console.") 21 | return "Completed!" 22 | end 23 | -------------------------------------------------------------------------------- /scripts/fib-global.lua: -------------------------------------------------------------------------------- 1 | function fib(n) 2 | if n <= 1 then 3 | return n 4 | else 5 | return fib(n - 1) + fib(n - 2) 6 | end 7 | end 8 | 9 | for i = 1, 100 do 10 | print(i .. ":" .. fib(i)) 11 | end 12 | -------------------------------------------------------------------------------- /scripts/fib.lua: -------------------------------------------------------------------------------- 1 | do 2 | function fib(n) 3 | if n <= 1 then 4 | return n 5 | else 6 | return fib(n - 1) + fib(n - 2) 7 | end 8 | end 9 | 10 | local i = 1 11 | while i < 100 do 12 | print(i .. ":" .. fib(i)) 13 | i = i + 1 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /scripts/for-up.lua: -------------------------------------------------------------------------------- 1 | -- for loop closures should capture the loop variable at that point in time and not the final value of the loop variable 2 | a = {} 3 | do 4 | for i = 1, 3 do 5 | local function t() 6 | return i 7 | end 8 | print(i) 9 | a[i] = t 10 | end 11 | 12 | return a[1]() + a[2]() + a[3]() -- 1+2+3 = 6 13 | end 14 | -------------------------------------------------------------------------------- /scripts/for.lua: -------------------------------------------------------------------------------- 1 | do 2 | for i = 1, 3 do 3 | print(i) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /scripts/gc.lua: -------------------------------------------------------------------------------- 1 | do 2 | local a = "1" 3 | a = "2" 4 | b = 0 5 | while b < 10000000 do 6 | b = b + 1 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /scripts/global.lua: -------------------------------------------------------------------------------- 1 | g = 4 2 | return g 3 | -------------------------------------------------------------------------------- /scripts/goto.lua: -------------------------------------------------------------------------------- 1 | b = true 2 | ::a:: 3 | print(a) 4 | do 5 | local a = 5 6 | print(a) 7 | if b then 8 | b = false 9 | goto a 10 | end 11 | print(a) 12 | end 13 | -------------------------------------------------------------------------------- /scripts/grouping.lua: -------------------------------------------------------------------------------- 1 | function a() 2 | return 1,2 3 | end 4 | 5 | b,c=a() 6 | 7 | print(b,c) -------------------------------------------------------------------------------- /scripts/hoist.lua: -------------------------------------------------------------------------------- 1 | a() 2 | 3 | function a() 4 | print "hi" 5 | end 6 | -------------------------------------------------------------------------------- /scripts/if.lua: -------------------------------------------------------------------------------- 1 | do 2 | local a = 1 3 | if true then 4 | local b = 2 5 | print(a) 6 | end 7 | print(b) 8 | end 9 | -------------------------------------------------------------------------------- /scripts/local-curry.lua: -------------------------------------------------------------------------------- 1 | function a() 2 | local b= function() 3 | print "a" 4 | end 5 | return b 6 | end 7 | 8 | a()() 9 | b() -------------------------------------------------------------------------------- /scripts/loop.lua: -------------------------------------------------------------------------------- 1 | a = 1 2 | while a < 10000000 do 3 | a = a + 1 4 | end 5 | print(a) 6 | -------------------------------------------------------------------------------- /scripts/metacall.lua: -------------------------------------------------------------------------------- 1 | mt = debug.getmetatable(0) or {} 2 | mt.__call = function(self, a) 3 | return self + a 4 | end 5 | debug.setmetatable(0, mt) 6 | 7 | b = 0 8 | (3) 9 | 10 | -- print(b) -- 5 11 | -------------------------------------------------------------------------------- /scripts/oop-error.lua: -------------------------------------------------------------------------------- 1 | 2 | print( 4*-6 (3 * 7 + 5) + 2 * 7) -------------------------------------------------------------------------------- /scripts/oop.lua: -------------------------------------------------------------------------------- 1 | print(4 * -6 * (3 * 7 + 5) + 2 * 7) 2 | -------------------------------------------------------------------------------- /scripts/redefine.lua: -------------------------------------------------------------------------------- 1 | do 2 | local a = 1 3 | local a = 2 4 | print(a) 5 | end 6 | -------------------------------------------------------------------------------- /scripts/return.lua: -------------------------------------------------------------------------------- 1 | function a() 2 | print(1) 3 | end 4 | 5 | a() 6 | -------------------------------------------------------------------------------- /scripts/return2.lua: -------------------------------------------------------------------------------- 1 | 2 | function a(n) 3 | print(n) 4 | if n<=1 then 5 | return n 6 | end 7 | return a(n-1) 8 | end 9 | 10 | print("final:"..a(20)) -------------------------------------------------------------------------------- /scripts/return3.lua: -------------------------------------------------------------------------------- 1 | function a(n) 2 | print(n) 3 | do 4 | print("a") 5 | if n <= 1 then 6 | return n 7 | end 8 | print("b") 9 | end 10 | print("c") 11 | end 12 | 13 | 14 | a(2) 15 | a(1) 16 | return "done" -------------------------------------------------------------------------------- /scripts/scope.lua: -------------------------------------------------------------------------------- 1 | --!strict 2 | -- test basic local scope 3 | do 4 | local a = 1 5 | if true then 6 | local b = 2 7 | print(a) 8 | end 9 | print(b) 10 | end 11 | -------------------------------------------------------------------------------- /scripts/scope2.lua: -------------------------------------------------------------------------------- 1 | -- test local scope within for loops 2 | do 3 | for a = 1, 3 do 4 | print(a) 5 | end 6 | print(a) 7 | 8 | local i = 10 9 | print(i) 10 | for i = 1, 3 do 11 | local d = 3 12 | local e = 5 13 | print(i * e) 14 | end 15 | print(i) 16 | end 17 | -------------------------------------------------------------------------------- /scripts/scope3.lua: -------------------------------------------------------------------------------- 1 | -- test upper scope variable usage in function call 2 | do 3 | local a = 4 4 | function f() 5 | print("print" .. a) 6 | end 7 | 8 | f() 9 | end 10 | -------------------------------------------------------------------------------- /scripts/scope4.lua: -------------------------------------------------------------------------------- 1 | -- test local scope index is correctly evaluated in compiler 2 | do 3 | function test(bool) 4 | local x = 1 5 | local b = 4 6 | if bool then 7 | local d = 5 8 | local e = 6 9 | local f = 7 10 | x = x + e 11 | end 12 | x = x + b 13 | return x 14 | end 15 | 16 | return test(true) + test(false) -- 11 + 5 = 16 17 | end 18 | -------------------------------------------------------------------------------- /scripts/silt.lua: -------------------------------------------------------------------------------- 1 | 2 | do 3 | local a = x-> 4 | x+=1\ 5 | return x 6 | 7 | 8 | end -------------------------------------------------------------------------------- /scripts/silt2.lua: -------------------------------------------------------------------------------- 1 | do 2 | a := 1 + 2 3 | -- a += 2 4 | -- b := 2 5 | -- a = a + 4 6 | print(a) 7 | end 8 | -------------------------------------------------------------------------------- /scripts/simple.lua: -------------------------------------------------------------------------------- 1 | return 1 + 1 2 | -------------------------------------------------------------------------------- /scripts/t.lua: -------------------------------------------------------------------------------- 1 | n = 12 2 | i = 2 3 | for i = 2, n, i do 4 | while n % i == 0 do 5 | print(i) 6 | n = n / i 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /scripts/table.lua: -------------------------------------------------------------------------------- 1 | do 2 | -- local t = { 1 } 3 | -- local t2 = { 1 } 4 | -- print(t[0]) 5 | -- print(t2[0]) 6 | -- t[0] = t2 7 | 8 | -- print(t[0][0]) 9 | 10 | -- t2[0] = 2 11 | -- print(t[0][0]) 12 | -- local c = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } } 13 | -- print(c[3][0]) 14 | local t = { a = 5, 1, 2, 3 } 15 | 16 | print(t.a) 17 | print(t[1]) 18 | print(#t) 19 | t.a = 6 20 | print(t.a) 21 | -- local b = 1 22 | -- print(b) 23 | -- b = 5 24 | -- print(b) 25 | end 26 | -------------------------------------------------------------------------------- /scripts/test.lua: -------------------------------------------------------------------------------- 1 | start = clock() 2 | i = 1 3 | a = "a" 4 | while i < 100000 do 5 | i = i + 1 6 | a = a .. "1" 7 | end 8 | elapsed = clock() - start 9 | print "done " 10 | print("elapsed: " .. elapsed) 11 | return { elapsed, i } 12 | -------------------------------------------------------------------------------- /src/archive/compiler_old.rs: -------------------------------------------------------------------------------- 1 | use std::iter::{Enumerate, Peekable}; 2 | 3 | use crate::{ 4 | chunk::Chunk, 5 | code::OpCode, 6 | environment::Environment, 7 | error::{Location, SiltError}, 8 | lexer::{Lexer, TokenResult}, 9 | token::Token, 10 | value::Value, 11 | }; 12 | 13 | struct Compiler { 14 | chunk: Chunk, 15 | iterator: Option>, 16 | // locations: Vec, 17 | previous: Location, 18 | // lexer: Lexer, 19 | // parser: Parser, 20 | // resolver: Resolver, 21 | // interpreter: Interpreter, 22 | } 23 | impl Compiler { 24 | pub fn new() -> Compiler { 25 | Compiler { 26 | chunk: Chunk::new(), 27 | iterator: None, 28 | // locations: vec![], 29 | previous: (0, 0), 30 | // lexer: Lexer::new(), 31 | // parser: Parser::new(), 32 | // resolver: Resolver::new(), 33 | // interpreter: Interpreter::new(), 34 | } 35 | } 36 | pub fn compile(&mut self, source: String, global: &mut Environment) -> Value { 37 | let mut lexer = Lexer::new(source.to_owned()); 38 | 39 | self.iterator = Some(lexer.peekable()); 40 | // lexer.enumerate().for_each(|(i, res)| match res { 41 | // Ok((token, location)) => { 42 | // self.iterator.push(t); 43 | // } 44 | // Err(e) => { 45 | // println!("| {}", e); 46 | // } 47 | // }); 48 | 49 | // let mut parser = crate::parser::parser::Parser::new(t, p, global); 50 | // println!("|----------------"); 51 | // let mut statements = parser.parse(); 52 | // statements 53 | // .iter() 54 | // .enumerate() 55 | // .for_each(|(i, e)| println!("| {} {}", i, e)); 56 | // // println!("{}", exp); 57 | // // let val = parser.evaluate(exp); 58 | // let err = parser.get_errors(); 59 | // if err.len() > 0 { 60 | // println!("|----------------"); 61 | // println!("Parse Errors:"); 62 | // err.iter().for_each(|e| println!("{}", e)); 63 | // println!("-----------------"); 64 | // } else { 65 | // println!("-----------------"); 66 | // let mut resolver = crate::resolver::Resolver::new(); 67 | // resolver.process(&mut statements); 68 | // let res = crate::interpreter::execute(global, &statements); 69 | // match res { 70 | // Ok(v) => { 71 | // println!(""); 72 | // return v; 73 | // } 74 | // Err(e) => { 75 | // println!("| Runtime Errors:"); 76 | // println!("| {}", e); 77 | // println!("-----------------"); 78 | // } 79 | // } 80 | // } 81 | 82 | self.expression(); 83 | // t.iter().enumerate().for_each(|(i, t)| { 84 | // let p = p.get(i).unwrap_or(&(0, 0)); 85 | // self.expression(t, p); 86 | // // println!("|{}:{}| {}", p.0, p.1, t) 87 | // }); 88 | 89 | self.emit(OpCode::RETURN, (0, 0)); 90 | Value::Nil 91 | } 92 | 93 | fn expression(&mut self) {} 94 | 95 | fn emit(&mut self, op: OpCode, location: Location) { 96 | self.chunk.write_code(op, location); 97 | } 98 | fn constant(&mut self, value: Value, location: Location) { 99 | let constant = self.chunk.write_constant(value) as u8; 100 | self.emit(OpCode::CONSTANT { constant }, location); 101 | } 102 | 103 | fn current_chunk(&self) -> &Chunk { 104 | &self.chunk 105 | } 106 | 107 | fn eat(&mut self) { 108 | let p = self.iterator.unwrap().next(); 109 | if let Some(Ok(t)) = p { 110 | self.previous = t.1; 111 | } 112 | } 113 | 114 | fn grouping(&mut self) { 115 | self.expression(); 116 | expect_token!(self, CloseParen, SiltError::UnterminatedParenthesis(0, 0)); 117 | // self.consume(TokenType::RightParen, "Expect ')' after expression."); 118 | } 119 | // fn expression(&mut self) {} 120 | } 121 | -------------------------------------------------------------------------------- /src/archive/environment.rs: -------------------------------------------------------------------------------- 1 | // use rustc_hash::FxHashMap as HashMap; 2 | // use hashbrown::HashMap; 3 | use std::{cell::RefCell, collections::HashMap, rc::Rc, vec}; 4 | // use std::println; 5 | 6 | use crate::{expression::Ident, value::Value}; 7 | 8 | pub type Scope = Rc>>; 9 | 10 | pub struct Environment { 11 | pub variables: Vec, 12 | depth: usize, // pub enclosing: Option<&'b mut Environment<'a, 'b>>, 13 | /** Whether undeclared variables should implicitly define up to the top level like normal lua, or start in immediate scope */ 14 | implicit_global: bool, 15 | strict: bool, 16 | free_registers: Vec, 17 | next_register: usize, 18 | map: HashMap, 19 | // local_table: HashMap, 20 | // meta_table: HashMap, // key 21 | } 22 | 23 | impl Environment { 24 | pub fn new() -> Self { 25 | Environment { 26 | variables: vec![Rc::new(RefCell::new(HashMap::default()))], 27 | depth: 0, // enclosing: None, 28 | implicit_global: true, 29 | strict: false, 30 | free_registers: vec![], 31 | next_register: 0, 32 | map: HashMap::default(), 33 | } 34 | } 35 | 36 | pub fn new_with_std() -> Self { 37 | let mut env = Environment::new(); 38 | env.load_standard_library(); 39 | env 40 | } 41 | 42 | pub fn load_standard_library(&mut self) { 43 | // self.register_std("clock", crate::standard::clock); 44 | // self.register_std("print", crate::standard::print); 45 | } 46 | 47 | fn register_std(&mut self, name: &str, func: fn(Vec) -> Value) -> usize { 48 | // let u = self.to_register(name); 49 | // self.declare_global((u, 0), Value::NativeFunction(func)); 50 | // u 51 | todo!() 52 | } 53 | 54 | pub fn declare_global(&mut self, ident: Ident, value: Value) { 55 | self.set(ident, value, true, false); 56 | } 57 | 58 | pub fn declare_local(&mut self, ident: Ident, value: Value) { 59 | self.set(ident, value, true, true); 60 | } 61 | pub fn set_in_scope(&mut self, ident: usize, value: Value) { 62 | self.variables[self.depth].borrow_mut().insert(ident, value); 63 | } 64 | 65 | pub fn assign_local(&mut self, ident: Ident, value: Value) { 66 | self.set(ident, value, false, true); 67 | } 68 | 69 | /** If we have implicit global then we default implicit declaration to the highest level as lua 70 | * does, otherwise we do something nicer and create in local scope if not shadowing anything*/ 71 | pub fn set(&mut self, ident: Ident, value: Value, declare: bool, local: bool) { 72 | // if declare { 73 | // self.variables[if local { self.depth } else { 0 }].insert(ident, value); 74 | // } else { 75 | // // println!( 76 | // // "assign {} to {} (current depth {})", 77 | // // value, ident, self.depth 78 | // // ); 79 | // // check if exists 80 | // if self.depth > 0 { 81 | // // for vars in self.variables.iter_mut().rev() { 82 | // // if let Some(val) = vars.get_mut(&ident) { 83 | // // *val = value; 84 | // // return; 85 | // // } 86 | // // } 87 | // for i in (0..=self.depth).rev() { 88 | // if let Some(val) = self.variables[i].get_mut(&ident) { 89 | // *val = value; 90 | // return; 91 | // } 92 | // } 93 | // } 94 | // // implicit declaration as we were unable to find an existing variable 95 | // if self.implicit_global { 96 | // self.variables[0].insert(ident, value); 97 | // } else { 98 | // self.variables[self.depth].insert(ident, value); 99 | // } 100 | // } 101 | 102 | self.variables[ident.1].borrow_mut().insert(ident.0, value); 103 | } 104 | 105 | pub fn get(&self, ident: &Ident) -> Value { 106 | // match self.variables.get(ident) { 107 | // Some(v) => v.clone(), 108 | // None => match &self.enclosing { 109 | // Some(e) => e.get(ident), 110 | // None => &Value::Nil, 111 | // }, 112 | // } 113 | if let Some(val) = self.variables[ident.1].borrow().get(&ident.0) { 114 | val.clone() 115 | } else { 116 | Value::Nil 117 | } 118 | 119 | // if self.depth > 0 { 120 | // for vars in self.variables.iter().rev() { 121 | // if let Some(val) = vars.get(ident) { 122 | // return val.clone(); 123 | // } 124 | // } 125 | // } else { 126 | // if let Some(val) = self.variables[0].get(ident) { 127 | // // println!("got"); 128 | // return val.clone(); 129 | // } 130 | // } 131 | // vars.get(ident) 132 | } 133 | 134 | // pub fn get_at(&self, ident: &usize, depth: usize) -> Value { 135 | // if let Some(val) = self.variables[depth].borrow().get(ident) { 136 | // return val.clone(); 137 | // } 138 | // Value::Nil 139 | // } 140 | 141 | pub fn new_scope(&mut self) { 142 | self.variables 143 | .push(Rc::new(RefCell::new(HashMap::default()))); 144 | self.depth += 1; 145 | } 146 | 147 | pub fn pop_scope(&mut self) { 148 | // self.variables.get_mut(self.depth).unwrap().clear(); // not faster, +2-3% time, despite keeping memory 149 | // self.variables[self.depth].clear(); // about the same as popping +1% time, but keeps memory 150 | self.variables.pop(); 151 | self.depth -= 1; 152 | } 153 | 154 | fn get_register(&mut self) -> usize { 155 | if let Some(reg) = self.free_registers.pop() { 156 | reg 157 | } else { 158 | let reg = self.next_register; 159 | self.next_register += 1; 160 | reg 161 | } 162 | } 163 | 164 | pub fn get_current_scope(&self) -> Vec { 165 | self.variables 166 | .iter() 167 | .rev() 168 | .take(self.depth + 1) 169 | .map(|s| s.clone()) 170 | .collect() 171 | } 172 | 173 | /////////////////////////// 174 | pub fn swap_scope(&mut self, scope: &Vec) -> Vec { 175 | let temp_scope = self.variables.drain(0..=self.depth).collect::>(); 176 | self.variables.extend(scope.iter().rev().cloned()); 177 | self.depth = self.variables.len() - 1; 178 | temp_scope 179 | } 180 | pub fn replace_scope(&mut self, scope: Vec) { 181 | self.variables = scope; 182 | self.depth = self.variables.len() - 1; 183 | } 184 | /////////////////////////// 185 | 186 | pub fn resolve_local(&mut self, ident: usize, depth: usize) { 187 | // TODO use the expression as a key? the expr as key should point to a the depth 188 | // self.local_table.insert(ident, depth); 189 | } 190 | fn free_register(&mut self, reg: usize) { 191 | self.free_registers.push(reg); 192 | } 193 | 194 | pub fn to_register(&mut self, name: &str) -> usize { 195 | if let Some(reg) = self.map.get(name) { 196 | *reg 197 | } else { 198 | let reg = self.get_register(); 199 | // println!("to_register {} @ {}", name, reg); 200 | self.map.insert(name.to_string(), reg); 201 | reg 202 | } 203 | } 204 | pub fn from_register(&self, reg: usize) -> &str { 205 | for (k, v) in self.map.iter() { 206 | if *v == reg { 207 | return k; 208 | } 209 | } 210 | "unknown" 211 | } 212 | 213 | pub fn is_strict(&self) -> bool { 214 | self.strict 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /src/archive/expression.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use crate::{error::Location, function::Function, token::Operator, value::Value}; 4 | 5 | /** first usize is mapped to environment hash for name, can be reversed for debug. Second usize is scope depth */ 6 | pub type Ident = (usize, usize); 7 | 8 | #[derive(Clone)] 9 | pub enum Expression { 10 | /** expression + expression */ 11 | Binary { 12 | left: Box, 13 | operator: Operator, 14 | right: Box, 15 | location: Location, 16 | }, 17 | /** +expression | !expression */ 18 | Unary { 19 | operator: Operator, 20 | right: Box, 21 | location: Location, 22 | }, 23 | /** true | 1 | nil | etc*/ 24 | Literal { value: Value, location: Location }, 25 | /** ( expression ) */ 26 | GroupingExpression { 27 | expression: Box, 28 | location: Location, 29 | }, 30 | /** get <- ident */ 31 | Variable { ident: Ident, location: Location }, 32 | /** put expression -> ident */ 33 | Assign { 34 | ident: Ident, 35 | value: Box, 36 | location: Location, 37 | }, 38 | /** expression OR expression AND expression */ 39 | Logical { 40 | left: Box, 41 | operator: Operator, 42 | right: Box, 43 | location: Location, 44 | }, 45 | /** ident() */ 46 | Call { 47 | callee: Box, 48 | args: Vec, 49 | location: Location, 50 | }, 51 | /** <- fn(){} */ 52 | Function { 53 | value: Rc, 54 | location: Location, 55 | }, 56 | /** shrug */ 57 | InvalidExpression, 58 | } 59 | impl std::fmt::Display for Expression { 60 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 61 | match self { 62 | Expression::Binary { 63 | left, 64 | operator, 65 | right, 66 | location, 67 | } => write!(f, "({} {} {})", operator, left, right), 68 | Expression::Logical { 69 | left, 70 | operator, 71 | right, 72 | location, 73 | } => write!(f, "({} {} {})", operator, left, right), 74 | Expression::Unary { 75 | operator, 76 | right, 77 | location, 78 | } => write!(f, "({} {})", operator, right), 79 | Expression::Literal { value, location } => write!(f, " {} ", value), 80 | Expression::GroupingExpression { 81 | expression, 82 | location, 83 | } => write!(f, "G({})", expression), 84 | Expression::Variable { ident, location } => write!(f, "{}:{}", ident.0, ident.1), 85 | Expression::Assign { 86 | ident, 87 | value, 88 | location, 89 | } => write!(f, "({}:{} := {})", ident.0, ident.1, value), 90 | Expression::Call { 91 | callee, 92 | args: arguments, 93 | .. 94 | } => { 95 | let mut s = format!("({}(", callee); 96 | for arg in arguments { 97 | s.push_str(&format!("{},", arg)); 98 | } 99 | s.push_str("))"); 100 | write!(f, "{}", s) 101 | } 102 | Expression::Function { value, .. } => write!(f, "function"), 103 | 104 | Expression::InvalidExpression => write!(f, "!Invalid_Expression!"), 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/archive/parser.rs: -------------------------------------------------------------------------------- 1 | pub mod parser { 2 | use std::{println, rc::Rc, vec}; 3 | 4 | use crate::{ 5 | environment::Environment, 6 | error::{ErrorTuple, Location, SiltError}, 7 | expression::{Expression, Ident}, 8 | function::Function, 9 | statement::Statement, 10 | token::{Operator, Token}, 11 | value::Value, 12 | }; 13 | 14 | macro_rules! build_block_until { 15 | ($self:ident $($rule:ident)|*) => {{ 16 | let mut statements = vec![]; 17 | while !matches!($self.peek(), Some( 18 | $( &Token::$rule)|*)) { 19 | statements.push($self.declaration()); 20 | } 21 | statements 22 | }}; 23 | } 24 | 25 | macro_rules! devout { 26 | ($self:ident $message:literal) => { 27 | #[cfg(feature = "dev-out")] 28 | println!("-> {}: {:?}", $message, $self.peek().unwrap_or(&Token::Nil)); 29 | }; 30 | } 31 | 32 | macro_rules! op_assign { 33 | ($self:ident, $ident:ident,$op:ident) => {{ 34 | let value = $self.expression(); 35 | let bin = Expression::Binary { 36 | left: Box::new(Expression::Variable { 37 | $ident, 38 | location: $self.get_last_loc(), 39 | }), 40 | operator: Operator::$op, 41 | right: Box::new(value), 42 | location: $self.get_last_loc(), 43 | }; 44 | Expression::Assign { 45 | ident: $ident, 46 | value: Box::new(bin), 47 | location: $self.get_last_loc(), 48 | } 49 | }}; 50 | } 51 | 52 | /** error if missing, eat if present */ 53 | macro_rules! expect_token { 54 | ($self:ident $token:ident) => {{ 55 | if let Some(&Token::$token) = $self.peek() { 56 | $self.eat(); 57 | } else { 58 | $self.error(SiltError::ExpectedToken(Token::$token)); 59 | return Statement::InvalidStatement; 60 | } 61 | };}; 62 | ($self:ident, $token:ident, $custom_error:expr) => {{ 63 | if let Some(&Token::$token) = $self.peek() { 64 | $self.eat(); 65 | } else { 66 | return Err($custom_error); 67 | } 68 | };}; 69 | } 70 | macro_rules! expect_token_exp { 71 | ($self:ident $token:ident) => {{ 72 | if let Some(&Token::$token) = $self.peek() { 73 | $self.eat(); 74 | } else { 75 | $self.error(SiltError::ExpectedToken(Token::$token)); 76 | return Expression::InvalidExpression; 77 | } 78 | };}; 79 | } 80 | 81 | pub struct Parser<'a> { 82 | pub iterator: std::iter::Peekable>, 83 | pub locations: Vec, 84 | pub current: usize, 85 | pub errors: Vec, 86 | pub valid: bool, 87 | pub global: &'a mut Environment, 88 | } 89 | impl<'a> Parser<'a> { 90 | pub fn new(t: Vec, p: Vec, global: &'a mut Environment) -> Parser<'a> { 91 | assert!(p.len() == p.len()); 92 | let ee = t.into_iter().peekable(); 93 | // let tt = t.iter().peekable(); 94 | Parser { 95 | iterator: ee, 96 | locations: p, 97 | current: 0, 98 | errors: vec![], 99 | valid: true, 100 | global, 101 | } 102 | } 103 | 104 | // pub fn advance(&mut self) -> Option { 105 | // self.current += 1; 106 | // self.iterator.next().cloned() 107 | // } 108 | 109 | fn error_last(&mut self, code: SiltError) { 110 | self.valid = false; 111 | self.errors.push(ErrorTuple { 112 | code, 113 | location: self.locations[self.current - 2], 114 | }); 115 | } 116 | 117 | fn error(&mut self, code: SiltError) { 118 | self.valid = false; 119 | self.errors.push(ErrorTuple { 120 | code, 121 | location: self.locations[self.current - 1], 122 | }); 123 | } 124 | 125 | pub fn get_errors(&self) -> &Vec { 126 | &self.errors 127 | } 128 | 129 | fn synchronize(&mut self) { 130 | while match self.peek() { 131 | Some(&Token::Class) 132 | | Some(&Token::Function) 133 | | Some(&Token::Do) 134 | | Some(&Token::For) 135 | | Some(&Token::If) 136 | | Some(&Token::While) 137 | | Some(&Token::Print) 138 | | Some(&Token::Return) => false, 139 | _ => true, 140 | } { 141 | self.eat(); 142 | } 143 | } 144 | 145 | // _ => Value::Nil, 146 | 147 | // fn next_statement(&self) -> bool {} 148 | 149 | pub fn eat(&mut self) -> Option { 150 | self.current += 1; 151 | self.iterator.next() 152 | // println!( 153 | // "{}", 154 | // match self.iterator.next() { 155 | // Some(t) => format!("{}", t), 156 | // None => format!("None"), 157 | // } 158 | // ); 159 | } 160 | 161 | pub fn next(&mut self) -> Option { 162 | self.current += 1; 163 | self.iterator.next() 164 | } 165 | 166 | /** only use after peek */ 167 | pub fn eat_out(&mut self) -> Token { 168 | self.current += 1; 169 | self.iterator.next().unwrap() 170 | } 171 | 172 | fn peek(&mut self) -> Option<&Token> { 173 | self.iterator.peek() 174 | } 175 | 176 | fn is_end(&mut self) -> bool { 177 | match self.peek() { 178 | None => true, 179 | _ => false, 180 | } 181 | } 182 | 183 | // fn get_last_loc(&self) -> Location { 184 | // self.locations[self.current - 1] 185 | // } 186 | fn get_loc(&self) -> Location { 187 | #[cfg(feature = "dev-out")] 188 | println!( 189 | "get index {} is loc {}:{}", 190 | self.current, self.locations[self.current].0, self.locations[self.current].1 191 | ); 192 | self.locations[self.current] 193 | } 194 | 195 | fn get_last_loc(&self) -> Location { 196 | #[cfg(feature = "dev-out")] 197 | println!( 198 | "get index {} is loc {}:{}", 199 | self.current - 1, 200 | self.locations[self.current - 1].0, 201 | self.locations[self.current - 1].1 202 | ); 203 | self.locations[self.current - 1] 204 | } 205 | 206 | pub fn parse(&mut self) -> Vec { 207 | let mut statements = vec![]; 208 | while !self.is_end() { 209 | // if let Ok(s) = self.statement() { 210 | statements.push(self.declaration()); 211 | // } 212 | // else synchronize 213 | } 214 | statements 215 | } 216 | // declare 217 | // if var return declare_staement 218 | // return statement 219 | // declare_statement 220 | // eat identifier 221 | // if equal then expresion 222 | // otherwise return as nil binary assign 223 | 224 | fn declaration(&mut self) -> Statement { 225 | devout!(self "declaration"); 226 | // if let Some(&Token::Local) = self.peek() { 227 | // self.eat(); 228 | match self.peek() { 229 | Some(&Token::Local) => self.declaration_scope(true, false), 230 | Some(&Token::Global) => self.declaration_scope(false, false), 231 | Some(&Token::Function) => { 232 | self.eat(); 233 | self.define_function(true, None) 234 | } 235 | _ => self.statement(), 236 | } 237 | 238 | // if let Some(&Token::Local | &Token::Global) = self.peek() { 239 | // let scope = self.eat_out(); 240 | // if let Some(Token::Identifier(ident)) = self.eat() { 241 | // let ident = self.global.to_register(&ident); 242 | 243 | // } else { 244 | // self.error(SiltError::ExpectedLocalIdentifier); 245 | // Statement::InvalidStatement 246 | // } 247 | // } else { 248 | // self.statement() 249 | // } 250 | } 251 | 252 | fn declaration_scope(&mut self, local: bool, already_function: bool) -> Statement { 253 | self.eat(); 254 | match self.eat_out() { 255 | Token::Identifier(ident) => { 256 | let ident = (self.global.to_register(&ident), 0); 257 | self.typing(ident, local) 258 | } 259 | Token::Function => { 260 | if !already_function { 261 | self.define_function(local, None) 262 | } else { 263 | self.error(SiltError::ExpectedLocalIdentifier); 264 | Statement::InvalidStatement 265 | } 266 | } 267 | _ => { 268 | self.error(SiltError::ExpectedLocalIdentifier); 269 | Statement::InvalidStatement 270 | } 271 | } 272 | } 273 | 274 | fn typing(&mut self, ident: Ident, local: bool) -> Statement { 275 | if let Some(&Token::Colon) = self.peek() { 276 | // typing or self calling 277 | self.eat(); 278 | let token = self.eat_out(); 279 | if let Token::ColonIdentifier(target) = token { 280 | // method or type name 281 | // if let Some(&Token::OpenParen) = self.peek() { 282 | // // self call 283 | 284 | // Statement::InvalidStatement 285 | // } else { 286 | // // typing 287 | // // return self.assign(self.peek(), ident); 288 | // Statement::InvalidStatement 289 | // } 290 | self.define_declaration(ident, local) 291 | } else { 292 | self.error(SiltError::InvalidColonPlacement); 293 | Statement::InvalidStatement 294 | } 295 | } else { 296 | self.define_declaration(ident, local) 297 | } 298 | } 299 | 300 | fn assignment(&mut self) -> Expression { 301 | devout!(self "assignment"); 302 | let exp = self.logical_or(); 303 | if let Some( 304 | &Token::Assign 305 | | &Token::AddAssign 306 | | &Token::SubAssign 307 | | &Token::MultiplyAssign 308 | | &Token::DivideAssign 309 | | &Token::ModulusAssign, 310 | ) = self.peek() 311 | { 312 | //let ident= current somehow?? use the exp as ident? 313 | return if let Expression::Variable { ident, location } = exp { 314 | self.assigner(ident) 315 | } else { 316 | let t = self.peek().unwrap().clone(); 317 | self.error(SiltError::InvalidAssignment(t)); 318 | Expression::Literal { 319 | value: Value::Nil, 320 | location: self.get_last_loc(), 321 | } 322 | }; 323 | } 324 | exp 325 | } 326 | 327 | fn define_declaration(&mut self, ident: Ident, local: bool) -> Statement { 328 | let t = self.peek(); 329 | match t { 330 | Some(&Token::Assign) => Statement::Declare { 331 | ident, 332 | local, 333 | expr: Box::new(self.next_expression()), 334 | }, 335 | // we can't increment what doesn't exist yet, like what are you even doing? 336 | Some( 337 | &Token::AddAssign 338 | | &Token::SubAssign 339 | | &Token::MultiplyAssign 340 | | &Token::DivideAssign 341 | | &Token::ModulusAssign, 342 | ) => { 343 | let tt = t.unwrap().clone(); 344 | self.error(SiltError::InvalidAssignment(tt)); 345 | Statement::InvalidStatement 346 | } 347 | _ => Statement::Declare { 348 | ident, 349 | local, 350 | expr: Box::new(Expression::Literal { 351 | value: Value::Nil, 352 | location: self.get_last_loc(), 353 | }), 354 | }, 355 | } 356 | } 357 | 358 | fn define_function(&mut self, local: bool, pre_ident: Option) -> Statement { 359 | // self.eat(); // parser callers have already eaten token, they're full! lol 360 | let location = self.get_last_loc(); 361 | if let Token::Identifier(ident) = self.eat_out() { 362 | let ident = (self.global.to_register(&ident), 0); 363 | let func = self.function_expression(location); 364 | return Statement::Declare { 365 | ident, 366 | local, 367 | expr: Box::new(func), 368 | }; 369 | } 370 | self.error(SiltError::ExpectedLocalIdentifier); 371 | Statement::InvalidStatement 372 | } 373 | 374 | fn function_expression(&mut self, location: Location) -> Expression { 375 | let mut params = vec![]; 376 | 377 | expect_token_exp!(self OpenParen); 378 | 379 | if let Some(&Token::CloseParen) = self.peek() { 380 | self.eat(); 381 | } else { 382 | while let Token::Identifier(ident) = self.eat_out() { 383 | let ident = self.global.to_register(&ident); 384 | params.push(ident); 385 | if let Some(&Token::Comma) = self.peek() { 386 | self.eat(); 387 | } else { 388 | break; 389 | } 390 | } 391 | // TODO specific terminating paren error 392 | expect_token_exp!(self CloseParen); 393 | } 394 | let block = self.block(); 395 | let func = Rc::new(Function::new(params, block)); 396 | Expression::Function { 397 | value: func, 398 | location, 399 | } 400 | } 401 | 402 | fn assigner(&mut self, ident: Ident) -> Expression { 403 | let tok = self.eat_out(); 404 | 405 | let location = self.get_last_loc(); 406 | match tok { 407 | Token::Assign => Expression::Assign { 408 | ident, 409 | value: Box::new(self.expression()), 410 | location, 411 | }, 412 | Token::AddAssign => { 413 | op_assign!(self, ident, Add) 414 | } 415 | Token::SubAssign => { 416 | op_assign!(self, ident, Sub) 417 | } 418 | Token::MultiplyAssign => { 419 | op_assign!(self, ident, Multiply) 420 | } 421 | Token::DivideAssign => { 422 | op_assign!(self, ident, Divide) 423 | } 424 | Token::ModulusAssign => { 425 | op_assign!(self, ident, Modulus) 426 | } 427 | _ => panic!("impossible"), //Statement::Expression(Expression::Variable {ident}) 428 | } 429 | } 430 | 431 | // fn assign(&mut self, ident: usize) -> Statement { 432 | // println!("-> assign {}", ident); 433 | // match self.peek() { 434 | // Some(&Token::Assign) => Statement::Declare { 435 | // ident, 436 | // value: Box::new(self.next_expression()), 437 | // }, 438 | // Some(&Token::AddAssign) => { 439 | // op_assign!(self, ident, Add) 440 | // } 441 | // Some(&Token::SubAssign) => { 442 | // op_assign!(self, ident, Sub) 443 | // } 444 | // Some(&Token::MultiplyAssign) => { 445 | // op_assign!(self, ident, Multiply) 446 | // } 447 | // Some(&Token::DivideAssign) => { 448 | // op_assign!(self, ident, Divide) 449 | // } 450 | // Some(&Token::ModulusAssign) => { 451 | // op_assign!(self, ident, Modulus) 452 | // } 453 | // _ => self.statement(), //Statement::Expression(Expression::Variable {ident}) 454 | // } 455 | // } 456 | 457 | ////////////////////////////// 458 | /// Statements 459 | ////////////////////////////// 460 | 461 | fn statement(&mut self) -> Statement { 462 | devout!(self "statement"); 463 | match self.peek() { 464 | Some(&Token::If) => self.if_statement(), 465 | Some(&Token::While) => self.while_statement(), 466 | Some(&Token::For) => self.for_statement(), 467 | Some(&Token::Print) => Statement::Print(Box::new(self.next_expression())), 468 | Some(&Token::Do) => Statement::Block(self.eat_block()), 469 | Some(&Token::Return) => self.return_statement(), 470 | // Some(&Token::Function) => self.define_function(false, None), 471 | Some(&Token::SemiColon) => { 472 | self.eat(); 473 | // worked our way into a corner with this one huh? 474 | Statement::Skip 475 | } 476 | 477 | _ => Statement::Expression(Box::new(self.expression())), // don't eat 478 | } 479 | } 480 | 481 | // fn next_block(&mut self, end:W ) -> Vec 482 | // where W : Option<<&Token as BitOr<&Token>>::Output> { 483 | // let d = Some(&Token::End | &Token::Else); 484 | // let mut statements = vec![]; 485 | // while !matches!(self.peek(), Some(end)) { 486 | // statements.push(self.local_declaration()); 487 | // } 488 | // 489 | // if !matches!(self.eat_out(), Token::End) { 490 | // self.error(SiltError::UnterminatedBlock); 491 | // } 492 | // statements 493 | // } 494 | 495 | /** eat token, collect statements until hitting end, error if no end hit */ 496 | fn eat_block(&mut self) -> Vec { 497 | self.eat(); 498 | self.block() 499 | } 500 | 501 | /** collect statements until hitting end, error if no end hit */ 502 | fn block(&mut self) -> Vec { 503 | let statements = build_block_until!(self End); 504 | 505 | if !matches!(self.eat_out(), Token::End) { 506 | self.error(SiltError::UnterminatedBlock); 507 | } 508 | statements 509 | } 510 | 511 | fn if_statement(&mut self) -> Statement { 512 | self.eat(); 513 | let condition = self.expression(); 514 | if let Some(&Token::Then) = self.peek() { 515 | self.eat(); 516 | 517 | let then_branch = build_block_until!(self End | Else | ElseIf); 518 | match self.peek() { 519 | Some(&Token::Else) => { 520 | self.eat(); 521 | let else_branch = build_block_until!(self End); 522 | 523 | self.eat(); 524 | Statement::If { 525 | cond: Box::new(condition), 526 | then: then_branch, 527 | else_cond: Some(else_branch), 528 | } 529 | } 530 | Some(&Token::ElseIf) => { 531 | // self.eat(); 532 | let nested = vec![self.if_statement()]; 533 | Statement::If { 534 | cond: Box::new(condition), 535 | then: then_branch, 536 | else_cond: Some(nested), 537 | } 538 | } 539 | Some(&Token::End) => { 540 | self.eat(); 541 | Statement::If { 542 | cond: Box::new(condition), 543 | then: then_branch, 544 | else_cond: None, 545 | } 546 | } 547 | _ => { 548 | self.error(SiltError::UnterminatedBlock); 549 | Statement::InvalidStatement 550 | } 551 | } 552 | } else { 553 | self.error(SiltError::ExpectedThen); 554 | Statement::InvalidStatement 555 | } 556 | } 557 | 558 | fn while_statement(&mut self) -> Statement { 559 | self.eat(); 560 | let cond = self.expression(); 561 | if let Some(&Token::Do) = self.peek() { 562 | let block = self.eat_block(); 563 | Statement::While { 564 | cond: Box::new(cond), 565 | block, 566 | } 567 | } else { 568 | self.error(SiltError::ExpectedDo); 569 | Statement::InvalidStatement 570 | } 571 | } 572 | 573 | fn for_statement(&mut self) -> Statement { 574 | // Statement::InvalidStatement 575 | self.eat(); 576 | let ident = self.eat_out(); 577 | if let Token::Identifier(ident_str) = ident { 578 | let ident = self.global.to_register(&ident_str); 579 | expect_token!(self Assign); 580 | let start = Box::new(self.expression()); 581 | expect_token!(self Comma); 582 | let end = Box::new(self.expression()); 583 | let step = if let Some(&Token::Comma) = self.peek() { 584 | self.eat(); 585 | Some(Box::new(self.expression())) 586 | } else { 587 | None 588 | }; 589 | return if let Some(&Token::Do) = self.peek() { 590 | let block = self.eat_block(); 591 | Statement::NumericFor { 592 | ident, 593 | start, 594 | end, 595 | step, 596 | block, 597 | } 598 | } else { 599 | self.error(SiltError::ExpectedDo); 600 | Statement::InvalidStatement 601 | }; 602 | } else { 603 | self.error(SiltError::ExpectedLocalIdentifier); 604 | } 605 | Statement::InvalidStatement 606 | } 607 | 608 | fn return_statement(&mut self) -> Statement { 609 | self.eat(); 610 | let value = if let Some(&Token::SemiColon | &Token::End) = self.peek() { 611 | Expression::Literal { 612 | value: Value::Nil, 613 | location: self.get_last_loc(), 614 | } 615 | } else { 616 | self.expression() 617 | }; 618 | Statement::Return(Box::new(value)) 619 | 620 | // Statement::Return(Box::new(self.next_expression())) 621 | } 622 | 623 | fn next_expression(&mut self) -> Expression { 624 | self.eat(); 625 | self.expression() 626 | } 627 | 628 | fn expression(&mut self) -> Expression { 629 | devout!(self "expression"); 630 | self.assignment() 631 | } 632 | 633 | fn logical_or(&mut self) -> Expression { 634 | let mut exp = self.logical_and(); 635 | while let Some(&Token::Op(Operator::Or)) = self.peek() { 636 | self.eat(); 637 | let right = self.logical_and(); 638 | exp = Expression::Logical { 639 | left: Box::new(exp), 640 | operator: Operator::Or, 641 | right: Box::new(right), 642 | location: self.get_last_loc(), 643 | }; 644 | } 645 | exp 646 | } 647 | 648 | fn logical_and(&mut self) -> Expression { 649 | let mut exp = self.equality(); 650 | while let Some(&Token::Op(Operator::And)) = self.peek() { 651 | self.eat(); 652 | let right = self.equality(); 653 | exp = Expression::Logical { 654 | left: Box::new(exp), 655 | operator: Operator::And, 656 | right: Box::new(right), 657 | location: self.get_last_loc(), 658 | }; 659 | } 660 | exp 661 | } 662 | 663 | fn equality(&mut self) -> Expression { 664 | let mut exp = self.comparison(); 665 | while let Some(&Token::Op(Operator::NotEqual | Operator::Equal)) = self.peek() { 666 | let operator = Self::de_op(self.eat_out()); 667 | let location = self.get_last_loc(); 668 | let right = self.comparison(); 669 | exp = Expression::Binary { 670 | left: Box::new(exp), 671 | operator, 672 | right: Box::new(right), 673 | location, 674 | }; 675 | } 676 | exp 677 | } 678 | 679 | fn comparison(&mut self) -> Expression { 680 | let mut exp = self.term(); 681 | 682 | while let Some(&Token::Op( 683 | Operator::Less | Operator::LessEqual | Operator::Greater | Operator::GreaterEqual, 684 | )) = self.peek() 685 | { 686 | let operator = Self::de_op(self.eat_out()); 687 | let location = self.get_last_loc(); 688 | let right = self.term(); 689 | exp = Expression::Binary { 690 | left: Box::new(exp), 691 | operator, 692 | right: Box::new(right), 693 | location, 694 | }; 695 | } 696 | exp 697 | } 698 | 699 | fn term(&mut self) -> Expression { 700 | let mut exp = self.factor(); 701 | while let Some(&Token::Op(Operator::Add | Operator::Sub | Operator::Concat)) = 702 | self.peek() 703 | { 704 | let operator = Self::de_op(self.eat_out()); 705 | let location = self.get_last_loc(); 706 | let right = self.factor(); 707 | exp = Expression::Binary { 708 | left: Box::new(exp), 709 | operator, 710 | right: Box::new(right), 711 | location, 712 | }; 713 | } 714 | exp 715 | } 716 | 717 | fn factor(&mut self) -> Expression { 718 | let mut exp = self.unary(); 719 | while let Some(&Token::Op(Operator::Multiply | Operator::Divide | Operator::Modulus)) = 720 | self.peek() 721 | { 722 | let operator = Self::de_op(self.eat_out()); 723 | let right = self.unary(); 724 | let location = self.get_last_loc(); 725 | exp = Expression::Binary { 726 | left: Box::new(exp), 727 | operator, 728 | right: Box::new(right), 729 | location, 730 | }; 731 | } 732 | exp 733 | } 734 | 735 | // op unary or primary 736 | pub fn unary(&mut self) -> Expression { 737 | if let Some(&Token::Op(Operator::Sub | Operator::Not | Operator::Tilde)) = self.peek() { 738 | let operator = Self::de_op(self.eat_out()); 739 | let location = self.get_last_loc(); 740 | let right = self.unary(); 741 | Expression::Unary { 742 | operator, 743 | right: Box::new(right), 744 | location, 745 | } 746 | } else { 747 | self.anonymous_check() 748 | } 749 | } 750 | 751 | fn anonymous_check(&mut self) -> Expression { 752 | let exp = self.primary(); 753 | if let Some(&Token::ArrowFunction) = self.peek() { 754 | let location = self.get_loc(); 755 | self.eat(); 756 | let params = if let Expression::Variable { ident, location } = exp { 757 | vec![ident.0] 758 | } else { 759 | vec![] 760 | }; 761 | let block = self.block(); 762 | let func = Rc::new(Function::new(params, block)); 763 | return Expression::Function { 764 | value: func, 765 | location, 766 | }; 767 | } else { 768 | self.call(exp) 769 | } 770 | } 771 | 772 | fn call(&mut self, mut exp: Expression) -> Expression { 773 | while match self.peek() { 774 | Some(&Token::OpenParen) => { 775 | devout!(self "call"); 776 | //TODO while(true) with break but also calls the finishCall func? 777 | let start = self.get_loc(); 778 | match self.arguments(start) { 779 | Ok(args) => { 780 | exp = Expression::Call { 781 | callee: Box::new(exp), 782 | args, 783 | location: start, 784 | } 785 | } 786 | Err(e) => { 787 | self.error(e); 788 | return Expression::InvalidExpression; 789 | } 790 | } 791 | true 792 | } 793 | Some(&Token::StringLiteral(_)) => { 794 | devout!(self "call"); 795 | let start = self.get_loc(); 796 | if let Some(Token::StringLiteral(s)) = self.eat() { 797 | let ss = Box::new(s.to_string()); 798 | let args = vec![Expression::Literal { 799 | value: Value::String(ss), 800 | location: start, 801 | }]; 802 | exp = Expression::Call { 803 | callee: Box::new(exp), 804 | args, 805 | location: start, 806 | } 807 | } 808 | true 809 | } 810 | _ => false, 811 | } {} 812 | exp 813 | } 814 | 815 | fn arguments(&mut self, start: Location) -> Result, SiltError> { 816 | self.eat(); 817 | let mut args = vec![]; 818 | while !matches!(self.peek(), Some(&Token::CloseParen)) { 819 | args.push(self.expression()); 820 | if let Some(&Token::Comma) = self.peek() { 821 | self.eat(); 822 | } 823 | } 824 | devout!(self "arguments"); 825 | 826 | expect_token!( 827 | self, 828 | CloseParen, 829 | SiltError::UnterminatedParenthesis(start.0, start.1) 830 | ); 831 | 832 | Ok(args) 833 | } 834 | fn primary(&mut self) -> Expression { 835 | // Err(code) => { 836 | // println!("Error Heere: {} :{}", code, self.current); 837 | // self.error(code); 838 | // Expression::InvalidExpression 839 | // } 840 | devout!(self "primary"); 841 | 842 | let t = self.next(); 843 | let location = self.get_last_loc(); 844 | // errors will 1 ahead, use error_last 845 | match t { 846 | Some(Token::Number(n)) => Expression::Literal { 847 | value: Value::Number(n), 848 | location, 849 | }, 850 | Some(Token::StringLiteral(s)) => Expression::Literal { 851 | value: Value::String(Box::new(s.to_string())), 852 | location, 853 | }, 854 | Some(Token::Integer(i)) => Expression::Literal { 855 | value: Value::Integer(i), 856 | location, 857 | }, 858 | Some(Token::True) => Expression::Literal { 859 | value: Value::Bool(true), 860 | location, 861 | }, 862 | Some(Token::False) => Expression::Literal { 863 | value: Value::Bool(false), 864 | location, 865 | }, 866 | Some(Token::Nil) => Expression::Literal { 867 | value: Value::Nil, 868 | location, 869 | }, 870 | 871 | Some(Token::OpenParen) => { 872 | let start = self.get_last_loc(); // we're ahead normally, in this func we're ahead by 2 as we already ate, yummers 873 | let exp = self.expression(); 874 | if let Some(Token::CloseParen) = self.peek() { 875 | self.eat(); 876 | Expression::GroupingExpression { 877 | expression: Box::new(exp), 878 | location: start, 879 | } 880 | } else { 881 | self.error(SiltError::UnterminatedParenthesis(start.0, start.1)); 882 | Expression::InvalidExpression 883 | } 884 | } 885 | Some(Token::Identifier(ident)) => Expression::Variable { 886 | ident: (self.global.to_register(&ident), 0), 887 | location, 888 | }, 889 | Some(Token::Function) => self.function_expression(location), 890 | // Some(Token::EOF) => Ok(Expression::EndOfFile), 891 | Some(Token::Op(o)) => { 892 | self.error(SiltError::ExpInvalidOperator(o)); 893 | Expression::InvalidExpression 894 | } 895 | Some(tt) => { 896 | // TODO nil? 897 | self.error(SiltError::InvalidTokenPlacement(tt)); 898 | Expression::InvalidExpression 899 | } 900 | None => { 901 | self.error_last(SiltError::EarlyEndOfFile); 902 | Expression::InvalidExpression 903 | } 904 | } 905 | } 906 | 907 | fn de_op(t: Token) -> Operator { 908 | if let Token::Op(o) = t { 909 | return o; 910 | } 911 | panic!("Bad operator") // can this happen 912 | } 913 | } 914 | } 915 | -------------------------------------------------------------------------------- /src/archive/resolver.rs: -------------------------------------------------------------------------------- 1 | use std::{borrow::Borrow, rc::Rc}; 2 | 3 | use hashbrown::HashMap; 4 | 5 | use crate::{ 6 | error::{ErrorTuple, Location, SiltError}, 7 | expression::{self, Expression, Ident}, 8 | function::{self, Function}, 9 | statement::Statement, 10 | }; 11 | 12 | type ResolverScope = HashMap; 13 | pub struct Resolver { 14 | scopes: Vec, 15 | errors: Vec, 16 | } 17 | impl Resolver { 18 | pub fn new() -> Self { 19 | Self { 20 | scopes: Vec::new(), 21 | errors: Vec::new(), 22 | } 23 | } 24 | pub fn process(&mut self, statements: &mut Vec) { 25 | for statement in statements { 26 | self.resolve_statement(statement); 27 | } 28 | } 29 | pub fn resolve_statement(&mut self, statement: &mut Statement) { 30 | match statement { 31 | Statement::Expression(exp) => self.resolve(exp.as_mut()), 32 | Statement::Print(exp) => self.resolve(exp.as_mut()), 33 | Statement::Declare { ident, local, expr } => { 34 | // TODO seperating declare and define may only be necessary to chase down self assignment variables like local a=a 35 | // But this is probably not necessary in lua or luau strict mode as functions need to self reference 36 | // and we have no simple way to distinguish between a function and a variable declaration here 37 | self.declare(ident.0); 38 | self.define(ident.0); 39 | self.resolve(expr.as_mut()); 40 | } 41 | _=>{} 42 | // Statement::Block(s) => { 43 | // self.start_scope(); 44 | // self.process(s); 45 | // self.end_scope(); 46 | // } 47 | // Statement::If { 48 | // cond, 49 | // then, 50 | // else_cond, 51 | // } => { 52 | // // TODO should be scoped 53 | // self.resolve(*cond); 54 | // self.process(then); 55 | // if let Some(else_cond) = else_cond { 56 | // self.process(else_cond); 57 | // } 58 | // } 59 | // Statement::While { cond, block } => { 60 | // // TODO should be scoped 61 | // self.resolve(*cond); 62 | // self.process(block); 63 | // } 64 | // Statement::NumericFor { 65 | // ident, 66 | // start, 67 | // end, 68 | // step, 69 | // block, 70 | // } => todo!(), 71 | // Statement::Return(exp) => self.resolve(*exp), 72 | // Statement::Skip => todo!(), 73 | // Statement::InvalidStatement => todo!(), 74 | } 75 | } 76 | 77 | fn resolve(&mut self, expression: &mut Expression) { 78 | if self.scopes.is_empty() { 79 | return; 80 | } 81 | match expression { 82 | Expression::Binary { 83 | left, 84 | operator, 85 | right, 86 | location, 87 | } => { 88 | self.resolve(&mut *left); 89 | self.resolve(&mut *right); 90 | } 91 | Expression::Logical { 92 | left, 93 | operator, 94 | right, 95 | location, 96 | } => { 97 | self.resolve(&mut *left); 98 | self.resolve(&mut *right); 99 | } 100 | Expression::Unary { 101 | operator, 102 | right, 103 | location, 104 | } => { 105 | self.resolve(&mut *right); 106 | } 107 | Expression::Literal { value, location } => {} 108 | Expression::GroupingExpression { 109 | expression, 110 | location, 111 | } => { 112 | self.resolve(&mut *expression); 113 | } 114 | Expression::Variable { 115 | mut ident, 116 | location, 117 | } => { 118 | // if let Some(scope) = self.scopes.last() { 119 | // if scope.get(&ident) == Some(&false) { 120 | // self.error(SiltError::ResReadInOwnInit, location); 121 | // } 122 | // } 123 | self.resolve_local(&mut ident); 124 | // let mut found = false; 125 | // for scope in self.scopes.iter().rev() { 126 | // if scope.contains_key(&ident) { 127 | // found = true; 128 | // break; 129 | // } 130 | // } 131 | // if !found { 132 | // self.env.declare_global(self.env.to_register(&ident), expression::NIL); 133 | // } 134 | } 135 | Expression::Assign { 136 | mut ident, 137 | value, 138 | location, 139 | } => { 140 | self.resolve(&mut *value); 141 | self.resolve_local(&mut ident) 142 | } 143 | Expression::Call { 144 | callee, 145 | args: arguments, 146 | .. 147 | } => { 148 | self.resolve(&mut *callee); 149 | for arg in arguments { 150 | self.resolve(arg); 151 | } 152 | } 153 | Expression::Function { value, .. } => { 154 | // self.declare(value.ident); 155 | // self.resolve_function(&mut value); 156 | 157 | self.resolve_function(value); 158 | } 159 | Expression::InvalidExpression => {} 160 | _ => {} 161 | } 162 | } 163 | fn error(&mut self, code: SiltError, location: Location) { 164 | self.errors.push(ErrorTuple { code, location }); 165 | } 166 | fn start_scope(&mut self) { 167 | self.scopes.push(HashMap::new()); 168 | } 169 | fn end_scope(&mut self) { 170 | self.scopes.pop(); 171 | } 172 | fn declare(&mut self, ident: usize) { 173 | if self.scopes.is_empty() { 174 | return; 175 | } 176 | 177 | if let Some(scope) = self.scopes.last_mut() { 178 | scope.insert(ident, false); 179 | } 180 | } 181 | fn define(&mut self, ident: usize) { 182 | if self.scopes.is_empty() { 183 | return; 184 | } 185 | 186 | if let Some(scope) = self.scopes.last_mut() { 187 | scope.insert(ident, true); 188 | } 189 | } 190 | 191 | fn resolve_local(&mut self, ident: &mut Ident) { 192 | for (i, scope) in self.scopes.iter().enumerate().rev() { 193 | if scope.contains_key(&ident.0) { 194 | *ident = (ident.0, i); 195 | // self.env.resolve_local(ident, self.scopes.len() - 1 - i); 196 | // self.env.scope.set(*ident, v, true, *local); 197 | // Interpreter::resovle(self.env, ident, i); 198 | // self.env.resolve_local(ident, i); 199 | return; 200 | } 201 | } 202 | } 203 | fn resolve_function(&mut self, function: &mut Rc) { 204 | self.start_scope(); 205 | for param in function.params.iter() { 206 | self.declare(*param); 207 | self.define(*param); 208 | } 209 | 210 | // make a new function with the processed statements 211 | 212 | // let processed: Vec = function 213 | // .body 214 | // .iter() 215 | // .map(|s| { 216 | // self.resolve_statement(s); 217 | // s 218 | // }) 219 | // .collect(); 220 | 221 | // let new = Rc::new(function::Function::new(function.params.clone(), processed)); 222 | 223 | let body = function.body.clone(); 224 | 225 | body.iter() 226 | .for_each(|s| self.resolve_statement(&mut s.clone())); 227 | 228 | *function = Rc::new(function::Function::new(function.params.clone(), body)); 229 | // let mut new: Function = (**function) 230 | // .body 231 | // .iter() 232 | // .map(|s| { 233 | // let s2 = s.clone(); 234 | 235 | // self.resolve_statement(s); 236 | // s 237 | // }) 238 | // .collect(); 239 | // new.body = processed; 240 | // let new = Rc::new(new); 241 | 242 | // self.process(function.body); 243 | self.end_scope(); 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /src/archive/statement.rs: -------------------------------------------------------------------------------- 1 | use crate::expression::{Expression, Ident}; 2 | 3 | #[derive(Clone)] 4 | pub enum Statement { 5 | Expression(Box), 6 | Print(Box), 7 | Declare { 8 | ident: Ident, 9 | local: bool, 10 | expr: Box, 11 | }, 12 | // Var(Token, Expression), 13 | Block(Vec), 14 | If { 15 | cond: Box, 16 | then: Vec, 17 | else_cond: Option>, 18 | }, 19 | While { 20 | cond: Box, 21 | block: Vec, 22 | }, 23 | NumericFor { 24 | /** no need to track variable depth */ 25 | ident: usize, 26 | start: Box, 27 | end: Box, 28 | step: Option>, 29 | block: Vec, 30 | }, 31 | Return(Box), 32 | // Break, 33 | // Continue, 34 | // Function { 35 | // ident: usize, 36 | // local: bool, 37 | // args: Vec, 38 | // block: Vec, 39 | // }, 40 | // Function(Token, Vec, Box), 41 | // Return(Token, Option), 42 | // Class(Token, Option, Vec), 43 | // Import(Token, Option), 44 | // EOF, 45 | Skip, 46 | InvalidStatement, 47 | } 48 | 49 | impl std::fmt::Display for Statement { 50 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 51 | match self { 52 | Statement::Expression(expr) => write!(f, "$${}", expr), 53 | Statement::Skip => write!(f, ";"), 54 | Statement::Print(expr) => write!(f, "$print$ {}", expr), 55 | Self::Return(expr) => write!(f, "$return$ {}", expr), 56 | Statement::Declare { 57 | ident, 58 | local, 59 | expr: value, 60 | } => write!( 61 | f, 62 | "$declare$ {} {}:{} := {}", 63 | if *local { "local" } else { "global" }, 64 | ident.0, 65 | ident.1, 66 | value 67 | ), 68 | Statement::InvalidStatement => write!(f, "!invalid!"), 69 | Statement::Block(statements) => { 70 | let mut s = String::new(); 71 | for statement in statements { 72 | s.push_str(&format!("\n||{}", statement)); 73 | } 74 | write!(f, "$block$ {}", s) 75 | } 76 | Statement::If { 77 | cond, 78 | then, 79 | else_cond, 80 | } => { 81 | let mut s = String::new(); 82 | for statement in then { 83 | s.push_str(&format!("\n||{}", statement)); 84 | } 85 | 86 | let mut s = format!("$if$ {} then {}", cond, s); 87 | 88 | if let Some(else_cond) = else_cond { 89 | let mut s2 = String::new(); 90 | for statement in then { 91 | s2.push_str(&format!("\n||{}", statement)); 92 | } 93 | s.push_str(&format!(" else {}", s2)); 94 | } 95 | write!(f, "{}", s) 96 | } 97 | Statement::While { cond, block } => { 98 | let mut s = String::new(); 99 | for statement in block { 100 | s.push_str(&format!("\n||{}", statement)); 101 | } 102 | write!(f, "$while$ {} {}", cond, s) 103 | } 104 | Statement::NumericFor { 105 | ident, 106 | start, 107 | end, 108 | step, 109 | block, 110 | } => { 111 | let mut s = String::new(); 112 | for statement in block { 113 | s.push_str(&format!("\n||{}", statement)); 114 | } 115 | write!( 116 | f, 117 | "$for$ {} := {} to {} step {}", 118 | ident, 119 | start, 120 | end, 121 | match step { 122 | Some(step) => format!("{}", step), 123 | None => format!("1"), 124 | } 125 | ) 126 | } // Statement::Function { 127 | // ident, 128 | // local, 129 | // args, 130 | // block, 131 | // } => { 132 | // let mut s = String::new(); 133 | // for statement in block { 134 | // s.push_str(&format!("\n||{}", statement)); 135 | // } 136 | // write!( 137 | // f, 138 | // "$function$ {} {} {}", 139 | // if *local { "local" } else { "global" }, 140 | // ident, 141 | // s 142 | // ) 143 | // } 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/chunk.rs: -------------------------------------------------------------------------------- 1 | use std::vec; 2 | 3 | use crate::{code::OpCode, error::Location, value::Value}; 4 | 5 | // TODO benchmark/compare to using a manually resized array 6 | #[derive(Default)] 7 | pub struct Chunk { 8 | pub code: Vec, 9 | constants: Vec, //TODO VALUE ARRAY typedef faster? 10 | locations: Vec<(usize, usize)>, 11 | valid: bool, 12 | } 13 | 14 | impl Chunk { 15 | pub fn new() -> Self { 16 | Self { 17 | code: vec![], 18 | constants: vec![], 19 | locations: vec![], 20 | valid: true, 21 | } 22 | } 23 | // capacity < 8 ? 8: capacity*2 24 | 25 | pub fn write_code(&mut self, byte: OpCode, location: Location) -> usize { 26 | // TODO https://shnatsel.medium.com/how-to-avoid-bounds-checks-in-rust-without-unsafe-f65e618b4c1e 27 | self.code.push(byte); 28 | self.locations.push(location); 29 | self.code.len() - 1 30 | } 31 | pub fn read_last_code(&self) -> &OpCode { 32 | self.code.last().unwrap() 33 | } 34 | 35 | pub fn write_constant(&mut self, value: Value) -> usize { 36 | self.constants.push(value); 37 | // TODO limit to u8 38 | self.constants.len() - 1 39 | } 40 | 41 | // TODO lets change to a hashmap, cant see an advantage not to so far 42 | /** for global identifiers we attempt to resolve to an existing global variable if it exists and return that index */ 43 | pub fn write_identifier(&mut self, identifier: Box) -> usize { 44 | match self.constants.iter().enumerate().position(|(i, x)| { 45 | if let Value::String(s) = x { 46 | s == &identifier 47 | } else { 48 | false 49 | } 50 | }) { 51 | Some(i) => i, 52 | None => self.write_constant(Value::String(identifier)), 53 | } 54 | } 55 | 56 | pub fn write_value(&mut self, value: Value, location: Location) { 57 | let u = self.write_constant(value); 58 | self.write_code(OpCode::CONSTANT { constant: u as u8 }, location); 59 | } 60 | 61 | pub fn get_constant(&self, index: u8) -> &Value { 62 | &self.constants[index as usize] 63 | } 64 | pub fn invalidate(&mut self) { 65 | self.valid = false; 66 | } 67 | pub fn is_valid(&self) -> bool { 68 | self.valid 69 | } 70 | 71 | // fn add_constant(&mut self, value: Value) -> usize { 72 | // self.constants.write_value(value) 73 | // } 74 | pub fn print_constants(&self) { 75 | println!("constants: {}", self.constants.len()); 76 | self.constants.iter().for_each(|c| { 77 | print!(" {},", c); 78 | }); 79 | println!(); 80 | } 81 | pub fn print_chunk(&self, name: Option) { 82 | match name { 83 | Some(n) => println!("=== Chunk ({}) ===", n), 84 | None => println!("=== Root Chunk ==="), 85 | } 86 | println!("code chunk: {}", self.code.len()); 87 | let mut last = 0; 88 | for (i, c) in self.code.iter().enumerate() { 89 | let l = self.locations[i]; 90 | if last == l.0 { 91 | print!(" "); 92 | } else { 93 | print!("{:03}", l.0); 94 | } 95 | // println!("{} {}:{}", c, l.0, l.1); 96 | let constant = match c { 97 | OpCode::CONSTANT { constant } 98 | | OpCode::DEFINE_GLOBAL { constant } 99 | | OpCode::GET_GLOBAL { constant } 100 | | OpCode::DEFINE_LOCAL { constant } => { 101 | format!("({})", self.get_constant(*constant)) 102 | } 103 | OpCode::GET_LOCAL { index } | OpCode::SET_LOCAL { index } => { 104 | format!("(${})", index) 105 | } 106 | _ => String::new(), 107 | }; 108 | 109 | println!(":{:02} | {} {}", l.1, c, constant); 110 | last = l.0; 111 | } 112 | println!("constants: {}", self.constants.len()); 113 | self.constants.iter().for_each(|c| { 114 | print!(" {},", c); 115 | }); 116 | println!(); 117 | self.constants.iter().for_each(|c| { 118 | if let Value::Function(f) = c { 119 | f.chunk.print_chunk(match &f.name { 120 | Some(n) => Some(n.clone()), 121 | None => Some("anon-fn".to_string()), 122 | }); 123 | } 124 | }); 125 | } 126 | 127 | pub fn free(&mut self) { 128 | self.code.clear(); 129 | self.constants.clear(); 130 | self.locations.clear(); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/code.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self, Display, Formatter}; 2 | 3 | #[allow(non_camel_case_types)] 4 | #[derive(Clone)] 5 | pub enum OpCode { 6 | CONSTANT { 7 | constant: u8, 8 | }, 9 | CLOSURE { 10 | constant: u8, 11 | }, 12 | DEFINE_GLOBAL { 13 | constant: u8, 14 | }, 15 | GET_GLOBAL { 16 | constant: u8, 17 | }, 18 | SET_GLOBAL { 19 | constant: u8, 20 | }, 21 | DEFINE_LOCAL { 22 | constant: u8, 23 | }, 24 | GET_LOCAL { 25 | index: u8, 26 | }, 27 | SET_LOCAL { 28 | index: u8, 29 | }, 30 | GET_UPVALUE { 31 | index: u8, 32 | }, 33 | SET_UPVALUE { 34 | index: u8, 35 | }, 36 | // TODO this the size bottleneck but we were considering word size anyway soooooo 37 | // TODO also we could explore a popless goto_if for if statements while conditionals still use the pop variant 38 | GOTO_IF_FALSE(u16), 39 | GOTO_IF_TRUE(u16), 40 | // TODO this should replace normal goto_if_false 41 | POP_AND_GOTO_IF_FALSE(u16), 42 | /** Compares 2nd(end) and 3rd(iter) value on stack, if greater then forward by X, otherwise push 3rd(iter) on to new stack */ 43 | FOR_NUMERIC(u16), 44 | FORWARD(u16), 45 | REWIND(u16), 46 | RETURN, 47 | POP, 48 | POPS(u8), 49 | CLOSE_UPVALUES(u8), 50 | ADD, 51 | SUB, 52 | MULTIPLY, 53 | DIVIDE, 54 | NEGATE, 55 | CONCAT, 56 | NOT, 57 | NIL, 58 | TRUE, 59 | FALSE, 60 | EQUAL, 61 | NOT_EQUAL, 62 | LESS, 63 | LESS_EQUAL, 64 | GREATER, 65 | GREATER_EQUAL, 66 | PRINT, 67 | META(u8), 68 | CALL(u8), 69 | REGISTER_UPVALUE { 70 | index: u8, 71 | neighboring: bool, 72 | }, 73 | 74 | LITERAL { 75 | dest: u8, 76 | literal: u8, 77 | }, 78 | LENGTH, 79 | NEW_TABLE, 80 | TABLE_INSERT { 81 | offset: u8, 82 | }, 83 | TABLE_BUILD(u8), 84 | TABLE_GET { 85 | depth: u8, 86 | }, 87 | TABLE_GET_BY_CONSTANT { 88 | constant: u8, 89 | }, 90 | TABLE_GET_FROM { 91 | index: u8, 92 | }, 93 | /** depth indicates how many index contansts are on the stack. Statement does not need pop */ 94 | TABLE_SET { 95 | depth: u8, 96 | }, 97 | // TABLE_SET_BY_CONSTANT { 98 | // constant: u8, 99 | // }, 100 | /** Increment a local value at index with top of stack*/ 101 | INCREMENT { 102 | index: u8, 103 | }, 104 | } 105 | pub enum Tester { 106 | CONSTANT(u8), 107 | RETURN, 108 | } 109 | 110 | impl Display for OpCode { 111 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 112 | match self { 113 | Self::CALL(i) => write!(f, "OP_CALL({})", i), 114 | Self::REGISTER_UPVALUE { 115 | index: i, 116 | neighboring: n, 117 | } => write!(f, "OP_REG_UPVALUE {} {}", if *n { "⬆️" } else { "⬆️⬆️" }, i), 118 | Self::GET_UPVALUE { index: i } => { 119 | write!(f, "OP_GET_UPVALUE {}", i) 120 | } 121 | Self::SET_UPVALUE { index: i } => { 122 | write!(f, "OP_SET_UPVALUE {}", i) 123 | } 124 | Self::META(_) => write!(f, "META"), 125 | Self::GOTO_IF_FALSE(offset) => { 126 | write!(f, "OP_GOTO_IF_FALSE {}", offset) 127 | } 128 | Self::POP_AND_GOTO_IF_FALSE(offset) => { 129 | write!(f, "OP_POP_AND_GOTO_IF_FALSE {}", offset) 130 | } 131 | Self::GOTO_IF_TRUE(offset) => { 132 | write!(f, "OP_GOTO_IF_TRUE {}", offset) 133 | } 134 | Self::FORWARD(offset) => write!(f, "OP_FORWARD {}", offset), 135 | Self::REWIND(offset) => write!(f, "OP_REWIND {}", offset), 136 | Self::FOR_NUMERIC(offset) => { 137 | write!(f, "OP_FOR_NUMERIC {}", offset) 138 | } 139 | Self::DEFINE_GLOBAL { constant } => { 140 | write!(f, "OP_DEFINE_GLOBAL {}", constant) 141 | } 142 | Self::GET_GLOBAL { constant } => { 143 | write!(f, "OP_GET_GLOBAL {}", constant) 144 | } 145 | Self::DEFINE_LOCAL { constant } => { 146 | write!(f, "OP_DEFINE_LOCAL {}", constant) 147 | } 148 | Self::RETURN => write!(f, "OP_RETURN"), 149 | Self::POP => write!(f, "OP_POP"), 150 | Self::POPS(n) => { 151 | write!(f, "OP_POPx{}", n) 152 | } 153 | Self::CLOSE_UPVALUES(n) => { 154 | write!(f, "OP_CLOSE_UPVALUEx{}", n) 155 | } 156 | Self::CONSTANT { constant } => { 157 | write!(f, "OP_CONSTANT {}", constant) 158 | } 159 | Self::CLOSURE { constant } => { 160 | write!(f, "OP_CLOSURE {}", constant) 161 | } 162 | Self::ADD => { 163 | write!(f, "OP_ADD") 164 | } 165 | Self::SUB => { 166 | write!(f, "OP_SUBTRACT") 167 | } 168 | Self::MULTIPLY => { 169 | write!(f, "OP_MULTIPLY") 170 | } 171 | Self::DIVIDE => { 172 | write!(f, "OP_DIVIDE") 173 | } 174 | Self::NEGATE => write!(f, "OP_NEGATE"), 175 | Self::CONCAT => write!(f, "OP_CONCAT"), 176 | Self::LITERAL { dest, literal } => { 177 | write!(f, "OP_LITERAL {} {}", dest, literal) 178 | } 179 | Self::NIL => write!(f, "OP_NIL"), 180 | Self::TRUE => write!(f, "OP_TRUE"), 181 | Self::FALSE => write!(f, "OP_FALSE"), 182 | Self::NOT => write!(f, "OP_NOT"), 183 | Self::PRINT => write!(f, "OP_PRINT"), 184 | 185 | Self::EQUAL => write!(f, "OP_EQUAL"), 186 | Self::NOT_EQUAL => write!(f, "OP_NOT_EQUAL"), 187 | Self::LESS => write!(f, "OP_LESS"), 188 | Self::LESS_EQUAL => write!(f, "OP_LESS_EQUAL"), 189 | Self::GREATER => write!(f, "OP_GREATER"), 190 | Self::GREATER_EQUAL => write!(f, "OP_GREATER_EQUAL"), 191 | Self::SET_GLOBAL { constant } => { 192 | write!(f, "OP_SET_GLOBAL {}", constant) 193 | } 194 | Self::GET_LOCAL { index } => { 195 | write!(f, "OP_GET_LOCAL {}", index) 196 | } 197 | Self::SET_LOCAL { index } => { 198 | write!(f, "OP_SET_LOCAL {}", index) 199 | } 200 | Self::LENGTH => write!(f, "OP_LENGTH"), 201 | Self::NEW_TABLE => write!(f, "OP_NEW_TABLE"), 202 | Self::TABLE_INSERT { offset } => write!(f, "OP_TABLE_INSERT @{}", offset), 203 | Self::TABLE_BUILD(u) => write!(f, "OP_TABLE_BUILD [;{}]", u), 204 | Self::TABLE_GET { depth } => write!(f, "OP_TABLE_GET {}[]", depth), 205 | Self::TABLE_GET_BY_CONSTANT { constant } => { 206 | write!(f, "OP_TABLE_GET_BY_CONSTANT {}", constant) 207 | } 208 | Self::TABLE_GET_FROM { index } => { 209 | write!(f, "OP_TABLE_GET_FROM {}", index) 210 | } 211 | Self::TABLE_SET { depth } => write!(f, "OP_TABLE_SET {}[]", depth), 212 | // Self::TABLE_SET_BY_CONSTANT { constant } => { 213 | // write!(f, "OP_TABLE_SET_BY_CONSTANT {}", constant) 214 | // } 215 | Self::INCREMENT { index } => write!(f, "OP_INCREMENT {}", index), 216 | } 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | token::{Operator, Token}, 3 | value::{self, Value}, 4 | }; 5 | 6 | #[derive(Clone, PartialEq, Debug)] 7 | pub enum SiltError { 8 | //parse errors 9 | InvalidNumber(String), 10 | NotANumber(String), 11 | UnexpectedCharacter(char), 12 | UnterminatedString, 13 | UnterminatedParenthesis(usize, usize), 14 | UnterminatedBracket(usize, usize), 15 | InvalidTokenPlacement(Token), 16 | InvalidColonPlacement, // more specific to types and calls 17 | ExpectedLocalIdentifier, 18 | ExpectedLabelIdentifier, 19 | ExpectedGotoIdentifier, 20 | ExpectedFieldIdentifier, 21 | TableExpectedCommaOrCloseBrace, 22 | UndefinedLabel(String), 23 | InvalidAssignment(Token), 24 | UnterminatedBlock, 25 | ExpectedThen, 26 | ExpectedDo, 27 | ExpectedToken(Token), 28 | TooManyLocals, 29 | TooManyOperations, 30 | TooManyParameters, 31 | ChunkCorrupt, 32 | 33 | //expression errors 34 | ExpInvalidOperator(Operator), 35 | ExpInvalidBitwise(ErrorTypes), 36 | ExpInvalidLength(ErrorTypes), 37 | ExpOpValueWithValue(ErrorTypes, Operator, ErrorTypes), 38 | ExpInvalidNegation(ErrorTypes), 39 | EarlyEndOfFile, 40 | ExpInvalid, 41 | ExpectedAssign, 42 | 43 | // resolver errors 44 | // ResReadInOwnInit, 45 | 46 | // statement errors 47 | 48 | //interpreted errors 49 | EvalNoInteger(ErrorTypes), 50 | NotCallable(String), 51 | Return(Value), 52 | 53 | //vm 54 | VmCompileError, 55 | VmRuntimeError, 56 | VmCorruptConstant, 57 | VmUpvalueResolveError, 58 | VmNonTableOperations(ErrorTypes), 59 | 60 | Unknown, 61 | } 62 | 63 | #[derive(Debug, Clone, PartialEq)] 64 | pub enum ErrorTypes { 65 | String, 66 | Number, 67 | Operator, 68 | Integer, 69 | Bool, 70 | Nil, 71 | Infinity, 72 | NativeFunction, 73 | Function, 74 | Closure, 75 | Table, 76 | UserData, 77 | } 78 | 79 | pub type Location = (usize, usize); 80 | 81 | impl std::fmt::Display for SiltError { 82 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 83 | match self { 84 | Self::VmUpvalueResolveError => { 85 | write!(f, "Unexpected issue resolving upvalue for closure") 86 | } 87 | Self::ExpectedAssign => write!(f, "Expected assignment operator '='"), 88 | Self::TableExpectedCommaOrCloseBrace => { 89 | write!( 90 | f, 91 | "Table expected a comma or termination by closing brace '}}'" 92 | ) 93 | } 94 | Self::UndefinedLabel(s) => write!(f, "No matching goto label for '{}'", s), 95 | Self::ExpectedGotoIdentifier => write!(f, "Expected identifier following goto keyword"), 96 | Self::ExpectedFieldIdentifier => { 97 | write!(f, "Expected identifier following field accessor '.'") 98 | } 99 | Self::ChunkCorrupt => write!(f, "Invalid chunk due compilation corruption"), 100 | Self::TooManyOperations => write!( 101 | f, 102 | "Too many operations within this condition, limited to 65535" 103 | ), 104 | Self::TooManyLocals => write!(f, "Too many local variables, limited to 255"), 105 | Self::TooManyParameters => write!(f, "Too many parameters, limited to 255"), 106 | Self::Return(v) => write!(f, "~Return {}~", v), 107 | Self::InvalidNumber(s) => write!(f, "Invalid number: {}", s), 108 | Self::NotANumber(s) => write!(f, "Not a number: {}", s), 109 | Self::UnexpectedCharacter(c) => write!(f, "Unexpected character: {}", c), 110 | Self::UnterminatedString => write!(f, "Unterminated string"), 111 | Self::UnterminatedParenthesis(x, y) => { 112 | write!( 113 | f, 114 | "Expected closing paren due to open paren '(' here {}:{}", 115 | x, y 116 | ) 117 | } 118 | Self::UnterminatedBracket(x, y) => { 119 | write!( 120 | f, 121 | "Expected closing bracket due to open bracket '[' here {}:{}", 122 | x, y 123 | ) 124 | } 125 | Self::ExpInvalidOperator(t) => write!(f, "Invalid expression token: {}", t), 126 | Self::EarlyEndOfFile => write!(f, "File ended early"), 127 | Self::ExpOpValueWithValue(v1, op, v2) => { 128 | write!(f, "Cannot {} '{}' and '{}'", op, v1, v2) 129 | } 130 | Self::VmNonTableOperations(v) => { 131 | write!( 132 | f, 133 | "Cannot perform table operations on a non-table value: {}", 134 | v 135 | ) 136 | } 137 | SiltError::ExpInvalidNegation(v) => write!(f, "Cannot negate '{}'", v), 138 | SiltError::InvalidTokenPlacement(t) => write!(f, "Invalid token placement: {}", t), 139 | SiltError::InvalidColonPlacement => { 140 | write!(f, "Colon must be followed by type and assigned or a call") 141 | } 142 | SiltError::ExpInvalidBitwise(v) => write!(f, "Cannot bitwise on '{}'", v), 143 | Self::ExpInvalidLength(v) => write!(f, "Cannot get length of '{}'", v), 144 | SiltError::EvalNoInteger(v) => { 145 | write!(f, "{} has no direct integer conversion for operation", v) 146 | } 147 | SiltError::ExpectedLocalIdentifier => { 148 | write!(f, "Expected identifier following local keyword") 149 | } 150 | Self::ExpectedLabelIdentifier => { 151 | write!(f, "Expected identifier only inbetween label tokens `::`") 152 | } 153 | SiltError::InvalidAssignment(t) => { 154 | write!(f, "Cannot use assignment operator '{}' on declaration", t) 155 | } 156 | SiltError::UnterminatedBlock => write!(f, "Unterminated block"), 157 | SiltError::ExpectedThen => write!(f, "Expected 'then' after if condition"), 158 | SiltError::ExpectedDo => write!(f, "Expected 'do' after while condition"), 159 | Self::ExpectedToken(t) => write!(f, "Expected token: {}", t), 160 | Self::NotCallable(s) => write!(f, "Value '{}' is not callable", s), 161 | Self::ExpInvalid => write!(f, "Invalid expression"), 162 | Self::VmCompileError => write!(f, "Error compiling chunk"), 163 | Self::VmRuntimeError => write!(f, "Runtime error for chunk"), 164 | Self::VmCorruptConstant => write!(f, "Constant store corrupted"), 165 | Self::Unknown => write!(f, "Unknown error"), 166 | // Self::ResReadInOwnInit => write!(f, "Cannot read variable in its own initializer"), 167 | } 168 | } 169 | } 170 | impl std::fmt::Display for ErrorTypes { 171 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 172 | match self { 173 | ErrorTypes::String => write!(f, "string"), 174 | ErrorTypes::Number => write!(f, "number"), 175 | ErrorTypes::Operator => write!(f, "operator"), 176 | ErrorTypes::Integer => write!(f, "integer"), 177 | ErrorTypes::Bool => write!(f, "bool"), 178 | ErrorTypes::Nil => write!(f, "nil"), 179 | ErrorTypes::Infinity => write!(f, "infinity"), 180 | ErrorTypes::NativeFunction => write!(f, "native_function"), 181 | ErrorTypes::Function => write!(f, "function"), 182 | ErrorTypes::Closure => write!(f, "(function)"), 183 | ErrorTypes::Table => write!(f, "table"), 184 | ErrorTypes::UserData => write!(f, "userdata"), 185 | } 186 | } 187 | } 188 | 189 | #[derive(Clone)] 190 | pub struct ErrorTuple { 191 | pub code: SiltError, 192 | pub location: Location, 193 | } 194 | 195 | impl std::fmt::Display for ErrorTuple { 196 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 197 | write!(f, "{}@{}:{}", self.code, self.location.0, self.location.1) 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/function.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::RefCell, fmt::Display, rc::Rc}; 2 | 3 | use crate::{chunk::Chunk, code::OpCode, lua::Lua, value::Value}; 4 | 5 | ///////////// 6 | /// 7 | pub struct CallFrame { 8 | pub function: Rc, // pointer 9 | // ip: *const OpCode 10 | // pub base: usize, 11 | // pointer point sinto VM values stack 12 | pub stack_snapshot: usize, 13 | pub local_stack: *mut Value, 14 | pub ip: *const OpCode, 15 | } 16 | 17 | impl<'frame> CallFrame { 18 | pub fn new(function: Rc, stack_snapshot: usize) -> Self { 19 | let ip = function.function.chunk.code.as_ptr(); 20 | Self { 21 | function, 22 | ip, 23 | local_stack: std::ptr::null_mut(), 24 | stack_snapshot, 25 | } 26 | } 27 | 28 | pub fn current_instruction(&self) -> &crate::code::OpCode { 29 | // &self.function.chunk.code[self.ip] 30 | unsafe { &*self.ip } 31 | } 32 | 33 | /** shift ip by 1 instruction */ 34 | pub fn iterate(&mut self) { 35 | // self.ip += 1; 36 | self.ip = unsafe { self.ip.add(1) }; 37 | } 38 | 39 | /** DANGER: does not shift ip, only returns instruction set in range past ip */ 40 | pub fn get_next_n_codes(&self, n: usize) -> &[OpCode] { 41 | // &self.function.chunk.code[self.ip..self.ip + n] 42 | const SIZE: usize = std::mem::size_of::(); 43 | unsafe { std::slice::from_raw_parts(self.ip.add(1), n * SIZE) } 44 | } 45 | 46 | /** move ip N instructions over */ 47 | pub fn shift(&mut self, n: usize) { 48 | // self.ip += n; 49 | self.ip = unsafe { self.ip.add(n) }; 50 | } 51 | 52 | pub fn set_val(&mut self, index: u8, value: Value) { 53 | // self.stack[index as usize] = value; 54 | unsafe { *self.local_stack.add(index as usize) = value }; 55 | } 56 | 57 | pub fn get_val(&self, index: u8) -> &Value { 58 | // &self.stack[index as usize] 59 | // println!("get_val: {}", index); 60 | // println!("top: {}", unsafe { &*self.local_stack }); 61 | unsafe { &*self.local_stack.add(index as usize) } 62 | } 63 | 64 | pub fn get_val_mut(&self, index: u8) -> &mut Value { 65 | unsafe { &mut *self.local_stack.add(index as usize) } 66 | } 67 | 68 | pub fn print_local_stack(&self) { 69 | println!("local stack: {:?}", unsafe { 70 | std::slice::from_raw_parts(self.local_stack, 10) 71 | }); 72 | } 73 | 74 | // pub fn push(&mut self, value: Value) { 75 | // // TODO can we push to the stack by pointer? Or should we just push on a Vec? 76 | // // *self.stack_top= value; 77 | 78 | // // unsafe { *self.stack_top = value }; 79 | // // self.stack.push(value); 80 | // // self.stack_top = self.stack.as_ptr().add(self.stack.len()); 81 | 82 | // // unsafe { *self.stack_top = value }; 83 | // // self.stack_top = unsafe { self.stack_top.add(1) }; 84 | // self.stack.push(value); 85 | // } 86 | 87 | // pub fn push(&mut self, value: Value) { 88 | // println!("pushing: {}", value); 89 | // // self.stack.push(value); 90 | // self.stack = unsafe { self.stack.add(1) }; 91 | // unsafe { *self.stack = value }; 92 | // } 93 | 94 | /** pop and return top of stack */ 95 | // pub fn pop(&mut self) -> Value { 96 | // // self.stack.pop().unwrap() 97 | // // let d = self.stack.wrapping_add(1); 98 | // // take value 99 | // let v = unsafe { std::mem::replace(&mut *self.stack, Value::Nil) }; 100 | // self.stack = unsafe { self.stack.sub(1) }; 101 | // v 102 | 103 | // // let o = unsafe { &*self.stack }; 104 | // // o 105 | // } 106 | 107 | /** pop N number of values from stack */ 108 | // pub fn popn(&mut self, n: u8) { 109 | // // self.stack.truncate(self.stack.len() - n as usize); 110 | // self.stack = unsafe { self.stack.sub(n as usize) }; 111 | // } 112 | 113 | /** take and replace with a Nil */ 114 | pub fn take(&mut self) -> &Value { 115 | // self.stack_top = unsafe { self.stack_top.sub(1) }; 116 | // unsafe { *self.stack_top } 117 | let v = unsafe { &*self.local_stack }; 118 | unsafe { *self.local_stack = Value::Nil }; 119 | v 120 | } 121 | 122 | // TODO validate safety of this, compiler has to be solid af! 123 | pub fn forward(&mut self, offset: u16) { 124 | // self.ip += offset as usize; 125 | self.ip = unsafe { self.ip.add(offset as usize) }; 126 | } 127 | 128 | pub fn rewind(&mut self, offset: u16) { 129 | // self.ip -= offset as usize; 130 | self.ip = unsafe { self.ip.sub(offset as usize) }; 131 | // println!("rewind: {}", unsafe { &*self.ip }); 132 | } 133 | } 134 | #[derive(Default)] 135 | pub struct FunctionObject { 136 | pub is_script: bool, 137 | pub name: Option, 138 | pub chunk: Chunk, 139 | pub upvalue_count: u8, 140 | // pub arity: usize, 141 | } 142 | 143 | impl FunctionObject { 144 | pub fn new(name: Option, is_script: bool) -> Self { 145 | Self { 146 | name, 147 | is_script, 148 | chunk: Chunk::new(), 149 | upvalue_count: 0, 150 | } 151 | } 152 | pub fn set_chunk(&mut self, chunk: Chunk) { 153 | self.chunk = chunk; 154 | } 155 | } 156 | 157 | impl Display for FunctionObject { 158 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 159 | if self.is_script { 160 | write!( 161 | f, 162 | "module={}", 163 | self.name.as_ref().unwrap_or(&"root".to_string()) 164 | ) 165 | } else { 166 | write!( 167 | f, 168 | "fn {}()", 169 | self.name.as_ref().unwrap_or(&"anonymous".to_string()) 170 | ) 171 | } 172 | } 173 | } 174 | 175 | pub type NativeFunction = fn(&mut Lua, Vec) -> Value; // TODO should be Result for runtime errors 176 | pub struct NativeObject { 177 | name: String, 178 | pub function: fn(&mut Lua, Vec) -> Value, 179 | } 180 | 181 | impl NativeObject { 182 | pub fn new(name: String, function: NativeFunction) -> Self { 183 | Self { name, function } 184 | } 185 | } 186 | 187 | pub struct Closure { 188 | pub function: Rc, 189 | pub upvalues: Vec>>, 190 | } 191 | 192 | impl Closure { 193 | pub fn new(function: Rc, upvalues: Vec>>) -> Self { 194 | Self { function, upvalues } 195 | } 196 | pub fn print_upvalues(&self) { 197 | self.upvalues.iter().enumerate().for_each(|(i, f)| { 198 | println!("fn-up {}:{}", i, f.borrow()); 199 | }); 200 | } 201 | } 202 | 203 | pub struct UpValue { 204 | // is_open: bool, 205 | // obj? 206 | pub index: u8, 207 | closed: Value, 208 | pub location: *mut Value, 209 | // pub value: *mut Value, // TODO oshould be a RC mutex of the value ideally 210 | } 211 | impl UpValue { 212 | pub fn new(index: u8, location: *mut Value) -> Self { 213 | Self { 214 | index, 215 | closed: Value::Nil, 216 | location, 217 | } 218 | } 219 | pub fn set_value(&mut self, value: Value) { 220 | unsafe { *self.location = value } 221 | } 222 | pub fn close_around(&mut self, value: Value) { 223 | self.closed = value; 224 | self.location = &mut self.closed as *mut Value; 225 | } 226 | pub fn close(&mut self) { 227 | #[cfg(feature = "dev-out")] 228 | println!("closing: {}", unsafe { &*self.location }); 229 | self.closed = unsafe { self.location.replace(Value::Nil) }; 230 | #[cfg(feature = "dev-out")] 231 | println!("closed: {}", self.closed); 232 | self.location = &mut self.closed as *mut Value; 233 | } 234 | pub fn copy_value(&self) -> Value { 235 | #[cfg(feature = "dev-out")] 236 | println!("copying: {}", unsafe { &*self.location }); 237 | unsafe { (*self.location).clone() } 238 | } 239 | pub fn get_location(&self) -> *mut Value { 240 | #[cfg(feature = "dev-out")] 241 | println!("getting location: {}", unsafe { &*self.location }); 242 | self.location 243 | } 244 | 245 | // pub fn get(&self) -> &Value { 246 | // unsafe { &*self.value } 247 | // } 248 | // pub fn set(&mut self, value: Value) { 249 | // unsafe { *self.value = value }; 250 | // } 251 | } 252 | 253 | impl Display for UpValue { 254 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 255 | write!( 256 | f, 257 | "⬆️{}x{}@{}", 258 | unsafe { &*self.location }, 259 | self.closed, 260 | self.index 261 | ) 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /src/interpreter.rs: -------------------------------------------------------------------------------- 1 | use std::{borrow::Borrow, f32::consts::E}; 2 | 3 | use crate::{ 4 | environment::Environment, 5 | error::{ErrorTuple, ErrorTypes, Location, SiltError}, 6 | expression::Expression, 7 | function::{Function, ScopedFunction}, 8 | statement::Statement, 9 | token::Operator, 10 | value::Value, 11 | }; 12 | 13 | macro_rules! str_op_str{ 14 | ($left:ident $op:tt $right:ident $enu:ident )=>{ 15 | { 16 | if let Ok(n1) = $left.parse::() { 17 | if let Ok(n2) = $right.parse::() { 18 | return Ok(Value::Integer(n1 $op n2)); 19 | } 20 | if let Ok(n2) = $right.parse::() { 21 | return Ok(Value::Number(int2f!(n1) $op n2)); 22 | } 23 | } 24 | if let Ok(n1) = $left.parse::() { 25 | if let Ok(n2) = $right.parse::() { 26 | return Ok(Value::Number(n1 $op n2)); 27 | } 28 | } 29 | return Err(SiltError::ExpOpValueWithValue( 30 | ErrorTypes::String, 31 | Operator::$enu, 32 | ErrorTypes::String, 33 | )); 34 | } 35 | } 36 | } 37 | 38 | macro_rules! str_op_int{ 39 | ($left:ident $op:tt $right:ident $enu:ident)=>{ 40 | { 41 | if let Ok(n1) = $left.parse::() { 42 | return Ok(Value::Integer(n1 $op $right)); 43 | 44 | } 45 | if let Ok(n1) = $left.parse::() { 46 | return Ok(Value::Number(n1 $op intr2f!($right))); 47 | } 48 | return Err(SiltError::ExpOpValueWithValue( 49 | ErrorTypes::String, 50 | Operator::$enu, 51 | ErrorTypes::Integer, 52 | )); 53 | } 54 | } 55 | } 56 | 57 | macro_rules! int_op_str{ 58 | ($left:ident $op:tt $right:ident $enu:ident)=>{ 59 | { 60 | if let Ok(n1) = $right.parse::() { 61 | return Ok(Value::Integer($left $op n1)); 62 | 63 | } 64 | if let Ok(n1) = $right.parse::() { 65 | return Ok(Value::Number((intr2f!($left) $op n1))); 66 | } 67 | return Err(SiltError::ExpOpValueWithValue( 68 | ErrorTypes::Integer, 69 | Operator::$enu, 70 | ErrorTypes::String, 71 | )); 72 | } 73 | } 74 | } 75 | 76 | macro_rules! op_error { 77 | ($left:ident $op:ident $right:ident ) => {{ 78 | return Err(SiltError::ExpOpValueWithValue($left, Operator::$op, $right)); 79 | }}; 80 | } 81 | 82 | macro_rules! str_op_num{ 83 | ($left:ident $op:tt $right:ident $enu:ident)=>{ 84 | { 85 | if let Ok(n1) = $left.parse::() { 86 | return Ok(Value::Number(n1 $op $right)); 87 | } 88 | return Err(SiltError::ExpOpValueWithValue( 89 | ErrorTypes::String, 90 | Operator::$enu, 91 | ErrorTypes::String, 92 | )); 93 | } 94 | } 95 | } 96 | 97 | macro_rules! num_op_str{ 98 | ($left:ident $op:tt $right:ident )=>{ 99 | if let Ok(n1) = $right.parse::() { 100 | return Ok(Value::Number(left $op n1)); 101 | } 102 | return Err(SiltError::ExpAddValueWithValue( 103 | Value::Number($left), 104 | Value::String($right), 105 | )); 106 | } 107 | } 108 | /** Convert Integer to Float, lossy for now */ 109 | macro_rules! int2f { 110 | ($left:ident) => { 111 | $left as f64 112 | }; 113 | } 114 | 115 | macro_rules! intr2f { 116 | ($left:ident) => { 117 | *$left as f64 118 | }; 119 | } 120 | 121 | macro_rules! err_tuple { 122 | ($err:expr, $loc:expr ) => {{ 123 | Err(ErrorTuple { 124 | code: $err, 125 | location: $loc, 126 | }) 127 | }}; 128 | } 129 | 130 | /** Execute statements within an environment, cleans up return values */ 131 | pub fn execute(scope: &mut Environment, statements: &Vec) -> Result { 132 | match _execute(scope, statements) { 133 | Ok(v) 134 | | Err(ErrorTuple { 135 | code: SiltError::Return(v), 136 | location: _, 137 | }) => Ok(v), 138 | Err(e) => Err(e), 139 | } 140 | } 141 | 142 | /** private execution function */ 143 | fn _execute(scope: &mut Environment, statements: &Vec) -> Result { 144 | // let 145 | // let mut errors: Vec = vec![]; 146 | let mut res = Value::Nil; 147 | for s in statements { 148 | res = match s { 149 | Statement::Expression(exp) => evaluate(scope, exp)?, 150 | Statement::Declare { 151 | ident, 152 | local, 153 | expr: value, 154 | } => { 155 | let v = evaluate(scope, &value)?; 156 | 157 | scope.set(*ident, v, true, *local); 158 | Value::Nil 159 | } 160 | Statement::If { 161 | cond, 162 | then, 163 | else_cond, 164 | } => { 165 | // TODO IF should be scoped, but we don't have a way to scope elseif without it being double scoped from our dumb shortcut of nesting it in the else 166 | let cond = evaluate(scope, cond)?; 167 | if is_truthy(&cond) { 168 | _execute(scope, then)? 169 | } else if let Some(else_cond) = else_cond { 170 | _execute(scope, else_cond)? 171 | } else { 172 | Value::Nil 173 | } 174 | } 175 | Statement::While { cond, block } => { 176 | scope.new_scope(); 177 | while let Ok(cond) = evaluate(scope, cond) { 178 | if is_truthy(&cond) { 179 | _execute(scope, &block)?; 180 | } else { 181 | break; 182 | } 183 | } 184 | scope.pop_scope(); 185 | Value::Nil 186 | } 187 | Statement::NumericFor { 188 | ident, 189 | start, 190 | end, 191 | step, 192 | block, 193 | } => { 194 | let mut iterated = evaluate(scope, start)?; 195 | let end = evaluate(scope, end)?; 196 | 197 | let step = match step { 198 | Some(s) => evaluate(scope, s)?, 199 | None => Value::Integer(1), 200 | }; 201 | scope.new_scope(); 202 | scope.set_in_scope(*ident, iterated.clone()); 203 | 204 | while { 205 | match eval_binary(&iterated, &Operator::LessEqual, &end) { 206 | Ok(v) => { 207 | // if is_truthy(&v) { 208 | // true 209 | // } else { 210 | // false 211 | // } 212 | is_truthy(&v) 213 | } 214 | _ => false, 215 | } 216 | } { 217 | _execute(scope, block)?; 218 | 219 | iterated = match eval_binary(&iterated, &Operator::Add, &step) { 220 | Ok(v) => v, 221 | Err(e) => return err_tuple!(e, (0, 0)), // TODO location 222 | }; 223 | scope.set_in_scope(*ident, iterated.clone()); 224 | 225 | // println!("after start: {}, step: {}", iterated.clone(), step); 226 | // println!("start: {}, step: {}", iterated, step); 227 | } 228 | scope.pop_scope(); 229 | #[cfg(feature = "implicit-return")] 230 | { 231 | iterated 232 | } 233 | #[cfg(not(feature = "implicit-return"))] 234 | { 235 | Value::Nil 236 | } 237 | } 238 | 239 | Statement::Block(statements) => { 240 | scope.new_scope(); 241 | // let mut local = Environment::new(); 242 | // local.create_enclosing(scope); 243 | // execute(&mut local, statements); 244 | let v = _execute(scope, statements)?; 245 | scope.pop_scope(); 246 | // drop(local); 247 | v 248 | } 249 | Statement::Print(exp) => print_statement(scope, exp)?, 250 | Statement::InvalidStatement => return err_tuple!(SiltError::ExpInvalid, (0, 0)), 251 | Statement::Return(exp) => match evaluate(scope, exp) { 252 | Ok(v) => { 253 | return Err(ErrorTuple { 254 | code: SiltError::Return(v), 255 | location: (0, 0), 256 | }) 257 | } 258 | Err(e) => return Err(e), 259 | }, 260 | Statement::Skip => Value::Nil, 261 | // _ => Value::Nil, 262 | }; 263 | } 264 | 265 | #[cfg(not(feature = "implicit-return"))] 266 | { 267 | Ok(Value::Nil) 268 | } 269 | 270 | #[cfg(feature = "implicit-return")] 271 | { 272 | Ok(res) 273 | } 274 | } 275 | 276 | // fn execute_lock(scope: &mut Environment, statements: Vec) { 277 | // let upper = scope; 278 | // statements.for_each(|s| execute(scope)) 279 | // } 280 | 281 | // fn eval_wrap(global: &mut Environment, exp: &Expression) -> Option { 282 | // //-> Option { 283 | // if let Err(e) = evaluate(global, exp) { 284 | // return Some(e); 285 | // } 286 | // None 287 | // } 288 | 289 | fn print_statement(global: &mut Environment, exp: &Expression) -> Result { 290 | match evaluate(global, exp) { 291 | Ok(v) => { 292 | println!("> {}", v); 293 | Ok(Value::Nil) 294 | } 295 | Err(e) => Err(e), 296 | } 297 | } 298 | 299 | pub fn evaluate(global: &mut Environment, exp: &Expression) -> Result { 300 | let v: Value = match exp { 301 | Expression::Literal { value, location } => value.clone(), 302 | Expression::Binary { 303 | left, 304 | operator, 305 | right, 306 | location, 307 | } => { 308 | let left = evaluate(global, left)?; 309 | let right = evaluate(global, right)?; 310 | match eval_binary(&left, operator, &right) { 311 | Ok(v) => v, 312 | Err(e) => return err_tuple!(e, *location), 313 | } 314 | } 315 | Expression::Logical { 316 | left, 317 | operator, 318 | right, 319 | location, 320 | } => { 321 | let left = evaluate(global, left)?; 322 | // let right = evaluate(global, *right)?; 323 | match operator { 324 | &Operator::Or => { 325 | if is_truthy(&left) { 326 | left 327 | } else { 328 | evaluate(global, right)? 329 | } 330 | } 331 | &Operator::And => { 332 | if !is_truthy(&left) { 333 | left 334 | } else { 335 | evaluate(global, right)? 336 | } 337 | } 338 | _ => return err_tuple!(SiltError::ExpInvalidOperator(operator.clone()), *location), // impossible? 339 | } 340 | } 341 | Expression::Unary { 342 | operator, 343 | right, 344 | location, 345 | } => { 346 | let right = evaluate(global, right)?; 347 | match operator { 348 | Operator::Sub => match right { 349 | Value::Number(n) => Value::Number(-n), 350 | Value::Integer(i) => Value::Integer(-i), 351 | v => { 352 | return err_tuple!(SiltError::ExpInvalidNegation(v.to_error()), *location); 353 | } 354 | }, 355 | Operator::Not => Value::Bool(!is_truthy(&right)), 356 | Operator::Tilde => match right { 357 | Value::Integer(i) => Value::Integer(!i), 358 | Value::Number(n) => { 359 | if n.fract() == 0.0 { 360 | Value::Integer(!(n as i64)) 361 | } else { 362 | return err_tuple!( 363 | SiltError::EvalNoInteger(right.to_error()), 364 | *location 365 | ); 366 | } 367 | } 368 | v => return err_tuple!(SiltError::ExpInvalidBitwise(v.to_error()), *location), 369 | }, 370 | _ => return err_tuple!(SiltError::ExpInvalidOperator(operator.clone()), *location), 371 | } 372 | } 373 | Expression::GroupingExpression { 374 | expression, 375 | location, 376 | } => todo!(), 377 | Expression::Variable { ident, location } => { 378 | // lookup_variable(global, *ident, *location)? 379 | let v = global.get(&ident); 380 | v 381 | // match v { 382 | // Value::Number(n) => Value::Number(*n), 383 | // Value::Integer(i) => Value::Integer(*i), 384 | // Value::Bool(b) => Value::Bool(*b), 385 | // Value::String(s) => Value::String(s.clone()), 386 | // Value::Nil => Value::Nil, 387 | // Value::Infinity(f) => Value::Infinity(*f), 388 | // Value::NativeFunction(f) => Value::NativeFunction(*f), 389 | // Value::Function(f) => Value::Function(f.clone()), 390 | // } 391 | } 392 | // Expression::AssignmentExpression { name, value } => todo!(), 393 | // Expression::EndOfFile => todo!(), 394 | Expression::InvalidExpression => todo!(), 395 | Expression::Assign { 396 | ident, 397 | value, 398 | location, 399 | } => { 400 | let val = evaluate(global, value)?; 401 | global.assign_local(*ident, val); 402 | Value::Nil 403 | } 404 | Expression::Function { value, location } => { 405 | let scoped = ScopedFunction::new(global.get_current_scope(), value.clone()); 406 | Value::Function(std::rc::Rc::new(scoped)) 407 | } 408 | 409 | Expression::Call { 410 | callee, 411 | args, 412 | location, 413 | } => { 414 | let callee = evaluate(global, callee)?; 415 | let strict = global.is_strict(); 416 | 417 | let mut args = args 418 | .iter() 419 | .map(|a| evaluate(global, a)) 420 | .collect::, ErrorTuple>>()?; 421 | 422 | if strict { 423 | // TODO args.len() == function.arity 424 | } 425 | match callee { 426 | Value::Function(f) => { 427 | let temp_scope = global.swap_scope(&f.scope); 428 | global.new_scope(); 429 | let ff: &ScopedFunction = f.borrow(); 430 | // ff.call(global, args); 431 | ff.func.params.iter().enumerate().for_each(|(i, param)| { 432 | global.set_in_scope( 433 | *param, 434 | match args.get(i) { 435 | Some(v) => v.clone(), 436 | None => Value::Nil, 437 | }, 438 | ); 439 | }); 440 | match _execute(global, &ff.func.body) { 441 | Ok(v) 442 | | Err(ErrorTuple { 443 | code: SiltError::Return(v), 444 | location: _, 445 | }) => { 446 | // we can accept special error return type and pass normally 447 | global.pop_scope(); 448 | global.replace_scope(temp_scope); 449 | v 450 | } 451 | Err(e) => { 452 | return Err(e); 453 | } 454 | } 455 | } 456 | Value::NativeFunction(f) => f(global, args), 457 | _ => { 458 | return err_tuple!(SiltError::NotCallable(callee.to_string()), *location); 459 | } 460 | } 461 | } 462 | }; 463 | 464 | Ok(v) 465 | } 466 | 467 | pub fn eval_binary(left: &Value, operator: &Operator, right: &Value) -> Result { 468 | let val = match (&left, &right) { 469 | (Value::Number(l), Value::Number(r)) => match operator { 470 | Operator::Add => Value::Number(l + r), 471 | Operator::Sub => Value::Number(l - r), 472 | Operator::Multiply => Value::Number(l * r), 473 | Operator::Divide => Value::Number(l / r), 474 | Operator::Modulus => Value::Number(l % r), 475 | Operator::Equal => Value::Bool(l == r), 476 | Operator::NotEqual => Value::Bool(l != r), 477 | Operator::Less => Value::Bool(l < r), 478 | Operator::LessEqual => Value::Bool(l <= r), 479 | Operator::Greater => Value::Bool(l > r), 480 | Operator::GreaterEqual => Value::Bool(l >= r), 481 | Operator::Not => return Err(SiltError::ExpInvalidOperator(operator.clone())), 482 | // Operator::And => logical_and(left, right), 483 | // Operator::Or => logical_or(left, right), 484 | Operator::FloorDivide => Value::Number((l / r).floor()), 485 | Operator::Exponent => Value::Number(l.powf(*r)), 486 | Operator::Concat => Value::String(Box::new(l.to_string() + &r.to_string())), 487 | Operator::Tilde => todo!(), 488 | _ => return Err(SiltError::ExpInvalidOperator(operator.clone())), 489 | }, 490 | (Value::Integer(l), Value::Integer(r)) => match operator { 491 | Operator::Add => Value::Integer(l + r), 492 | Operator::Sub => Value::Integer(l - r), 493 | Operator::Multiply => Value::Integer(l * r), 494 | Operator::Divide | Operator::FloorDivide => Value::Integer(l / r), 495 | Operator::Modulus => Value::Integer(l % r), 496 | 497 | // Operator::And => logical_and(left, right), 498 | // Operator::Or => logical_or(left, right), 499 | Operator::Exponent => int_exp(*l, *r), 500 | Operator::Equal => Value::Bool(l == r), 501 | Operator::NotEqual => Value::Bool(l != r), 502 | Operator::Less => Value::Bool(l < r), 503 | Operator::LessEqual => Value::Bool(l <= r), 504 | Operator::Greater => Value::Bool(l > r), 505 | Operator::GreaterEqual => Value::Bool(l >= r), 506 | Operator::Concat => Value::String(Box::new(l.to_string() + &r.to_string())), 507 | Operator::Not | Operator::And | Operator::Or => { 508 | return Err(SiltError::ExpInvalidOperator(operator.clone())) 509 | } 510 | Operator::Tilde => todo!(), 511 | }, 512 | (Value::Number(l), Value::Integer(r)) => match operator { 513 | Operator::Add => Value::Number(l + intr2f!(r)), 514 | Operator::Sub => Value::Number(l - intr2f!(r)), 515 | Operator::Multiply => Value::Number(l * intr2f!(r)), 516 | Operator::Divide => Value::Number(l / intr2f!(r)), 517 | Operator::FloorDivide => Value::Number((l / intr2f!(r)).floor()), 518 | Operator::Modulus => Value::Number(l % intr2f!(r)), 519 | Operator::Exponent => Value::Number(l.powf(intr2f!(r))), 520 | // Operator::And => logical_and(left, right), 521 | // Operator::Or => logical_or(left, right), 522 | Operator::Equal => Value::Bool(*l == intr2f!(r)), 523 | Operator::NotEqual => Value::Bool(*l != intr2f!(r)), 524 | Operator::Less => Value::Bool(*l < intr2f!(r)), 525 | Operator::LessEqual => Value::Bool(*l <= intr2f!(r)), 526 | Operator::Greater => Value::Bool(*l > intr2f!(r)), 527 | Operator::GreaterEqual => Value::Bool(*l >= intr2f!(r)), 528 | Operator::Concat => Value::String(Box::new(l.to_string() + &r.to_string())), 529 | Operator::Not | Operator::And | Operator::Or => { 530 | return Err(SiltError::ExpInvalidOperator(operator.clone())) 531 | } 532 | Operator::Tilde => todo!(), 533 | }, 534 | (Value::Integer(l), Value::Number(r)) => match operator { 535 | Operator::Add => Value::Number(intr2f!(l) + r), 536 | Operator::Sub => Value::Number(intr2f!(l) - r), 537 | Operator::Multiply => Value::Number(intr2f!(l) * r), 538 | Operator::Divide => Value::Number(intr2f!(l) / r), 539 | Operator::FloorDivide => Value::Number((intr2f!(l) / r).floor()), 540 | Operator::Modulus => Value::Number(intr2f!(l) % r), 541 | Operator::Equal => Value::Bool(intr2f!(l) == *r), 542 | Operator::NotEqual => Value::Bool(intr2f!(l) != *r), 543 | Operator::Less => Value::Bool(intr2f!(l) < *r), 544 | Operator::LessEqual => Value::Bool(intr2f!(l) <= *r), 545 | Operator::Greater => Value::Bool(intr2f!(l) > *r), 546 | Operator::GreaterEqual => Value::Bool(intr2f!(l) >= *r), 547 | // Operator::And => logical_and(left, right), 548 | // Operator::Or => logical_or(left, right), 549 | Operator::Exponent => Value::Number(intr2f!(l).powf(*r)), 550 | Operator::Concat => Value::String(Box::new(l.to_string() + &r.to_string())), 551 | Operator::Not | Operator::And | Operator::Or => { 552 | return Err(SiltError::ExpInvalidOperator(operator.clone())) 553 | } 554 | Operator::Tilde => todo!(), 555 | }, 556 | (Value::String(l), Value::String(r)) => { 557 | match operator { 558 | Operator::Add => { 559 | str_op_str!(l + r Add); 560 | } 561 | Operator::Sub => { 562 | str_op_str!(l - r Sub); 563 | } 564 | Operator::Multiply => { 565 | str_op_str!(l * r Multiply); 566 | } 567 | Operator::Divide => { 568 | // always float 569 | if let Ok(n1) = l.parse::() { 570 | if let Ok(n2) = r.parse::() { 571 | return Ok(Value::Number(n1 / n2)); 572 | } 573 | } 574 | return Err(SiltError::ExpOpValueWithValue( 575 | ErrorTypes::String, 576 | Operator::Divide, 577 | ErrorTypes::String, 578 | )); 579 | } 580 | Operator::FloorDivide => { 581 | if let Ok(n1) = l.parse::() { 582 | if let Ok(n2) = r.parse::() { 583 | return Ok(Value::Integer(n1 / n2)); 584 | } 585 | if let Ok(n2) = r.parse::() { 586 | return Ok(Value::Number((int2f!(n1) / n2).floor())); 587 | } 588 | } 589 | if let Ok(n1) = l.parse::() { 590 | if let Ok(n2) = r.parse::() { 591 | return Ok(Value::Number((n1 / n2).floor())); 592 | } 593 | } 594 | return Err(SiltError::ExpOpValueWithValue( 595 | ErrorTypes::String, 596 | Operator::FloorDivide, 597 | ErrorTypes::String, 598 | )); 599 | } 600 | Operator::Equal => Value::Bool(l == r), 601 | Operator::NotEqual => Value::Bool(l != r), 602 | Operator::Less => Value::Bool(l < r), 603 | Operator::LessEqual => Value::Bool(l <= r), 604 | Operator::Greater => Value::Bool(l > r), 605 | Operator::GreaterEqual => Value::Bool(l >= r), 606 | 607 | // Operator::And => logical_and(left, right), 608 | // Operator::Or => logical_or(left, right), 609 | Operator::Modulus => str_op_str!(l % r Modulus), 610 | Operator::Exponent => { 611 | if let Ok(n1) = l.parse::() { 612 | if let Ok(n2) = r.parse::() { 613 | return Ok(int_exp(n1, n2)); 614 | } 615 | if let Ok(n2) = r.parse::() { 616 | return Ok(Value::Number(int2f!(n1).powf(n2))); 617 | } 618 | } 619 | if let Ok(n1) = l.parse::() { 620 | if let Ok(n2) = r.parse::() { 621 | return Ok(Value::Number(n1.powf(n2))); 622 | } 623 | } 624 | return Err(SiltError::ExpOpValueWithValue( 625 | ErrorTypes::String, 626 | Operator::Exponent, 627 | ErrorTypes::String, 628 | )); 629 | } 630 | Operator::Concat => { 631 | if let Value::String(ll) = left { 632 | Value::String(Box::new((**ll).to_owned() + &**r)) 633 | } else { 634 | Value::Nil 635 | } 636 | } 637 | Operator::Tilde => return Err(SiltError::ExpInvalidBitwise(ErrorTypes::String)), 638 | Operator::Not | Operator::And | Operator::Or => { 639 | return Err(SiltError::ExpInvalidOperator(operator.clone())) 640 | } 641 | } 642 | } 643 | (Value::String(l), Value::Number(r)) => match operator { 644 | Operator::Add => { 645 | str_op_num!(l + r Add); 646 | } 647 | Operator::Sub => { 648 | str_op_num!(l - r Sub); 649 | } 650 | 651 | Operator::Multiply => { 652 | str_op_num!(l * r Multiply); 653 | } 654 | Operator::Divide => { 655 | str_op_num!(l / r Divide); 656 | } 657 | // Operator::And => logical_and(left, right), 658 | // Operator::Or => logical_or(left, right), 659 | Operator::FloorDivide => { 660 | if let Ok(n1) = l.parse::() { 661 | Value::Number(n1.powf(*r)) 662 | } else { 663 | return Err(SiltError::ExpOpValueWithValue( 664 | ErrorTypes::String, 665 | Operator::FloorDivide, 666 | ErrorTypes::Number, 667 | )); 668 | } 669 | } 670 | Operator::Modulus => str_op_num!(l % r Modulus), 671 | Operator::Exponent => { 672 | if let Ok(n1) = l.parse::() { 673 | return Ok(Value::Number(n1.powf(*r))); 674 | } 675 | return Err(SiltError::ExpOpValueWithValue( 676 | ErrorTypes::String, 677 | Operator::Exponent, 678 | ErrorTypes::Number, 679 | )); 680 | } 681 | Operator::Concat => { 682 | if let Value::String(ll) = left { 683 | Value::String(((**ll).to_owned() + &r.to_string()).into()) 684 | } else { 685 | Value::Nil 686 | } 687 | } 688 | Operator::Equal => Value::Bool(**l == r.to_string()), 689 | Operator::NotEqual => Value::Bool(**l != r.to_string()), 690 | op @ (Operator::Less 691 | | Operator::LessEqual 692 | | Operator::Greater 693 | | Operator::GreaterEqual) => { 694 | return Err(SiltError::ExpOpValueWithValue( 695 | ErrorTypes::String, 696 | op.clone(), 697 | ErrorTypes::Number, 698 | )); 699 | } 700 | Operator::Tilde => return Err(SiltError::ExpInvalidBitwise(ErrorTypes::String)), 701 | Operator::Not | Operator::And | Operator::Or => { 702 | return Err(SiltError::ExpInvalidOperator(operator.clone())) 703 | } 704 | }, 705 | (Value::String(l), Value::Integer(r)) => match operator { 706 | Operator::Add => str_op_int!(l + r Add), 707 | Operator::Sub => str_op_int!(l - r Sub), 708 | Operator::Multiply => str_op_int!(l * r Multiply), 709 | Operator::Divide => str_op_int!(l / r Divide), 710 | // Operator::And => logical_and(left, right), 711 | // Operator::Or => logical_or(left, right), 712 | Operator::FloorDivide => { 713 | if let Ok(n1) = l.parse::() { 714 | return Ok(Value::Integer(n1 / r)); 715 | } 716 | if let Ok(n1) = l.parse::() { 717 | return Ok(Value::Number((n1 / intr2f!(r)).floor())); 718 | } 719 | return Err(SiltError::ExpOpValueWithValue( 720 | ErrorTypes::String, 721 | Operator::FloorDivide, 722 | ErrorTypes::Integer, 723 | )); 724 | } 725 | Operator::Modulus => str_op_int!(l % r Modulus), 726 | Operator::Exponent => { 727 | if let Ok(n1) = l.parse::() { 728 | return Ok(int_exp(n1, *r)); 729 | } 730 | if let Ok(n1) = l.parse::() { 731 | return Ok(Value::Number(n1.powf(intr2f!(r)))); 732 | } 733 | return Err(SiltError::ExpOpValueWithValue( 734 | ErrorTypes::String, 735 | Operator::Exponent, 736 | ErrorTypes::Integer, 737 | )); 738 | } 739 | Operator::Concat => { 740 | if let Value::String(ll) = left { 741 | Value::String(((**ll).to_owned() + &r.to_string()).into()) 742 | } else { 743 | Value::Nil 744 | } 745 | } 746 | Operator::Equal => Value::Bool(**l == r.to_string()), 747 | Operator::NotEqual => Value::Bool(**l != r.to_string()), 748 | op @ (Operator::Less 749 | | Operator::LessEqual 750 | | Operator::Greater 751 | | Operator::GreaterEqual) => { 752 | return Err(SiltError::ExpOpValueWithValue( 753 | ErrorTypes::String, 754 | op.clone(), 755 | ErrorTypes::Integer, 756 | )); 757 | } 758 | Operator::Tilde => return Err(SiltError::ExpInvalidBitwise(ErrorTypes::String)), 759 | Operator::Not | Operator::And | Operator::Or => { 760 | return Err(SiltError::ExpInvalidOperator(operator.clone())) 761 | } 762 | }, 763 | (Value::Integer(l), Value::String(r)) => match operator { 764 | Operator::Add => int_op_str!(l + r Add), 765 | Operator::Sub => int_op_str!(l - r Sub), 766 | Operator::Multiply => int_op_str!(l * r Multiply), 767 | Operator::Divide => int_op_str!(l / r Divide), 768 | // Operator::And => logical_and(left, right), 769 | // Operator::Or => logical_or(left, right), 770 | Operator::FloorDivide => { 771 | if let Ok(n1) = r.parse::() { 772 | return Ok(Value::Integer(l / n1)); 773 | } 774 | if let Ok(n1) = r.parse::() { 775 | return Ok(Value::Number((intr2f!(l) / n1).floor())); 776 | } 777 | return Err(SiltError::ExpOpValueWithValue( 778 | ErrorTypes::Integer, 779 | Operator::FloorDivide, 780 | ErrorTypes::String, 781 | )); 782 | } 783 | Operator::Modulus => int_op_str!(l % r Modulus), 784 | Operator::Exponent => { 785 | if let Ok(n1) = r.parse::() { 786 | return Ok(int_exp(*l, n1)); 787 | } 788 | if let Ok(n1) = r.parse::() { 789 | return Ok(Value::Number(intr2f!(l).powf(n1))); 790 | } 791 | return Err(SiltError::ExpOpValueWithValue( 792 | ErrorTypes::Integer, 793 | Operator::Exponent, 794 | ErrorTypes::String, 795 | )); 796 | } 797 | Operator::Concat => Value::String((l.to_string() + &r).into()), 798 | Operator::Equal => Value::Bool(l.to_string() == **r), 799 | Operator::NotEqual => Value::Bool(l.to_string() != **r), 800 | op @ (Operator::Less 801 | | Operator::LessEqual 802 | | Operator::Greater 803 | | Operator::GreaterEqual) => { 804 | return Err(SiltError::ExpOpValueWithValue( 805 | ErrorTypes::Integer, 806 | op.clone(), 807 | ErrorTypes::String, 808 | )); 809 | } 810 | Operator::Tilde => return Err(SiltError::ExpInvalidBitwise(ErrorTypes::String)), 811 | Operator::Not | Operator::And | Operator::Or => { 812 | return Err(SiltError::ExpInvalidOperator(operator.clone())) 813 | } 814 | }, 815 | (Value::Integer(_), Value::Bool(_)) 816 | | (Value::Number(_), Value::Bool(_)) 817 | | (Value::Bool(_), Value::Integer(_)) 818 | | (Value::Bool(_), Value::Number(_)) 819 | | (Value::Bool(_), Value::String(_)) 820 | | (Value::String(_), Value::Bool(_)) 821 | | (Value::Bool(_), Value::Nil) 822 | | (Value::Nil, Value::Bool(_)) => match operator { 823 | Operator::Equal => Value::Bool(false), 824 | Operator::NotEqual => Value::Bool(true), 825 | // Operator::And => logical_and(left, right), 826 | // Operator::Or => logical_or(left, right), 827 | op => { 828 | return Err(SiltError::ExpOpValueWithValue( 829 | left.to_error(), 830 | op.clone(), 831 | right.to_error(), 832 | )) 833 | } 834 | }, 835 | 836 | (Value::Integer(_) | Value::Number(_) | Value::String(_), Value::Nil) => match operator { 837 | Operator::Equal => Value::Bool(false), 838 | Operator::NotEqual => Value::Bool(true), 839 | // Operator::Or => left, 840 | // Operator::And => right, 841 | op => { 842 | return Err(SiltError::ExpOpValueWithValue( 843 | left.to_error(), 844 | op.clone(), 845 | right.to_error(), 846 | )) 847 | } 848 | }, 849 | (Value::Nil, Value::Integer(_) | Value::Number(_) | Value::String(_)) => match operator { 850 | Operator::Equal => Value::Bool(false), 851 | Operator::NotEqual => Value::Bool(true), 852 | // Operator::Or => right, 853 | // Operator::And => left, 854 | op => { 855 | return Err(SiltError::ExpOpValueWithValue( 856 | left.to_error(), 857 | op.clone(), 858 | right.to_error(), 859 | )) 860 | } 861 | }, 862 | 863 | (Value::Number(_), Value::String(_)) => todo!(), 864 | (Value::Bool(l), Value::Bool(r)) => match operator { 865 | Operator::Equal => Value::Bool(l == r), 866 | Operator::NotEqual => Value::Bool(l != r), 867 | // Operator::And => logical_and(left, right), 868 | // Operator::Or => logical_or(left, right), 869 | op => { 870 | return Err(SiltError::ExpOpValueWithValue( 871 | left.to_error(), 872 | op.clone(), 873 | right.to_error(), 874 | )) 875 | } 876 | }, 877 | (Value::Nil, Value::Nil) => match operator { 878 | Operator::Equal => Value::Bool(true), 879 | Operator::NotEqual => Value::Bool(false), 880 | // Operator::And => Value::Nil, 881 | // Operator::Or => Value::Nil, 882 | op => { 883 | return Err(SiltError::ExpOpValueWithValue( 884 | ErrorTypes::Nil, 885 | op.clone(), 886 | ErrorTypes::Nil, 887 | )) 888 | } 889 | }, 890 | (Value::Integer(_), Value::Infinity(_)) => todo!(), 891 | (Value::Number(_), Value::Infinity(_)) => todo!(), 892 | (Value::Bool(_), Value::Infinity(_)) => todo!(), 893 | (Value::Nil, Value::Infinity(_)) => todo!(), 894 | (Value::Infinity(_), Value::Integer(_)) => todo!(), 895 | (Value::Infinity(_), Value::Number(_)) => todo!(), 896 | (Value::Infinity(_), Value::Bool(_)) => todo!(), 897 | (Value::Infinity(_), Value::Nil) => todo!(), 898 | (Value::Infinity(_), Value::Infinity(_)) => todo!(), 899 | (Value::Infinity(_), Value::String(_)) => todo!(), 900 | (Value::String(_), Value::Infinity(_)) => todo!(), 901 | (Value::NativeFunction(_), _) 902 | | (_, Value::NativeFunction(_)) 903 | | (Value::Function(_), _) 904 | | (_, Value::Function(_)) => return Err(SiltError::ExpInvalidOperator(operator.clone())), // _=>Value::Nil, 905 | (_, _) => todo!(), 906 | }; 907 | Ok(val) 908 | } 909 | 910 | fn is_truthy(v: &Value) -> bool { 911 | match v { 912 | Value::Bool(b) => *b, 913 | Value::Nil => false, 914 | _ => true, 915 | } 916 | } 917 | /** If 1st truthy return 2nd param, if 1st falsey return 1st param*/ 918 | fn logical_and(left: Value, right: Value) -> Value { 919 | if is_truthy(&left) { 920 | return right; 921 | } 922 | left 923 | } 924 | 925 | fn logical_or(left: Value, right: Value) -> Value { 926 | if is_truthy(&left) { 927 | return left; 928 | } 929 | right 930 | } 931 | 932 | fn int_exp(left: i64, right: i64) -> Value { 933 | if right < 0 { 934 | Value::Number(int2f!(left).powf(int2f!(right))) 935 | } else { 936 | match left.checked_pow(right as u32) { 937 | Some(n) => Value::Integer(n), 938 | None => Value::Integer(core::i64::MAX), 939 | } 940 | } 941 | } 942 | 943 | fn coerce_num(s: &str) -> Result { 944 | if let Ok(n) = s.parse::() { 945 | return Ok(Value::Integer(n)); 946 | } 947 | if let Ok(n) = s.parse::() { 948 | return Ok(Value::Number(n)); 949 | } 950 | Err(SiltError::InvalidNumber(s.to_owned())) 951 | } 952 | -------------------------------------------------------------------------------- /src/lexer.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | error::{ErrorTuple, Location, SiltError}, 3 | token::{Flag, Operator, Token}, 4 | }; 5 | 6 | enum Mode { 7 | Normal, 8 | Flag, 9 | Typer, 10 | // LookAhead, 11 | } 12 | pub struct Lexer { 13 | pub source: String, 14 | pub iterator: std::iter::Peekable>, 15 | pub start: usize, 16 | pub end: usize, 17 | pub current: usize, 18 | pub line: usize, 19 | pub column: usize, 20 | pub column_start: usize, 21 | mode: Mode, 22 | // ahead_buffer: Vec, 23 | } 24 | 25 | pub type TokenTuple = (Token, Location); 26 | pub type TokenResult = Result; 27 | pub type TokenOption = Option; 28 | 29 | impl Iterator for Lexer { 30 | type Item = TokenResult; 31 | 32 | fn next(&mut self) -> Option { 33 | if self.current >= self.end { 34 | return None; 35 | } 36 | match self.mode { 37 | Mode::Normal => self.step(), 38 | Mode::Flag => match self.get_flag() { 39 | Some(r) => Some(r), 40 | None => { 41 | self.mode = Mode::Normal; 42 | self.step() 43 | } 44 | }, 45 | // Mode::LookAhead => { 46 | // if self.ahead_buffer.is_empty() { 47 | // self.mode = Mode::Normal; 48 | // self.step() 49 | // } else { 50 | // self.ahead_buffer.remove(0) 51 | // } 52 | // } 53 | Mode::Typer => self.colon_blow(), 54 | } 55 | } 56 | } 57 | 58 | impl<'a> Lexer { 59 | pub fn new(source: String) -> Self { 60 | let st = source.clone(); 61 | let len = st.len(); 62 | // TODO is this insane? 63 | let chars = source.chars().collect::>().into_iter().peekable(); 64 | Lexer { 65 | source: st, 66 | start: 0, 67 | column: 0, 68 | column_start: 0, 69 | current: 0, 70 | end: len, 71 | line: 1, 72 | iterator: chars, 73 | mode: Mode::Normal, 74 | // ahead_buffer: Vec::new(), 75 | } 76 | } 77 | 78 | fn set_start(&mut self) { 79 | self.start = self.current; 80 | self.column_start = self.column; 81 | } 82 | 83 | fn eat(&mut self) { 84 | self.current += 1; 85 | self.column += 1; 86 | self.iterator.next(); 87 | } 88 | 89 | fn eat_out(&mut self) -> Option { 90 | self.current += 1; 91 | self.column += 1; 92 | self.iterator.next() 93 | } 94 | 95 | fn peek(&mut self) -> Option<&char> { 96 | self.iterator.peek() 97 | } 98 | 99 | fn _error(&mut self, code: SiltError) -> TokenResult { 100 | Err(ErrorTuple { 101 | code, 102 | location: (self.line, self.start), 103 | }) 104 | } 105 | 106 | fn error(&mut self, code: SiltError) -> TokenOption { 107 | Some(self._error(code)) 108 | } 109 | 110 | // pub fn get_errors(&mut self) -> Vec { 111 | // self.error_list.drain(..).collect() 112 | // } 113 | 114 | fn _send(&mut self, token: Token) -> TokenResult { 115 | Ok((token, (self.line, self.column_start + 1))) 116 | } 117 | 118 | fn send(&mut self, token: Token) -> TokenOption { 119 | // self.tokens.push(token); 120 | // self.locations.push((self.line, self.column_start + 1)); // add 1 because column_start is 0-indexed 121 | Some(self._send(token)) 122 | } 123 | 124 | fn eat_send(&mut self, token: Token) -> TokenOption { 125 | self.eat(); 126 | self.send(token) 127 | } 128 | 129 | fn eat_eat_send(&mut self, token: Token) -> TokenOption { 130 | self.eat(); 131 | self.eat(); 132 | self.send(token) 133 | } 134 | 135 | // fn maybe_add(&mut self, token: Option) { 136 | // if let Some(t) = token { 137 | // self.tokens.push(t); 138 | // self.locations.push((self.line, self.column)); 139 | // } 140 | // } 141 | 142 | fn new_line(&mut self) { 143 | self.line += 1; 144 | self.column = 0; 145 | } 146 | 147 | fn get_sofar(&self) -> String { 148 | self.source[self.start..self.current].to_string() 149 | } 150 | 151 | fn number(&mut self, prefix_dot: bool) -> TokenOption { 152 | if prefix_dot { 153 | self.start = self.current - 1; 154 | self.column_start = self.column - 1; 155 | } else { 156 | self.set_start(); 157 | } 158 | self.eat(); 159 | let mut is_float = prefix_dot; 160 | let mut strip = false; 161 | while self.current < self.end { 162 | match self.peek() { 163 | Some(c) => match c { 164 | '0'..='9' => { 165 | self.eat(); 166 | } 167 | #[cfg(feature = "under-number")] 168 | '_' => { 169 | self.eat(); 170 | strip = true; 171 | } 172 | '.' => { 173 | if is_float { 174 | return self.error(SiltError::InvalidNumber(self.get_sofar())); 175 | } 176 | is_float = true; 177 | self.eat(); 178 | } 179 | 'a'..='z' | 'A'..='Z' | '_' => { 180 | return self.error(SiltError::InvalidNumber(self.get_sofar())); 181 | } 182 | _ => break, 183 | }, 184 | None => break, 185 | } 186 | } 187 | let cc = &self.source[self.start..self.current]; 188 | if is_float { 189 | let n = match if strip { 190 | cc.replace("_", "").parse::() 191 | } else { 192 | cc.parse::() 193 | } { 194 | Ok(n) => n, 195 | Err(_) => { 196 | return self.error(SiltError::NotANumber(cc.to_string())); 197 | } 198 | }; 199 | self.send(Token::Number(n)) 200 | } else { 201 | let n = match if strip { 202 | cc.replace("_", "").parse::() 203 | } else { 204 | cc.parse::() 205 | } { 206 | Ok(n) => n, 207 | Err(_) => { 208 | return self.error(SiltError::NotANumber(cc.to_string())); 209 | } 210 | }; 211 | return self.send(Token::Integer(n)); 212 | } 213 | } 214 | 215 | fn string(&mut self, apos: bool) -> TokenOption { 216 | // start column at '"' not within string starter 217 | self.column_start = self.column; 218 | self.eat(); 219 | self.start = self.current; 220 | while self.current < self.end { 221 | match self.peek() { 222 | Some(c) => match c { 223 | '\n' => { 224 | return self.error(SiltError::UnterminatedString); 225 | } 226 | '\'' => { 227 | self.eat(); 228 | if apos { 229 | break; 230 | } 231 | } 232 | '"' => { 233 | self.eat(); 234 | if !apos { 235 | break; 236 | } 237 | } 238 | _ => { 239 | self.eat(); 240 | } 241 | }, 242 | None => { 243 | return self.error(SiltError::UnterminatedString); 244 | } 245 | } 246 | } 247 | let cc = self.source[self.start..self.current - 1].to_string(); 248 | self.send(Token::StringLiteral(cc.into_boxed_str())) 249 | } 250 | 251 | fn multi_line_string(&mut self) -> TokenOption { 252 | self.column_start = self.column; 253 | self.eat(); 254 | self.start = self.current; 255 | while self.current < self.end { 256 | let char = self.peek(); 257 | match char { 258 | Some(c) => match c { 259 | '\n' => { 260 | self.new_line(); 261 | self.eat(); 262 | } 263 | ']' => { 264 | self.eat(); 265 | if self.peek() == Some(&']') { 266 | self.eat(); 267 | break; 268 | } 269 | } 270 | _ => { 271 | self.eat(); 272 | } 273 | }, 274 | None => { 275 | return self.error(SiltError::UnterminatedString); 276 | } 277 | } 278 | } 279 | let cc = self.source[self.start..self.current - 2].to_string(); 280 | self.send(Token::StringLiteral(cc.into_boxed_str())) 281 | } 282 | 283 | fn word_eater(&mut self) { 284 | while self.current < self.end { 285 | match self.peek() { 286 | Some(c) => match c { 287 | 'a'..='z' | 'A'..='Z' | '0'..='9' | '_' => self.eat(), 288 | _ => break, 289 | }, 290 | None => break, 291 | } 292 | } 293 | } 294 | 295 | fn get_flag(&mut self) -> TokenOption { 296 | // let mut words = vec![]; 297 | self.mode = Mode::Flag; 298 | self.set_start(); 299 | while self.current < self.end { 300 | match self.eat_out() { 301 | Some(c) => match c { 302 | 'a'..='z' | 'A'..='Z' | '0'..='9' | '_' => self.eat(), 303 | '\n' => { 304 | self.new_line(); 305 | self.mode = Mode::Normal; 306 | break; 307 | } 308 | _ => { 309 | let cc = &self.source[self.start..self.current]; 310 | match cc.to_lowercase().as_str() { 311 | "strict" => return self.send(Token::Flag(Flag::Strict)), 312 | "local" => return self.send(Token::Flag(Flag::Local)), 313 | _ => {} 314 | } 315 | // words.push(cc.to_string()); 316 | self.set_start(); 317 | } 318 | }, 319 | None => break, 320 | } 321 | } 322 | None 323 | } 324 | 325 | /** Follow up a colon to determine if an identifer is listed, this may be either a typing or a method determined by context */ 326 | fn colon_blow(&mut self) -> TokenOption { 327 | while let Some(' ' | '\r' | '\t') = self.peek() { 328 | self.eat(); 329 | } 330 | 331 | self.set_start(); 332 | self.word_eater(); 333 | self.mode = Mode::Normal; 334 | if self.start == self.current { 335 | return self.error(SiltError::ExpectedLocalIdentifier); 336 | } 337 | let cc = &self.source[self.start..self.current]; 338 | self.send(Token::ColonIdentifier(cc.into())) 339 | } 340 | 341 | // fn look_ahead(&mut self, token: Token) -> TokenOption { 342 | // match token{ 343 | // Token::Identifier(_)=>{ 344 | // match self.step(){ 345 | // Some(Token::ArrowFunction)=>{ 346 | // self.mode = Mode::LookAhead; 347 | // self.eat(); 348 | // self.ahead_buffer.push(self.send(token)); 349 | // return self.send(Token::ArrowFunction); 350 | // } 351 | // } 352 | // } 353 | // Token::OpenParen 354 | // }else{ 355 | 356 | // } 357 | // while self.current < self.end { 358 | // match self.step() { 359 | // Some(Ok((tuple))) => { 360 | // if let Token::Identifier(_) | Token::Comma= tuple{ 361 | // self.look_ahead( Some(Ok(tuple))); 362 | // }else{ 363 | // break; 364 | // } 365 | // // continue 366 | 367 | // } 368 | // } 369 | // self.send(token) 370 | // } 371 | 372 | pub fn step(&mut self) -> TokenOption { 373 | while self.current < self.end { 374 | let char = match self.peek() { 375 | Some(c) => *c, 376 | None => return None, 377 | }; 378 | 379 | self.set_start(); 380 | if let Some(res) = match char { 381 | '_' | 'a'..='z' | 'A'..='Z' => { 382 | self.eat(); 383 | self.word_eater(); 384 | let cc = &self.source[self.start..self.current]; 385 | // TODO is there ever a scenario where a trie is faster then rust's match? 386 | self.send(match cc { 387 | "if" => Token::If, 388 | "else" => Token::Else, 389 | "elseif" => Token::ElseIf, 390 | "end" => Token::End, 391 | "for" => Token::For, 392 | "while" => Token::While, 393 | "function" => Token::Function, 394 | "in" => Token::In, 395 | "local" => Token::Local, 396 | #[cfg(feature = "global")] 397 | "global" => Token::Global, 398 | "nil" => Token::Nil, 399 | "not" => Token::Op(Operator::Not), 400 | "or" => Token::Op(Operator::Or), 401 | "repeat" => Token::Repeat, 402 | "until" => Token::Until, 403 | "return" => Token::Return, 404 | "then" => Token::Then, 405 | "true" => Token::True, 406 | "false" => Token::False, 407 | "and" => Token::Op(Operator::And), 408 | "break" => Token::Break, 409 | "do" => Token::Do, 410 | "goto" => Token::Goto, 411 | "class" => Token::Class, 412 | "sprint" => Token::Print, 413 | _ => Token::Identifier(Box::new(cc.to_string())), //return self.look_ahead(Token::Identifier(Box::new(cc.to_string()))), 414 | }) 415 | } 416 | '0'..='9' => self.number(false), 417 | '.' => { 418 | self.eat(); 419 | match self.peek() { 420 | Some('0'..='9') => self.number(true), 421 | Some('.') => self.eat_send(Token::Op(Operator::Concat)), 422 | _ => self.send(Token::Dot), 423 | } 424 | } 425 | '=' => { 426 | self.eat(); 427 | let t = match self.peek() { 428 | Some('=') => { 429 | self.eat(); 430 | Token::Op(Operator::Equal) 431 | } 432 | _ => Token::Assign, 433 | }; 434 | self.send(t) 435 | } 436 | '+' => { 437 | self.eat(); 438 | let t = match self.peek() { 439 | Some('=') => { 440 | self.eat(); 441 | Token::AddAssign 442 | } 443 | _ => Token::Op(Operator::Add), 444 | }; 445 | self.send(t) 446 | } 447 | '-' => { 448 | self.eat(); 449 | match self.peek() { 450 | Some('-') => { 451 | self.eat(); 452 | if let Some('!') = self.peek() { 453 | self.eat(); 454 | self.get_flag() 455 | } else { 456 | while self.current < self.end { 457 | if let Some('\n') = self.eat_out() { 458 | self.new_line(); 459 | break; 460 | } 461 | } 462 | None 463 | } 464 | } 465 | Some('=') => { 466 | self.eat(); 467 | self.send(Token::SubAssign) 468 | } 469 | Some('>') => { 470 | self.eat(); 471 | self.send(Token::ArrowFunction) 472 | } 473 | _ => self.send(Token::Op(Operator::Sub)), 474 | } 475 | } 476 | '/' => { 477 | self.eat(); 478 | match self.peek() { 479 | Some('=') => { 480 | self.eat(); 481 | self.send(Token::DivideAssign) 482 | } 483 | _ => self.send(Token::Op(Operator::Divide)), 484 | } 485 | } 486 | '*' => { 487 | self.eat(); 488 | match self.peek() { 489 | Some('=') => { 490 | self.eat(); 491 | self.send(Token::MultiplyAssign) 492 | } 493 | _ => self.send(Token::Op(Operator::Multiply)), 494 | } 495 | } 496 | '%' => { 497 | self.eat(); 498 | match self.peek() { 499 | Some('=') => { 500 | self.eat(); 501 | self.send(Token::ModulusAssign) 502 | } 503 | _ => self.send(Token::Op(Operator::Modulus)), 504 | } 505 | } 506 | '(' => { 507 | self.eat(); 508 | self.send(Token::OpenParen) 509 | } 510 | ')' => { 511 | self.eat(); 512 | self.send(Token::CloseParen) 513 | } 514 | ';' => { 515 | self.eat(); 516 | self.send(Token::SemiColon) 517 | } 518 | ',' => { 519 | self.eat(); 520 | self.send(Token::Comma) 521 | } 522 | '"' => self.string(false), 523 | '\'' => self.string(true), 524 | '<' => { 525 | self.eat(); 526 | match self.peek() { 527 | Some('=') => { 528 | self.eat(); 529 | self.send(Token::Op(Operator::LessEqual)) 530 | } 531 | _ => self.send(Token::Op(Operator::Less)), 532 | } 533 | } 534 | '>' => { 535 | self.eat(); 536 | match self.peek() { 537 | Some('=') => { 538 | self.eat(); 539 | self.send(Token::Op(Operator::GreaterEqual)) 540 | } 541 | _ => self.send(Token::Op(Operator::Greater)), 542 | } 543 | } 544 | '[' => { 545 | self.eat(); 546 | match self.peek() { 547 | Some(c) => match c { 548 | '[' => self.multi_line_string(), 549 | // '=' => { 550 | // self.eat(); 551 | // return Some(Token::OpenBracketAssign); 552 | // } 553 | _ => self.send(Token::OpenBracket), 554 | }, 555 | None => self.send(Token::OpenBracket), 556 | } 557 | } 558 | ']' => { 559 | self.eat(); 560 | self.send(Token::CloseBracket) 561 | } 562 | '{' => { 563 | self.eat(); 564 | self.send(Token::OpenBrace) 565 | } 566 | '}' => { 567 | self.eat(); 568 | self.send(Token::CloseBrace) 569 | } 570 | ' ' | '\r' | '\t' => { 571 | self.eat(); 572 | None 573 | } 574 | '\n' => { 575 | self.new_line(); 576 | self.eat(); 577 | None 578 | } 579 | '#' => { 580 | self.eat(); 581 | self.send(Token::Op(Operator::Length)) 582 | } 583 | ':' => { 584 | self.eat(); 585 | match self.peek() { 586 | Some(':') => { 587 | self.eat(); 588 | self.send(Token::ColonColon) 589 | } 590 | #[cfg(feature = "short-declare")] 591 | Some('=') => { 592 | self.eat(); 593 | self.send(Token::Op(Operator::ColonEquals)) 594 | } 595 | _ => { 596 | self.send(Token::Colon) 597 | // trust me on this, it's easier to parse this way 598 | // let t = self.colon_blow(); 599 | // self.add(t); 600 | } 601 | } 602 | } 603 | '~' => { 604 | self.eat(); 605 | match self.peek() { 606 | Some('=') => { 607 | self.eat(); 608 | self.send(Token::Op(Operator::NotEqual)) 609 | } 610 | _ => self.send(Token::Op(Operator::Tilde)), 611 | } 612 | } 613 | #[cfg(feature = "bang")] 614 | '!' => { 615 | self.eat(); 616 | match self.peek() { 617 | Some('=') => { 618 | self.eat(); 619 | self.send(Token::Op(Operator::NotEqual)) 620 | } 621 | _ => self.send(Token::Op(Operator::Not)), 622 | } 623 | } 624 | cw => { 625 | let re = self.error(SiltError::UnexpectedCharacter(cw)); 626 | self.eat(); 627 | re 628 | } 629 | } { 630 | return Some(res); 631 | } 632 | } 633 | None 634 | } 635 | } 636 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use error::ErrorTuple; 2 | use lua::Lua; 3 | use value::Value; 4 | 5 | mod chunk; 6 | mod code; 7 | pub mod compiler; 8 | mod error; 9 | mod function; 10 | mod lexer; 11 | pub mod lua; 12 | pub mod prelude; 13 | pub mod standard; 14 | pub mod table; 15 | mod token; 16 | mod userdata; 17 | pub mod value; 18 | 19 | use wasm_bindgen::prelude::*; 20 | 21 | fn simple(source: &str) -> Value { 22 | let mut vm = Lua::new(); 23 | vm.load_standard_library(); 24 | match vm.run(source) { 25 | Ok(v) => v, 26 | Err(e) => Value::String(Box::new(e[0].to_string())), 27 | } 28 | 29 | // let e = compiler.get_errors(); 30 | // if e.len() > 0 { 31 | // return Value::String(Box::new(e[0].to_string())); 32 | // } 33 | // Value::String(Box::new("Unknown error".to_string())) 34 | } 35 | 36 | fn complex(source: &str) -> Result { 37 | let mut vm = Lua::new(); 38 | vm.load_standard_library(); 39 | match vm.run(source) { 40 | Ok(v) => Ok(v), 41 | Err(e) => Err(e.get(0).unwrap().clone()), 42 | } 43 | } 44 | 45 | #[wasm_bindgen] 46 | extern "C" { 47 | pub fn jprintln(s: &str); 48 | } 49 | 50 | #[wasm_bindgen] 51 | pub fn run(source: &str) -> String { 52 | let mut vm = Lua::new(); 53 | vm.load_standard_library(); 54 | match vm.run(source) { 55 | Ok(v) => v.to_string(), 56 | Err(e) => e[0].to_string(), 57 | } 58 | } 59 | 60 | macro_rules! valeq { 61 | ($source:literal, $val:expr) => { 62 | assert_eq!(simple($source), $val); 63 | }; 64 | } 65 | 66 | macro_rules! fails { 67 | ($source:literal, $val:expr) => {{ 68 | match complex($source) { 69 | Ok(_) => panic!("Expected error"), 70 | Err(e) => assert_eq!(e.code, $val), 71 | } 72 | }}; 73 | } 74 | 75 | macro_rules! vstr { 76 | ($source:literal) => { 77 | Value::String(Box::new($source.to_string())) 78 | }; 79 | } 80 | 81 | #[cfg(test)] 82 | mod tests { 83 | use crate::{ 84 | chunk::Chunk, 85 | code::{self, OpCode}, 86 | complex, 87 | error::SiltError, 88 | function::FunctionObject, 89 | lua::Lua, 90 | prelude::ErrorTypes, 91 | simple, 92 | token::{Operator, Token}, 93 | value::Value, 94 | }; 95 | use std::{mem::size_of, println, rc::Rc}; 96 | 97 | #[test] 98 | fn test_32bits() { 99 | println!("size of i32: {}", size_of::()); 100 | println!("size of i64: {}", size_of::()); 101 | println!("size of f32: {}", size_of::()); 102 | println!("size of f64: {}", size_of::()); 103 | println!("size of bool: {}", size_of::()); 104 | println!("size of char: {}", size_of::()); 105 | println!("size of usize: {}", size_of::()); 106 | println!("size of u8: {}", size_of::()); 107 | println!("size of &str: {}", size_of::<&str>()); 108 | println!("size of String: {}", size_of::()); 109 | println!("size of Box: {}", size_of::>()); 110 | println!("size of boxed {}", size_of::>()); 111 | println!("size of Operator: {}", size_of::()); 112 | println!("size of Tester: {}", size_of::()); 113 | println!("size of Flag: {}", size_of::()); 114 | println!("size of token: {}", size_of::()); 115 | 116 | println!("size of token: {}", size_of::()); 117 | println!( 118 | "size of silt_error: {}", 119 | size_of::() 120 | ); 121 | println!( 122 | "size of error_types: {}", 123 | size_of::() 124 | ); 125 | 126 | println!("size of value: {}", size_of::()); 127 | println!("size of OpCode: {}", size_of::()); 128 | 129 | assert!(size_of::() == 24); 130 | assert!(size_of::() == 4); 131 | } 132 | 133 | #[test] 134 | fn speed() { 135 | let source_in = r#" 136 | start=clock() 137 | i=1 138 | a="a" 139 | while i < 100000 do 140 | i = i +1 141 | a = a .. "1" 142 | end 143 | elapsed=clock()-start 144 | print "done " 145 | print ("elapsed: "..elapsed) 146 | return {elapsed,i} 147 | "#; 148 | let tuple = if let crate::value::Value::Table(t) = simple(source_in) { 149 | let tt = t.borrow(); 150 | ( 151 | if let Some(&Value::Number(n)) = tt.getn(1) { 152 | n 153 | } else { 154 | 999999. 155 | }, 156 | if let Some(&Value::Integer(n)) = tt.getn(2) { 157 | println!("{} iterations", n); 158 | n 159 | } else { 160 | 0 161 | }, 162 | ) 163 | } else { 164 | panic!("not a table") 165 | }; 166 | 167 | // assert!(tuple.0 < 2.14); 168 | assert!(tuple.1 == 100_000); 169 | } 170 | 171 | // #[test] 172 | // fn fibby() { 173 | // let source_in = r#" 174 | // function fib(n) 175 | // if n <= 1 then 176 | // return n 177 | // else 178 | // return fib(n-1) + fib(n-2) 179 | // end 180 | // end 181 | 182 | // for i = 1, 35 do 183 | // sprint i..":"..fib(i) 184 | // end 185 | // "#; 186 | // println!("{}", simple(source_in)); 187 | // } 188 | 189 | #[test] 190 | fn fib() { 191 | let source_in = r#" 192 | start=clock() 193 | function fib(n) 194 | if n <= 1 then 195 | return n 196 | else 197 | return fib(n-1) + fib(n-2) 198 | end 199 | end 200 | 201 | for i = 1, 25 do 202 | print(i..":"..fib(i)) 203 | end 204 | elapsed=clock()-start 205 | return elapsed 206 | "#; 207 | 208 | let n = if let crate::value::Value::Number(n) = simple(source_in) { 209 | println!("{} seconds", n); 210 | n 211 | } else { 212 | 999999. 213 | }; 214 | println!("{} seconds", n); 215 | // assert!(n < 3.4) 216 | } 217 | #[test] 218 | fn chunk_validity() { 219 | let mut c = Chunk::new(); 220 | c.write_value(Value::Number(1.2), (1, 1)); 221 | c.write_value(Value::Number(3.4), (1, 2)); 222 | c.write_code(OpCode::ADD, (1, 3)); 223 | c.write_value(Value::Number(9.2), (1, 4)); 224 | c.write_code(OpCode::DIVIDE, (1, 5)); 225 | c.write_code(OpCode::NEGATE, (1, 1)); 226 | c.write_code(OpCode::RETURN, (1, 3)); 227 | c.print_chunk(None); 228 | println!("-----------------"); 229 | let blank = FunctionObject::new(None, false); 230 | let mut tester = FunctionObject::new(None, false); 231 | tester.set_chunk(c); 232 | let mut vm = Lua::new(); 233 | match vm.execute(Rc::new(tester)) { 234 | Ok(v) => { 235 | assert_eq!(v, Value::Number(-0.5)); 236 | } 237 | Err(e) => { 238 | panic!("Test should not fail with error: {}", e) 239 | } 240 | } 241 | } 242 | 243 | #[test] 244 | fn compliance() { 245 | valeq!("return 1+2", Value::Integer(3)); 246 | valeq!("return '1'..'2'", vstr!("12")); 247 | 248 | valeq!( 249 | r#" 250 | local a= 1+2 251 | return a 252 | "#, 253 | Value::Integer(3) 254 | ); 255 | valeq!( 256 | r#" 257 | local a= '1'..'2' 258 | return a 259 | "#, 260 | vstr!("12") 261 | ); 262 | valeq!( 263 | r#" 264 | local a= 'a' 265 | a='b' 266 | local b='c' 267 | b=a..b 268 | return b 269 | "#, 270 | vstr!("bc") 271 | ); 272 | } 273 | 274 | #[test] 275 | fn string_infererence() { 276 | valeq!("return '1'+2", Value::Integer(3)); 277 | fails!( 278 | "return 'a1'+2", 279 | SiltError::ExpOpValueWithValue(ErrorTypes::String, Operator::Add, ErrorTypes::Integer) 280 | ); 281 | } 282 | 283 | #[test] 284 | fn scope() { 285 | valeq!( 286 | r#" 287 | -- for loop closures should capture the loop variable at that point in time and not the final value of the loop variable 288 | a = {} 289 | do 290 | for i = 1, 3 do 291 | local function t() 292 | return i 293 | end 294 | a[i] = t 295 | end 296 | 297 | return a[1]() + a[2]() + a[3]() -- 1+2+3 = 6 298 | end 299 | "#, 300 | Value::Integer(6) 301 | ); 302 | valeq!( 303 | r#" 304 | -- Same but test in global scope 305 | a = {} 306 | for i = 1, 3 do 307 | local function t() 308 | return i 309 | end 310 | a[i] = t 311 | end 312 | 313 | return a[1]() + a[2]() + a[3]() -- 1+2+3 = 6 314 | "#, 315 | Value::Integer(6) 316 | ); 317 | } 318 | 319 | #[test] 320 | fn closures() { 321 | valeq!( 322 | r#" 323 | do 324 | local a = 1 325 | local b = 2 326 | local function f1() 327 | local c = 3 328 | local function f2() 329 | local d = 4 330 | c = c + d -- 7, 11 331 | return a + b + c + d -- 1+2+7+4= 14, 1+2+11+4 = 18 332 | end 333 | return f2 334 | end 335 | local e = 1000 336 | local x = f1() 337 | return x() + x() -- 14+18 = 32 338 | end 339 | "#, 340 | Value::Integer(32) 341 | ); 342 | } 343 | 344 | #[test] 345 | fn closures2() { 346 | valeq!( 347 | r#" 348 | do 349 | local a = 1 350 | function f1() 351 | return a 352 | end 353 | 354 | local b = f1() 355 | a = 3 356 | b = b + f1() 357 | return b --4 358 | end 359 | "#, 360 | Value::Integer(4) 361 | ); 362 | valeq!( 363 | r#" 364 | do 365 | function outer() 366 | local y = 0 367 | local x = 2 368 | local function middle() 369 | local function inner() 370 | return x 371 | end 372 | y = y + 1 373 | return inner 374 | end 375 | 376 | y = y + 1 377 | x = x + y 378 | return middle 379 | end 380 | 381 | a = outer() 382 | b = a() 383 | return b() 384 | end 385 | "#, 386 | Value::Integer(3) 387 | ); 388 | } 389 | } 390 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use silt_lua::prelude::Lua; 2 | 3 | const FALLBACK_FILE: &str = "scripts/test.lua"; 4 | fn main() { 5 | let args: Vec = std::env::args().collect(); 6 | { 7 | // cli(source_in, &mut global); 8 | 9 | // match cli(source_in, &mut global) { 10 | // value::Value::Nil => {} 11 | // v => println!("{}", v), 12 | // } 13 | // println!(">"); 14 | 15 | // loop { 16 | // let mut line = String::new(); 17 | // if let Ok(_) = std::io::stdin().read_line(&mut line) { 18 | // let s = line.trim(); 19 | // match cli(s, &mut global) { 20 | // value::Value::Nil => {} 21 | // v => println!("{}", v), 22 | // } 23 | // println!(">"); 24 | // } 25 | // } 26 | 27 | // println!("Hello, world!"); 28 | // let source = r#" 29 | // abc 30 | // abc2 31 | // bc=5--hi there 32 | // a=1 33 | // b=2 34 | // c=a+b 35 | // d='hello' 36 | // e="world" 37 | // f=[[multi 38 | // line]] 39 | // print(c) 40 | // if a==1 then 41 | // a=2 42 | // end 43 | // print(a) 44 | // let source_in = 45 | // "#; 46 | // let source_in = r#" 47 | // a=1 48 | // while a < 10_000_00 do 49 | // a = a + 1 50 | // end 51 | // print a 52 | // "#; 53 | 54 | //benchmark 55 | // let source_in = r#" 56 | // start=clock() 57 | // i=1 58 | // a="a" 59 | // while i < 100_000 do 60 | // i = i +1 61 | // a = a .. "1" 62 | // end 63 | // elapsed=clock()-start 64 | // print "done" 65 | // print elapsed 66 | // elapsed 67 | // "#; 68 | 69 | // func test 70 | // let source_in = r#" 71 | 72 | // n=1 73 | // function b(h) print 'b'..n n=h print 'c'..n end 74 | 75 | // function a() print 'e'..n n=3 print 'f'..n b(10) print 'g'..n end 76 | 77 | // print 'a'..n 78 | // b(9) 79 | // print 'd'..n 80 | // a() 81 | // print 'h'.. n 82 | // "#; 83 | 84 | // thrice 85 | // let source_in = r#" 86 | 87 | // function thrice(fn) 88 | // for i = 1, 1 do 89 | // fn(i) 90 | // end 91 | // end 92 | 93 | // thrice(function(i) print("p "..i) end) 94 | // "#; 95 | 96 | // let source_in = r#" 97 | // function create_counter() 98 | // local count = 0 99 | // return function() 100 | // count = count + 1 101 | // print(count) 102 | // return count 103 | // end 104 | // end 105 | 106 | // local counter = create_counter() 107 | // counter() 108 | // counter() 109 | // "#; 110 | // let source_in = r#" 111 | // function echo(n) 112 | // print("x"..n) 113 | // return 2 114 | // end 115 | 116 | // echo(2) 117 | // print("y"..2) 118 | 119 | // --print(echo(echo(1)+echo(2)) + echo(echo(4)+echo(5)) ) 120 | // "#; 121 | // let source_in = r#" 4*-6*(3 * 7 + 5) + 2 * 9"#; 122 | 123 | // let source_in = r#" 124 | // do 125 | // local a=1 126 | // if true then 127 | // local b=2 128 | // print(a) 129 | // end 130 | // print(b) 131 | // end 132 | // "#; 133 | 134 | // let source_in = r#" 135 | // "#; 136 | 137 | // panic!("test"); 138 | 139 | // fibonaci 140 | // let source_in = r#" 141 | // a=0 142 | // b=1 143 | // while a < 10000 do 144 | // b+=temp 145 | // print a 146 | // temp=a 147 | // a=b 148 | // end 149 | // "#; 150 | // let source_in = "local a= 'test'..'1' a='now' sprint a"; // sprint 1+8"; 151 | // let source_in = "local a= -5+6"; 152 | // let source_in = r#"!(5-4 > 3*2 == !nil)"#; 153 | // let source_in = "!nil==!false"; 154 | 155 | // let source_in = r#" 156 | // local a="test" 157 | // local b="t" 158 | // sprint b 159 | // b="a" 160 | // sprint b 161 | // b=a.."working" 162 | // sprint b 163 | // "#; 164 | 165 | // let source_in = r#" 166 | // local a=2 167 | // local b=1 168 | // a*b=5 169 | // sprint ab 170 | // "#; 171 | // let source_in = r#" 172 | // do 173 | // local a=1 174 | // a=3+4 175 | // sprint a 176 | // end 177 | // sprint a 178 | // "#; 179 | // let source_in = r#" 180 | // do 181 | // local a=3 182 | // sprint a 183 | // end 184 | // sprint a 185 | // "#; 186 | // // REMEMBER 10,000,000 takes about ~6.47 seconds 187 | // let source_in = r#" 188 | // do 189 | // local a=1 190 | // while a<= 10_000_000 do 191 | // a=a+1 192 | // end 193 | // sprint a 194 | // end 195 | // "#; 196 | 197 | // let source_in = r#" 198 | // "#; 199 | 200 | // let source_in = r#" 201 | // function fib(n) 202 | // if n <= 1 then 203 | // return n 204 | // else 205 | // return fib(n-1) + fib(n-2) 206 | // end 207 | // end 208 | // -- return fib(21) 209 | 210 | // for i = 1, 35 do 211 | // sprint i..":"..fib(i) 212 | // end 213 | // "#; 214 | 215 | // let source_in = r#" 216 | // start=clock() 217 | // return 1000*(clock() - start) 218 | // "#; 219 | // let source_in = r#" 220 | // start=clock() 221 | // i=1 222 | // a="a" 223 | // while i < 10 do 224 | // i = i +1 225 | // a = a .. "1" 226 | // end 227 | // elapsed=clock()-start 228 | // print "done" 229 | // print ("elapsed: "..elapsed) 230 | // return elapsed 231 | // "#; 232 | // let source_in = r#" 233 | // local d=5 234 | // function sum() 235 | // local a=1 236 | // local b=2 237 | // local c=3 238 | // return a+b+c+d+8 239 | // end 240 | 241 | // return sum() 242 | // "#; 243 | } 244 | // load string from scripts/closure4.lua 245 | let file = if args.len() > 1 { 246 | std::fs::read_to_string(args[1].as_str()).unwrap() 247 | } else { 248 | std::fs::read_to_string(FALLBACK_FILE).unwrap() 249 | }; 250 | let source_in = file.as_str(); 251 | // let source_in = r#" 252 | // global g=2 253 | // function is1(n) 254 | // if n==1 then 255 | // return 'ye' 256 | // else 257 | // return 'naw' 258 | // end 259 | // end 260 | // return is1(1) 261 | // "#; 262 | 263 | // let source_in = r#" 264 | // do 265 | // local a=5 266 | // sprint a 267 | // a=nil 268 | // sprint a 269 | // local b=5+6 270 | // 3+2 271 | // a=1+4 +b 272 | // sprint a 273 | // end 274 | // "#; 275 | // TODO should get about 100 million in a second for lua 276 | // let source_in = r#" 277 | // do 278 | // local a=1 279 | // for i=1, 10 do 280 | // a=i 281 | // end 282 | // sprint a 283 | // end 284 | // "#; 285 | 286 | // let mut compiler = Compiler::new(); 287 | // let o = compiler.compile(source_in.to_string()); 288 | 289 | // #[cfg(feature = "dev-out")] 290 | // o.chunk.print_chunk(None); 291 | // compiler.print_errors(); 292 | 293 | let mut vm = Lua::new(); 294 | vm.load_standard_library(); 295 | match vm.run(source_in) { 296 | Ok(o) => { 297 | println!("-----------------"); 298 | println!(">> {}", o); 299 | } 300 | Err(e) => { 301 | e.iter().for_each(|e| println!("!!Err: {}", e)); 302 | } 303 | } 304 | } 305 | 306 | // fn cli(source: &str, global: &mut environment::Environment) -> value::Value { 307 | // println!("-----------------"); 308 | // let mut lexer = lexer::Lexer::new(source.to_owned()); 309 | // let mut t: Vec = lexer.collect(); 310 | // let mut erronious = false; 311 | // let mut tokens = vec![]; 312 | // let mut locations = vec![]; 313 | // t.drain(..).enumerate().for_each(|(i, res)| match res { 314 | // Ok(t) => { 315 | // let p = t.1; 316 | // println!("|{}:{}| {}", p.0, p.1, t.0); 317 | 318 | // tokens.push(t.0); 319 | // locations.push(t.1); 320 | // } 321 | // Err(e) => { 322 | // erronious = true; 323 | // println!("|!! {}", e) 324 | // } 325 | // }); 326 | 327 | // if !erronious { 328 | // let mut parser = crate::parser::parser::Parser::new(tokens, locations, global); 329 | // println!("|----------------"); 330 | // let mut statements = parser.parse(); 331 | // statements 332 | // .iter() 333 | // .enumerate() 334 | // .for_each(|(i, e)| println!("| {} {}", i, e)); 335 | // // println!("{}", exp); 336 | // // let val = parser.evaluate(exp); 337 | // let err = parser.get_errors(); 338 | // if err.len() > 0 { 339 | // println!("|----------------"); 340 | // println!("Parse Errors:"); 341 | // err.iter().for_each(|e| println!("{}", e)); 342 | // println!("-----------------"); 343 | // } else { 344 | // println!("-----------------"); 345 | // let mut resolver = crate::resolver::Resolver::new(); 346 | // resolver.process(&mut statements); 347 | // let res = crate::interpreter::execute(global, &statements); 348 | // match res { 349 | // Ok(v) => { 350 | // println!(""); 351 | // return v; 352 | // } 353 | // Err(e) => { 354 | // println!("| Runtime Errors:"); 355 | // println!("| {}", e); 356 | // println!("-----------------"); 357 | // } 358 | // } 359 | // } 360 | // } 361 | 362 | // println!(""); 363 | // value::Value::Nil 364 | // } 365 | -------------------------------------------------------------------------------- /src/prelude.rs: -------------------------------------------------------------------------------- 1 | #[doc(no_inline)] 2 | pub use crate::{ 3 | error::ErrorTypes, 4 | function::{Closure, FunctionObject, NativeObject}, 5 | lua::Lua, 6 | table::Table, 7 | userdata::UserData, 8 | value::{Reference, Value}, 9 | }; 10 | -------------------------------------------------------------------------------- /src/standard.rs: -------------------------------------------------------------------------------- 1 | use crate::{lua::Lua, value::Value}; 2 | 3 | pub fn clock(_: &mut Lua, _: Vec) -> Value { 4 | Value::Number( 5 | std::time::SystemTime::now() 6 | .duration_since(std::time::UNIX_EPOCH) 7 | .unwrap() 8 | .as_secs_f64(), 9 | ) 10 | } 11 | 12 | pub fn print(_: &mut Lua, args: Vec) -> Value { 13 | let s = args 14 | .iter() 15 | .map(|v| v.to_string()) 16 | .collect::>() 17 | .join("\t"); 18 | println!("> {}", s); 19 | 20 | #[cfg(target_arch = "wasm32")] 21 | crate::jprintln(s.as_str()); 22 | 23 | Value::Nil 24 | } 25 | -------------------------------------------------------------------------------- /src/table.rs: -------------------------------------------------------------------------------- 1 | use hashbrown::HashMap; 2 | 3 | use crate::value::Value; 4 | 5 | pub struct Table { 6 | data: HashMap, 7 | /** replicate standard lua behavior */ 8 | counter: i64, 9 | id: usize, 10 | } 11 | 12 | impl Table { 13 | pub fn new(id: usize) -> Self { 14 | Table { 15 | data: HashMap::new(), 16 | counter: 0, 17 | id, 18 | } 19 | } 20 | pub fn insert(&mut self, key: Value, value: Value) { 21 | self.data.insert(key, value); 22 | } 23 | pub fn get(&self, key: &Value) -> Option<&Value> { 24 | self.data.get(key) 25 | } 26 | pub fn getn(&self, i: usize) -> Option<&Value> { 27 | self.data.get(&Value::Integer(i as i64)) 28 | } 29 | pub fn get_value(&self, key: &Value) -> Value { 30 | match self.data.get(key) { 31 | Some(v) => v.clone(), 32 | None => Value::Nil, 33 | } 34 | } 35 | 36 | pub fn len(&self) -> usize { 37 | self.data.len() 38 | } 39 | 40 | /** push by counter's current index, if it aready exists keep incrementing until empty position is found */ 41 | pub fn push(&mut self, value: Value) { 42 | // DEV this just feels clunky to replicate lua's behavior 43 | self.counter += 1; 44 | let mut key = Value::Integer(self.counter); 45 | while self.data.contains_key(&key) { 46 | self.counter += 1; 47 | key.force_to_int(self.counter); 48 | } 49 | self.data.insert(key, value); 50 | } 51 | } 52 | 53 | impl ToString for Table { 54 | fn to_string(&self) -> String { 55 | format!( 56 | "table{}[{}]{{{}}}", 57 | self.id, 58 | self.data.len(), 59 | self.data 60 | .iter() 61 | .map(|(k, v)| format!("{}: {}", k, v)) 62 | .collect::>() 63 | .join(", ") 64 | ) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/token.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Display; 2 | // implement clone 3 | 4 | #[derive(Debug, Clone, PartialEq)] 5 | pub enum Token { 6 | // TODO temporary 7 | Print, 8 | 9 | Identifier(Box), 10 | Break, 11 | Do, 12 | If, 13 | Else, 14 | ElseIf, 15 | End, 16 | For, 17 | While, 18 | Function, 19 | ArrowFunction, 20 | In, 21 | Local, 22 | Goto, 23 | 24 | Repeat, 25 | Until, 26 | Return, 27 | Then, 28 | 29 | // symbols 30 | Dot, 31 | Assign, //Equal 32 | 33 | // operator 34 | Op(Operator), 35 | // Not, 36 | // And, 37 | // Or, 38 | // Add, 39 | // Sub, 40 | // Multiply, 41 | // Divide, 42 | // FloorDivide, 43 | // Modulus, 44 | // Exponent, 45 | // Concat, 46 | // Equal, 47 | // NotEqual, 48 | // LessThan, 49 | // LessThanOrEqual, 50 | // GreaterThan, 51 | // GreaterThanOrEqual, 52 | AddAssign, 53 | SubAssign, 54 | MultiplyAssign, 55 | DivideAssign, 56 | ModulusAssign, 57 | Colon, //: 58 | ColonColon, //:: 59 | ColonIdentifier(Box), // :ident (for types and calls) 60 | OpenParen, 61 | CloseParen, 62 | OpenBracket, 63 | CloseBracket, 64 | OpenBrace, 65 | CloseBrace, 66 | SemiColon, 67 | Comma, 68 | 69 | // primary 70 | Nil, 71 | Number(f64), 72 | Integer(i64), 73 | StringLiteral(Box), 74 | True, 75 | False, 76 | EOF, 77 | 78 | //extra 79 | // Bang, // ! 80 | Class, 81 | Global, 82 | Flag(Flag), 83 | Type, 84 | } 85 | 86 | #[derive(Debug, Clone, PartialEq)] 87 | pub enum Operator { 88 | Not, 89 | Tilde, 90 | And, 91 | Or, 92 | Add, 93 | Sub, 94 | Multiply, 95 | Divide, 96 | FloorDivide, 97 | Modulus, 98 | Exponent, 99 | Concat, 100 | Equal, 101 | NotEqual, 102 | Less, 103 | LessEqual, 104 | Greater, 105 | GreaterEqual, 106 | Length, 107 | ColonEquals, 108 | } 109 | 110 | #[derive(Debug, Clone, PartialEq)] 111 | pub enum Flag { 112 | Strict, 113 | Local, 114 | } 115 | impl Token { 116 | pub fn unwrap_identifier(&self) -> &Box { 117 | match self { 118 | Token::Identifier(s) => s, 119 | _ => panic!("unwrap_identifier"), 120 | } 121 | } 122 | } 123 | impl Display for Flag { 124 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 125 | match *self { 126 | Flag::Strict => write!(f, "strict"), 127 | Flag::Local => write!(f, "local"), 128 | } 129 | } 130 | } 131 | 132 | impl Display for Token { 133 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 134 | match *self { 135 | Token::Print => write!(f, "print"), 136 | Token::Op(ref op) => write!(f, "{}", op), 137 | Token::Break => write!(f, "break"), 138 | Token::Do => write!(f, "do"), 139 | Token::If => write!(f, "if"), 140 | Token::Else => write!(f, "else"), 141 | Token::ElseIf => write!(f, "elseif"), 142 | Token::End => write!(f, "end"), 143 | Token::For => write!(f, "for"), 144 | Token::While => write!(f, "while"), 145 | Token::Function => write!(f, "function"), 146 | Token::ArrowFunction => write!(f, "arrow function"), 147 | Token::In => write!(f, "in"), 148 | Token::Local => write!(f, "local"), 149 | Token::Nil => write!(f, "nil"), 150 | Token::Repeat => write!(f, "repeat"), 151 | Token::Until => write!(f, "until"), 152 | Token::Return => write!(f, "return"), 153 | Token::Then => write!(f, "then"), 154 | Token::True => write!(f, "true"), 155 | Token::False => write!(f, "false"), 156 | Token::Goto => write!(f, "goto"), 157 | Token::Assign => write!(f, "::="), 158 | Token::AddAssign => write!(f, "+="), 159 | Token::SubAssign => write!(f, "-="), 160 | Token::MultiplyAssign => write!(f, "*="), 161 | Token::DivideAssign => write!(f, "/="), 162 | Token::ModulusAssign => write!(f, "%="), 163 | Token::Colon => write!(f, ":"), 164 | Token::ColonColon => write!(f, "::"), 165 | Token::Class => write!(f, "class"), 166 | Token::Number(n) => write!(f, "{}f", n), 167 | Token::Integer(i) => write!(f, "{}i", i), 168 | Token::Identifier(ref s) => write!(f, "ident({})", s), 169 | Token::OpenParen => write!(f, "("), 170 | Token::CloseParen => write!(f, ")"), 171 | Token::OpenBrace => write!(f, "{{"), 172 | Token::CloseBrace => write!(f, "}}"), 173 | Token::SemiColon => write!(f, ";"), 174 | Token::Comma => write!(f, ","), 175 | Token::StringLiteral(ref s) => write!(f, "string({})", s), 176 | Token::OpenBracket => write!(f, "["), 177 | Token::CloseBracket => write!(f, "]"), 178 | // Token::EOF => write!(f, "EOF"), 179 | Token::Dot => write!(f, "call"), 180 | // Token::Bang => write!(f, "!"), 181 | Token::Type => write!(f, "type"), 182 | Token::ColonIdentifier(ref ident) => write!(f, ":{}", ident), 183 | Token::Global => write!(f, "global"), 184 | Token::Flag(ref flag) => write!(f, "flag({})", flag), 185 | Self::EOF => write!(f, "EOF"), 186 | } 187 | } 188 | } 189 | 190 | impl Display for Operator { 191 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 192 | match *self { 193 | Operator::And => write!(f, "and"), 194 | Operator::Not => write!(f, "not"), 195 | Operator::Or => write!(f, "or"), 196 | Operator::Add => write!(f, "+"), 197 | Operator::Sub => write!(f, "-"), 198 | Operator::Multiply => write!(f, "*"), 199 | Operator::Divide => write!(f, "/"), 200 | Operator::FloorDivide => write!(f, "//"), 201 | Operator::Modulus => write!(f, "%"), 202 | Operator::Exponent => write!(f, "^"), 203 | Operator::Concat => write!(f, ".."), 204 | Operator::Equal => write!(f, "=="), 205 | Operator::NotEqual => write!(f, "~="), 206 | Operator::Less => write!(f, "<"), 207 | Operator::LessEqual => write!(f, "<="), 208 | Operator::Greater => write!(f, ">"), 209 | Operator::GreaterEqual => write!(f, ">="), 210 | Operator::Tilde => write!(f, "~"), 211 | Operator::Length => write!(f, "#"), 212 | Operator::ColonEquals => write!(f, ":="), 213 | } 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /src/types.rs: -------------------------------------------------------------------------------- 1 | pub enum Type { 2 | Number, 3 | Integer, 4 | String, 5 | Boolean, 6 | Nil, 7 | Function, 8 | Table, 9 | List, 10 | Userdata, 11 | Thread, 12 | LightUserdata, 13 | Any, 14 | None, 15 | } 16 | -------------------------------------------------------------------------------- /src/userdata.rs: -------------------------------------------------------------------------------- 1 | pub trait UserData { 2 | fn get_type(&self) -> String; 3 | fn to_string(&self) -> String; 4 | fn get_meta_methods(&self) -> Option { 5 | None 6 | } 7 | } 8 | 9 | pub struct MetaMethods { 10 | // pub add: Option UserData>, 11 | // pub sub: Option UserData>, 12 | // pub mul: Option UserData>, 13 | // pub div: Option UserData>, 14 | // pub pow: Option UserData>, 15 | // pub concat: Option UserData>, 16 | // pub eq: Option UserData>, 17 | // pub lt: Option UserData>, 18 | // pub le: Option UserData>, 19 | // pub len: Option UserData>, 20 | // pub tostring: Option UserData>, 21 | // pub call: Option) -> UserData>, 22 | } 23 | 24 | struct ExampleUserData { 25 | value: i64, 26 | } 27 | 28 | impl UserData for ExampleUserData { 29 | fn get_type(&self) -> String { 30 | "ExampleUserData".to_string() 31 | } 32 | fn to_string(&self) -> String { 33 | self.value.to_string() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/value.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::RefCell, rc::Rc}; 2 | 3 | use hashbrown::HashMap; 4 | 5 | use crate::{ 6 | error::{ErrorTypes, SiltError}, 7 | function::{Closure, FunctionObject, NativeObject}, 8 | lua::Lua, 9 | table::Table, 10 | token::Operator, 11 | userdata::UserData, 12 | }; 13 | 14 | /** 15 | * Increment self by value and operand type, if increment op fails then use fallback, for instance += would fallback to + 16 | * The fallback is used in scenarios where trying to adjust self would change the type, such as an integer to float 17 | */ 18 | macro_rules! binary_self_op { 19 | ($l:ident, $op:tt, $fallback:tt, $r:ident, $opp:tt) => { 20 | { 21 | match(&mut *$l, $r){ 22 | (Value::Number(left), Value::Number(right)) => *left $op right, 23 | (Value::Integer(left), Value::Integer(right)) =>*left $op right, 24 | (Value::Number(left), Value::Integer(right)) => *left $op (*right as f64), 25 | // (Value::Integer(left), Value::Number(right)) => Some(Value::Number((*left as f64) $fallback right)), 26 | (Value::Integer(left), Value::Number(right)) => *$l= Value::Number((*left as f64) $fallback right), 27 | // TODO 28 | (ll,rr) => return Err(SiltError::ExpOpValueWithValue(ll.to_error(), Operator::$opp, rr.to_error())) 29 | } 30 | Ok(()) 31 | } 32 | }; 33 | } 34 | 35 | /** Lua value enum representing different data types within a VM */ 36 | pub enum Value { 37 | Nil, 38 | Integer(i64), 39 | Number(f64), 40 | Bool(bool), 41 | /** true for negative */ 42 | Infinity(bool), 43 | // Bool(bool), 44 | // TODO in most cases strings are just copied around on the heap, this is expensive but saves us from using GC here 45 | // TODO 2 consider other encoding options for efficiency. Is having a seperate ASCII string type beneficial to only english speakers? how would other speaking languages handle ascii strings without needed glyphs? 46 | String(Box), 47 | // List(Vec), 48 | // Map(HashMap), 49 | Table(Rc>), 50 | // Array // TODO lua 5 has an actual array type chosen contextually, how much faster can we make a table by using it? 51 | // Boxed() 52 | Function(Rc), // closure: Environment, 53 | Closure(Rc), 54 | // Func(fn(Vec) -> Value) 55 | NativeFunction(Rc), 56 | UserData(Rc), 57 | } 58 | 59 | pub enum ReferneceStore { 60 | Table(HashMap), 61 | } 62 | pub struct Reference { 63 | pub value: Rc, 64 | pub id: usize, 65 | } 66 | 67 | impl std::fmt::Display for Value { 68 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 69 | match self { 70 | Value::Integer(i) => write!(f, "{}", i), 71 | Value::Number(n) => write!(f, "{}", n), 72 | Value::Bool(b) => write!(f, "{}", b), 73 | Value::Nil => write!(f, "nil"), 74 | Value::String(s) => write!(f, "\"{}\"", s), 75 | Value::Infinity(_) => write!(f, "inf"), 76 | Value::NativeFunction(_) => write!(f, "native_function"), 77 | Value::Closure(c) => write!(f, "=>({})", c.function), 78 | Value::Function(ff) => write!(f, "{}", ff), 79 | Value::Table(t) => write!(f, "{}", t.borrow().to_string()), 80 | Value::UserData(u) => write!(f, "{}", u.to_string()), 81 | } 82 | } 83 | } 84 | 85 | impl core::fmt::Debug for Value { 86 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 87 | write!(f, "{}", self) 88 | } 89 | } 90 | 91 | impl Value { 92 | /** Condense value into a tiny enum for passing to errors*/ 93 | pub fn to_error(&self) -> ErrorTypes { 94 | match self { 95 | Value::Integer(_) => ErrorTypes::Integer, 96 | Value::Number(_) => ErrorTypes::Number, 97 | Value::Bool(_) => ErrorTypes::Bool, 98 | Value::Nil => ErrorTypes::Nil, 99 | Value::String(_) => ErrorTypes::String, 100 | Value::Infinity(_) => ErrorTypes::Infinity, 101 | Value::NativeFunction(_) => ErrorTypes::NativeFunction, 102 | Value::Function { .. } => ErrorTypes::Function, 103 | Value::Closure(_) => ErrorTypes::Closure, 104 | Value::Table(_) => ErrorTypes::Table, 105 | Value::UserData(_) => ErrorTypes::UserData, 106 | } 107 | } 108 | 109 | pub fn force_to_int(&mut self, n: i64) { 110 | *self = Value::Integer(n); 111 | } 112 | pub fn force_to_float(&mut self, n: f64) { 113 | *self = Value::Number(n); 114 | } 115 | 116 | pub fn increment(&mut self, value: &Value) -> Result<(), SiltError> { 117 | binary_self_op!(self, +=,+, value, Add) 118 | // match match (&mut *self, value) { 119 | // (Value::Number(left), Value::Number(right)) => { 120 | // *left += right; 121 | // None 122 | // } 123 | // (Value::Integer(left), Value::Number(right)) => { 124 | // Some(Value::Number((*left as f64) + right)) 125 | // // self.force_to_float(left as f64 + right); 126 | // } 127 | 128 | // _ => unreachable!(), 129 | // } { 130 | // Some(v) => *self = v, 131 | // None => {} 132 | // } 133 | // Ok(()) 134 | } 135 | } 136 | 137 | impl Clone for Value { 138 | fn clone(&self) -> Self { 139 | match self { 140 | Value::Integer(i) => Value::Integer(*i), 141 | Value::Number(n) => Value::Number(*n), 142 | Value::Bool(b) => Value::Bool(*b), 143 | Value::Nil => Value::Nil, 144 | Value::String(s) => Value::String(s.clone()), 145 | Value::Infinity(b) => Value::Infinity(*b), 146 | Value::NativeFunction(f) => Value::NativeFunction(f.clone()), 147 | // TODO: implement this 148 | Value::Function(r) => Value::Function(Rc::clone(r)), 149 | Value::Closure(c) => Value::Closure(Rc::clone(c)), 150 | // Value::Table(t) => Value::Table(Reference { 151 | // value: Rc::clone(&t.value), 152 | // id: t.id, 153 | // }), 154 | Value::Table(t) => Value::Table(Rc::clone(t)), 155 | Value::UserData(u) => Value::UserData(Rc::clone(u)), 156 | } 157 | } 158 | } 159 | 160 | impl PartialEq for Value { 161 | fn eq(&self, other: &Self) -> bool { 162 | match (self, other) { 163 | (Value::Integer(i), Value::Integer(j)) => i == j, 164 | (Value::Number(i), Value::Number(j)) => i == j, 165 | (Value::Bool(i), Value::Bool(j)) => i == j, 166 | (Value::Nil, Value::Nil) => true, 167 | (Value::String(i), Value::String(j)) => i == j, 168 | (Value::Infinity(i), Value::Infinity(j)) => i == j, 169 | (Value::NativeFunction(i), Value::NativeFunction(j)) => { 170 | i.function as *const fn(&mut Lua, Vec) -> Value 171 | == j.function as *const fn(&mut Lua, Vec) -> Value 172 | } 173 | (Value::Function(i), Value::Function(j)) => Rc::ptr_eq(i, j), 174 | (Value::Table(i), Value::Table(j)) => Rc::ptr_eq(&i, &j), 175 | _ => false, 176 | } 177 | } 178 | } 179 | 180 | impl Default for Value { 181 | fn default() -> Self { 182 | Value::Nil 183 | } 184 | } 185 | 186 | impl core::hash::Hash for Value { 187 | fn hash(&self, state: &mut H) { 188 | core::mem::discriminant(self).hash(state); 189 | } 190 | } 191 | 192 | impl Eq for Value {} 193 | 194 | // impl Drop for Value { 195 | // fn drop(&mut self) { 196 | // println!("dropping value: {}", self); 197 | // } 198 | // } 199 | -------------------------------------------------------------------------------- /wasm-tester/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | hello-wasm example 6 | 7 | 8 | 16 | 17 | 18 | --------------------------------------------------------------------------------