├── tests ├── programs │ ├── unused_local.lox │ ├── class_method_this.lox │ ├── constructor_error.lox │ ├── class_method.lox │ ├── sayHi.lox │ ├── constructor_returns_this.lox │ ├── lambda.lox │ ├── fib.lox │ ├── binding.lox │ ├── getter.lox │ ├── constructor.lox │ ├── simple_class.lox │ ├── counter.lox │ ├── constructor_returns_this_early.lox │ ├── simple_method.lox │ ├── callback_from_method.lox │ ├── inherit_methods.lox │ └── super.lox └── test_integration.rs ├── .vscode ├── settings.json └── launch.json ├── docs ├── pkg │ ├── jlox_rs_lib_bg.wasm │ ├── package.json │ ├── jlox_rs_lib_bg.wasm.d.ts │ ├── jlox_rs_lib.d.ts │ ├── jlox_rs_lib_bg.js │ └── jlox_rs_lib.js ├── styles.css ├── examples.js ├── loxMonarchTokensProvider.js ├── micromodal.css └── index.html ├── macros ├── Cargo.toml └── src │ └── lib.rs ├── Cargo.toml ├── src ├── main.rs ├── types.rs ├── token.rs ├── environment.rs ├── lib.rs ├── ast.rs ├── scanner.rs ├── resolver.rs ├── parser.rs └── interpreter.rs ├── .gitignore └── README.md /tests/programs/unused_local.lox: -------------------------------------------------------------------------------- 1 | fun f() { 2 | var x = 0; 3 | } 4 | 5 | f(); -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.procMacro.attributes.enable": true 3 | } -------------------------------------------------------------------------------- /docs/pkg/jlox_rs_lib_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abesto/jlox-rs/HEAD/docs/pkg/jlox_rs_lib_bg.wasm -------------------------------------------------------------------------------- /tests/programs/class_method_this.lox: -------------------------------------------------------------------------------- 1 | class C { 2 | class f() { 3 | return this; 4 | } 5 | } 6 | 7 | print C.f(); -------------------------------------------------------------------------------- /tests/programs/constructor_error.lox: -------------------------------------------------------------------------------- 1 | class Err { 2 | init() { 3 | this.x = 3 / 0; 4 | } 5 | } 6 | 7 | Err(); -------------------------------------------------------------------------------- /tests/programs/class_method.lox: -------------------------------------------------------------------------------- 1 | class Math { 2 | class square(n) { 3 | return n * n; 4 | } 5 | } 6 | 7 | print Math.square(2); -------------------------------------------------------------------------------- /tests/programs/sayHi.lox: -------------------------------------------------------------------------------- 1 | fun sayHi(first, last) { 2 | print "Hi, " + first + " " + last + "!"; 3 | } 4 | 5 | sayHi("Valued", "Customer"); -------------------------------------------------------------------------------- /tests/programs/constructor_returns_this.lox: -------------------------------------------------------------------------------- 1 | class Ret { 2 | init() { 3 | this.x = 12; 4 | } 5 | } 6 | 7 | var o = Ret(); 8 | print o.init(); -------------------------------------------------------------------------------- /tests/programs/lambda.lox: -------------------------------------------------------------------------------- 1 | fun thrice(fn) { 2 | for (var i = 1; i <= 3; i = i + 1) { 3 | fn(i); 4 | } 5 | } 6 | 7 | thrice(fun (a) { 8 | print a; 9 | }); -------------------------------------------------------------------------------- /tests/programs/fib.lox: -------------------------------------------------------------------------------- 1 | fun fib(n) { 2 | if (n <= 1) return n; 3 | return fib(n - 2) + fib(n - 1); 4 | } 5 | 6 | for (var i = 18; i < 20; i = i + 1) { 7 | print fib(i); 8 | } -------------------------------------------------------------------------------- /macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "macros" 4 | version = "0.1.0" 5 | 6 | [dependencies] 7 | quote = "1.0.21" 8 | syn = "1.0.99" 9 | 10 | [lib] 11 | proc-macro = true 12 | -------------------------------------------------------------------------------- /tests/programs/binding.lox: -------------------------------------------------------------------------------- 1 | var a = "global"; 2 | 3 | { 4 | fun showA() { 5 | print a; 6 | } 7 | 8 | showA(); 9 | var a = "block"; 10 | showA(); 11 | print a; 12 | } -------------------------------------------------------------------------------- /tests/programs/getter.lox: -------------------------------------------------------------------------------- 1 | class Square { 2 | init(x) { 3 | this.x = x; 4 | } 5 | 6 | area { 7 | return this.x * this.x; 8 | } 9 | } 10 | 11 | print Square(4).area; -------------------------------------------------------------------------------- /tests/programs/constructor.lox: -------------------------------------------------------------------------------- 1 | class Plus { 2 | init(n) { 3 | this.n = n; 4 | } 5 | 6 | add(n) { 7 | return n + this.n; 8 | } 9 | } 10 | 11 | var two = Plus(2); 12 | print two.add(3); -------------------------------------------------------------------------------- /tests/programs/simple_class.lox: -------------------------------------------------------------------------------- 1 | class Bacon { 2 | eat() { 3 | print "Crunch crunch crunch!"; 4 | } 5 | } 6 | 7 | Bacon().eat(); 8 | 9 | var a = Bacon(); 10 | var b = Bacon(); 11 | a.b = b; 12 | a.b.eat(); -------------------------------------------------------------------------------- /tests/programs/counter.lox: -------------------------------------------------------------------------------- 1 | fun makeCounter() { 2 | var i = 0; 3 | fun count() { 4 | i = i + 1; 5 | print i; 6 | } 7 | 8 | return count; 9 | } 10 | 11 | var counter = makeCounter(); 12 | counter(); 13 | counter(); -------------------------------------------------------------------------------- /tests/programs/constructor_returns_this_early.lox: -------------------------------------------------------------------------------- 1 | class C { 2 | init(x) { 3 | this.x = x; 4 | return; 5 | this.x = 200; 6 | } 7 | } 8 | 9 | print C(3); 10 | print C(5).x; 11 | print C(6).init(7); 12 | print C(6).init(7).x; -------------------------------------------------------------------------------- /tests/programs/simple_method.lox: -------------------------------------------------------------------------------- 1 | class Greeter { 2 | hello() { 3 | print "Hi, I'm " + this.name; 4 | } 5 | 6 | setName(name) { 7 | this.name = name; 8 | } 9 | } 10 | 11 | var g = Greeter(); 12 | g.setName("Wally"); 13 | g.hello(); -------------------------------------------------------------------------------- /docs/pkg/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jlox-rs", 3 | "version": "0.1.0", 4 | "files": [ 5 | "jlox_rs_lib_bg.wasm", 6 | "jlox_rs_lib.js", 7 | "jlox_rs_lib.d.ts" 8 | ], 9 | "module": "jlox_rs_lib.js", 10 | "types": "jlox_rs_lib.d.ts", 11 | "sideEffects": false 12 | } -------------------------------------------------------------------------------- /tests/programs/callback_from_method.lox: -------------------------------------------------------------------------------- 1 | class Thing { 2 | getCallback() { 3 | fun localFunction(arg) { 4 | print this.field + " " + arg; 5 | } 6 | return localFunction; 7 | } 8 | } 9 | 10 | var thing = Thing(); 11 | var callback = thing.getCallback(); 12 | thing.field = "one"; 13 | callback("two"); -------------------------------------------------------------------------------- /docs/pkg/jlox_rs_lib_bg.wasm.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | export const memory: WebAssembly.Memory; 4 | export function run(a: number, b: number, c: number): void; 5 | export function __wbindgen_add_to_stack_pointer(a: number): number; 6 | export function __wbindgen_malloc(a: number): number; 7 | export function __wbindgen_realloc(a: number, b: number, c: number): number; 8 | export function __wbindgen_free(a: number, b: number): void; 9 | -------------------------------------------------------------------------------- /tests/programs/inherit_methods.lox: -------------------------------------------------------------------------------- 1 | class Parent { 2 | init(n) { 3 | this.n = n; 4 | } 5 | 6 | class four() { 7 | return 4; 8 | } 9 | 10 | plus(n) { 11 | return this.n + n; 12 | } 13 | 14 | squared { 15 | return this.n * this.n; 16 | } 17 | } 18 | 19 | class Child < Parent {} 20 | 21 | var x = Child(10); 22 | print x.n; 23 | print Child.four(); 24 | print x.plus(5); 25 | print x.squared; -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "jlox-rs" 4 | version = "0.1.0" 5 | 6 | [dependencies] 7 | derivative = "2.2.0" 8 | macros = {path = "./macros"} 9 | paste = "1.0.8" 10 | thiserror = "1.0.32" 11 | 12 | [dev-dependencies] 13 | assert_cmd = "2.0.4" 14 | 15 | [[bin]] 16 | name = "jlox_rs" 17 | path = "src/main.rs" 18 | 19 | [lib] 20 | crate-type = ["cdylib", "rlib"] 21 | name = "jlox_rs_lib" 22 | 23 | [target.'cfg(target_arch = "wasm32")'.dependencies] 24 | console_error_panic_hook = "0.1.7" 25 | wasm-bindgen = "0.2.83" 26 | -------------------------------------------------------------------------------- /tests/programs/super.lox: -------------------------------------------------------------------------------- 1 | class A { 2 | method() { 3 | return "A method"; 4 | } 5 | 6 | getter { 7 | return "A getter"; 8 | } 9 | } 10 | 11 | class B < A { 12 | method() { 13 | return "B method"; 14 | } 15 | 16 | getter { 17 | return "B getter"; 18 | } 19 | 20 | testMethod() { 21 | return super.method(); 22 | } 23 | 24 | testGetter() { 25 | return super.getter; 26 | } 27 | } 28 | 29 | class C < B {} 30 | 31 | print C().testMethod(); 32 | print C().testGetter(); -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use jlox_rs_lib::{Error, Lox}; 2 | 3 | fn main() { 4 | // Yes really manual parsing, if the book says to use a (Java) library I'll use Clap, until then we. do. it. by. The. BOOK. 5 | let args: Vec = std::env::args().collect(); 6 | 7 | #[allow(clippy::comparison_chain)] 8 | if args.len() > 2 { 9 | println!("Usage: {} [script]", args[0]); 10 | std::process::exit(64); 11 | } else if args.len() == 2 { 12 | if let Err(e) = Lox::new().run_file(&args[1], Box::new(std::io::stdout())) { 13 | eprintln!("{}", e); 14 | std::process::exit(match e { 15 | Error::Runtime(_) => 70, 16 | Error::Io(_) => 74, 17 | _ => 65, 18 | }); 19 | } 20 | } else { 21 | Lox::new().run_prompt(Box::new(std::io::stdout())).unwrap(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /docs/styles.css: -------------------------------------------------------------------------------- 1 | body, 2 | html { 3 | height: 100%; 4 | } 5 | 6 | .main-container { 7 | display: flex; 8 | flex-direction: column; 9 | 10 | align-items: stretch; 11 | align-content: stretch; 12 | 13 | height: 100%; 14 | } 15 | 16 | .code-container { 17 | display: flex; 18 | flex-direction: row; 19 | flex: 2; 20 | 21 | align-items: stretch; 22 | align-content: stretch; 23 | } 24 | 25 | .code, 26 | .output { 27 | flex: 0.5; 28 | } 29 | .code { 30 | border: 1px solid silver; 31 | } 32 | 33 | .controls { 34 | display: flex; 35 | flex-direction: row; 36 | 37 | align-items: center; 38 | 39 | padding: 15px; 40 | background-color: lightgray; 41 | } 42 | 43 | .controls > :not(:last-child) { 44 | margin-right: 15px; 45 | } 46 | 47 | .examples { 48 | width: fit-content; 49 | } 50 | 51 | #help-button { 52 | background-color: #eee; 53 | color: black; 54 | } 55 | 56 | button, 57 | select { 58 | cursor: pointer; 59 | } 60 | 61 | code { 62 | white-space: nowrap; 63 | } 64 | -------------------------------------------------------------------------------- /macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use quote::quote; 3 | use syn::{parse_macro_input, DeriveInput}; 4 | 5 | #[proc_macro_derive(ResolveErrorLocation)] 6 | pub fn derive_resolve_location(input: TokenStream) -> TokenStream { 7 | let input = parse_macro_input!(input as DeriveInput); 8 | let ty = input.ident; 9 | 10 | let expanded = if let syn::Data::Enum(data) = &input.data { 11 | let variant = data.variants.iter().map(|v| &v.ident); 12 | quote! { 13 | impl crate::types::ResolveErrorLocation for #ty { 14 | fn resolve(&mut self, source: &[u8]) { 15 | match self { 16 | #( 17 | Self::#variant { ref mut location, .. } => { location.resolve(source); } 18 | ),* 19 | }; 20 | } 21 | } 22 | } 23 | } else { 24 | quote! { 25 | compile_error!("ResolveErrorLocation derive rule only supports enums") 26 | } 27 | }; 28 | 29 | TokenStream::from(expanded) 30 | } 31 | -------------------------------------------------------------------------------- /docs/pkg/jlox_rs_lib.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * @param {string} program 5 | * @returns {string} 6 | */ 7 | export function run(program: string): string; 8 | 9 | export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module; 10 | 11 | export interface InitOutput { 12 | readonly memory: WebAssembly.Memory; 13 | readonly run: (a: number, b: number, c: number) => void; 14 | readonly __wbindgen_add_to_stack_pointer: (a: number) => number; 15 | readonly __wbindgen_malloc: (a: number) => number; 16 | readonly __wbindgen_realloc: (a: number, b: number, c: number) => number; 17 | readonly __wbindgen_free: (a: number, b: number) => void; 18 | } 19 | 20 | export type SyncInitInput = BufferSource | WebAssembly.Module; 21 | /** 22 | * Instantiates the given `module`, which can either be bytes or 23 | * a precompiled `WebAssembly.Module`. 24 | * 25 | * @param {SyncInitInput} module 26 | * 27 | * @returns {InitOutput} 28 | */ 29 | export function initSync(module: SyncInitInput): InitOutput; 30 | 31 | /** 32 | * If `module_or_path` is {RequestInfo} or {URL}, makes a request and 33 | * for everything else, calls `WebAssembly.instantiate` directly. 34 | * 35 | * @param {InitInput | Promise} module_or_path 36 | * 37 | * @returns {Promise} 38 | */ 39 | export default function init (module_or_path?: InitInput | Promise): Promise; 40 | -------------------------------------------------------------------------------- /docs/examples.js: -------------------------------------------------------------------------------- 1 | const examples = { 2 | greeter: `class Greeter { 3 | init(name) { 4 | this.name = name; 5 | } 6 | 7 | greet(you) { 8 | print "Hi " + you + ", I'm " + this.name; 9 | } 10 | } 11 | 12 | var g = Greeter("jlox-rs"); 13 | g.greet("esteemed user"); 14 | `, 15 | 16 | closures: `fun makeCounter() { 17 | // \`i\` will be captured in the closure of \`count\` 18 | var i = 0; 19 | fun count() { 20 | i = i + 1; 21 | print i; 22 | } 23 | 24 | // Yes we can return a function from a function! 25 | return count; 26 | } 27 | 28 | var counter = makeCounter(); 29 | counter(); 30 | counter();`, 31 | 32 | advanced_classes: `class Math { 33 | // This is a static method 34 | class squared(n) { 35 | return n * n; 36 | } 37 | } 38 | 39 | class Rectangle { 40 | init(a, b) { 41 | this.a = a; 42 | this.b = b; 43 | } 44 | 45 | // Note the lack of parentheses; this is a getter 46 | area { 47 | return this.a * this.b; 48 | } 49 | 50 | // Not a special method 51 | to_string() { 52 | return "Rectangle(" + this.a + ", " + this.b + ")"; 53 | } 54 | } 55 | 56 | // Inheritance 57 | class Square < Rectangle { 58 | init(a) { 59 | this.a = a; 60 | } 61 | 62 | b { 63 | return this.a; 64 | } 65 | 66 | to_string() { 67 | // Note the \`super\` call 68 | return super.to_string() + "/Square"; 69 | } 70 | } 71 | 72 | print Rectangle(1, 2).area; 73 | print Square(4).area == Math.squared(4); 74 | print Square(5).to_string();`, 75 | }; 76 | 77 | export default examples; 78 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode,git 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=rust,visualstudiocode,git 3 | 4 | ### Git ### 5 | # Created by git for backups. To disable backups in Git: 6 | # $ git config --global mergetool.keepBackup false 7 | *.orig 8 | 9 | # Created by git when using merge tools for conflicts 10 | *.BACKUP.* 11 | *.BASE.* 12 | *.LOCAL.* 13 | *.REMOTE.* 14 | *_BACKUP_*.txt 15 | *_BASE_*.txt 16 | *_LOCAL_*.txt 17 | *_REMOTE_*.txt 18 | 19 | ### Rust ### 20 | # Generated by Cargo 21 | # will have compiled files and executables 22 | debug/ 23 | target/ 24 | 25 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 26 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 27 | Cargo.lock 28 | 29 | # These are backup files generated by rustfmt 30 | **/*.rs.bk 31 | 32 | # MSVC Windows builds of rustc generate these, which store debugging information 33 | *.pdb 34 | 35 | ### VisualStudioCode ### 36 | .vscode/* 37 | !.vscode/settings.json 38 | !.vscode/tasks.json 39 | !.vscode/launch.json 40 | !.vscode/extensions.json 41 | !.vscode/*.code-snippets 42 | 43 | # Local History for Visual Studio Code 44 | .history/ 45 | 46 | # Built Visual Studio Code Extensions 47 | *.vsix 48 | 49 | ### VisualStudioCode Patch ### 50 | # Ignore all local history of files 51 | .history 52 | .ionide 53 | 54 | # Support for Project snippet scope 55 | .vscode/*.code-snippets 56 | 57 | # Ignore code-workspaces 58 | *.code-workspace 59 | 60 | # End of https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode,git 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jlox-rs 2 | 3 | > Following along the first (Java) part of https://craftinginterpreters.com/, but with Rust, because reasons. 4 | 5 | Status: done! 6 | 7 | Check out the interpreter in action in your browser at https://abesto.github.io/jlox-rs/ (and click "What am I looking at?" for tons of details) 8 | 9 | ## Performance 10 | 11 | About half as fast as the canonical `jlox` implementation. Considering that's Java and this is Rust, this is somewhat surprising. Considering this is the first time I ever build an interpreter, and did zero optimization, this is very surprising, but in the opposite direction. 12 | 13 | Benchmark script: 14 | 15 | ``` 16 | fun fib(n) { 17 | if (n < 2) return n; 18 | return fib(n - 1) + fib(n - 2); 19 | } 20 | 21 | print fib(30); 22 | ``` 23 | 24 | Benchmark results: 25 | 26 | ``` 27 | $ hyperfine --warmup 1 '../craftinginterpreters/jlox ./fib.lox' './target/release/jlox_rs ./fib.lox' 28 | Benchmark 1: ../craftinginterpreters/jlox ./fib.lox 29 | Time (mean ± σ): 855.8 ms ± 196.5 ms [User: 1134.4 ms, System: 106.2 ms] 30 | Range (min … max): 695.4 ms … 1151.9 ms 10 runs 31 | 32 | Benchmark 2: ./target/release/jlox_rs ./fib.lox 33 | Time (mean ± σ): 2.016 s ± 0.026 s [User: 2.011 s, System: 0.002 s] 34 | Range (min … max): 1.982 s … 2.057 s 10 runs 35 | 36 | Summary 37 | '../craftinginterpreters/jlox ./fib.lox' ran 38 | 2.36 ± 0.54 times faster than './target/release/jlox_rs ./fib.lox' 39 | ``` 40 | 41 | ## Correctness 42 | 43 | I added my own tests as I went, and then at the very end found that https://github.com/munificent/craftinginterpreters contains a full test suite. Running it against this implementation, I see 100 passes and 139 failures. A ton of the failures are due to error message formatting. A handful might not be, but honestly that's fine for this hobby project. I'll probably use those tests for `clox-rs` though! 44 | -------------------------------------------------------------------------------- /.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 executable 'jlox-rs'", 11 | "cargo": { 12 | "args": [ 13 | "build", 14 | "--bin=jlox-rs", 15 | "--package=jlox-rs" 16 | ], 17 | "filter": { 18 | "name": "jlox-rs", 19 | "kind": "bin" 20 | } 21 | }, 22 | "args": [ 23 | "tests/programs/counter.lox" 24 | ], 25 | "cwd": "${workspaceFolder}" 26 | }, 27 | { 28 | "type": "lldb", 29 | "request": "launch", 30 | "name": "Debug unit tests in executable 'jlox-rs'", 31 | "cargo": { 32 | "args": [ 33 | "test", 34 | "--no-run", 35 | "--bin=jlox-rs", 36 | "--package=jlox-rs" 37 | ], 38 | "filter": { 39 | "name": "jlox-rs", 40 | "kind": "bin" 41 | } 42 | }, 43 | "args": [], 44 | "cwd": "${workspaceFolder}" 45 | }, 46 | { 47 | "type": "lldb", 48 | "request": "launch", 49 | "name": "Debug integration test 'test_integration'", 50 | "cargo": { 51 | "args": [ 52 | "test", 53 | "--no-run", 54 | "--test=test_integration", 55 | "--package=jlox-rs" 56 | ], 57 | "filter": { 58 | "name": "test_integration", 59 | "kind": "test" 60 | } 61 | }, 62 | "args": [], 63 | "cwd": "${workspaceFolder}" 64 | } 65 | ] 66 | } -------------------------------------------------------------------------------- /docs/loxMonarchTokensProvider.js: -------------------------------------------------------------------------------- 1 | function makeTokensProvider() { 2 | return { 3 | keywords: [ 4 | "and", 5 | "break", 6 | "class", 7 | "else", 8 | "false", 9 | "fun", 10 | "for", 11 | "if", 12 | "nil", 13 | "or", 14 | "print", 15 | "return", 16 | "super", 17 | "this", 18 | "true", 19 | "var", 20 | "while", 21 | "init", 22 | ], 23 | 24 | operators: [ 25 | ":", 26 | "-", 27 | "+", 28 | "?", 29 | "/", 30 | "*", 31 | "!", 32 | "!=", 33 | "=", 34 | "==", 35 | ">", 36 | ">=", 37 | "<", 38 | "<=", 39 | ",", 40 | ], 41 | 42 | symbols: /[=> Self { 22 | Self::Offset { command, offset } 23 | } 24 | 25 | pub fn resolve(&mut self, source: &[u8]) { 26 | if let Self::Offset { command, offset } = self { 27 | let mut character = 0; 28 | let mut line = 0; 29 | 30 | for c in source.iter().take(*offset) { 31 | if c == &b'\n' { 32 | character = 0; 33 | line += 1; 34 | } else { 35 | character += 1; 36 | } 37 | } 38 | 39 | *self = Self::Resolved { 40 | command: *command, 41 | character, 42 | line, 43 | }; 44 | } 45 | } 46 | 47 | pub fn command(&self) -> CommandIndex { 48 | match self { 49 | Self::Offset { command, .. } | Self::Resolved { command, .. } => *command, 50 | } 51 | } 52 | } 53 | 54 | pub trait ResolveErrorLocation { 55 | fn resolve(&mut self, source: &[u8]); 56 | } 57 | 58 | impl ResolveErrorLocation for Vec { 59 | fn resolve(&mut self, source: &[u8]) { 60 | for e in self { 61 | e.resolve(source); 62 | } 63 | } 64 | } 65 | 66 | pub struct ErrorLocationResolver<'a> { 67 | source: &'a [u8], 68 | } 69 | 70 | impl<'a> ErrorLocationResolver<'a> { 71 | #[must_use] 72 | pub fn new(source: &'a [u8]) -> Self { 73 | Self { source } 74 | } 75 | 76 | pub fn resolve(&self, mut e: E) -> E 77 | where 78 | E: ResolveErrorLocation, 79 | { 80 | e.resolve(self.source); 81 | e 82 | } 83 | } 84 | 85 | impl std::fmt::Display for SourceLocation { 86 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 87 | match self { 88 | Self::Resolved { 89 | line, character, .. 90 | } => write!(f, "{}:{}", line, character), 91 | Self::Offset { offset, .. } => write!(f, "byte {}", offset), 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /docs/pkg/jlox_rs_lib_bg.js: -------------------------------------------------------------------------------- 1 | import * as wasm from './jlox_rs_lib_bg.wasm'; 2 | 3 | let WASM_VECTOR_LEN = 0; 4 | 5 | let cachedUint8Memory0 = new Uint8Array(); 6 | 7 | function getUint8Memory0() { 8 | if (cachedUint8Memory0.byteLength === 0) { 9 | cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); 10 | } 11 | return cachedUint8Memory0; 12 | } 13 | 14 | const lTextEncoder = typeof TextEncoder === 'undefined' ? (0, module.require)('util').TextEncoder : TextEncoder; 15 | 16 | let cachedTextEncoder = new lTextEncoder('utf-8'); 17 | 18 | const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' 19 | ? function (arg, view) { 20 | return cachedTextEncoder.encodeInto(arg, view); 21 | } 22 | : function (arg, view) { 23 | const buf = cachedTextEncoder.encode(arg); 24 | view.set(buf); 25 | return { 26 | read: arg.length, 27 | written: buf.length 28 | }; 29 | }); 30 | 31 | function passStringToWasm0(arg, malloc, realloc) { 32 | 33 | if (realloc === undefined) { 34 | const buf = cachedTextEncoder.encode(arg); 35 | const ptr = malloc(buf.length); 36 | getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf); 37 | WASM_VECTOR_LEN = buf.length; 38 | return ptr; 39 | } 40 | 41 | let len = arg.length; 42 | let ptr = malloc(len); 43 | 44 | const mem = getUint8Memory0(); 45 | 46 | let offset = 0; 47 | 48 | for (; offset < len; offset++) { 49 | const code = arg.charCodeAt(offset); 50 | if (code > 0x7F) break; 51 | mem[ptr + offset] = code; 52 | } 53 | 54 | if (offset !== len) { 55 | if (offset !== 0) { 56 | arg = arg.slice(offset); 57 | } 58 | ptr = realloc(ptr, len, len = offset + arg.length * 3); 59 | const view = getUint8Memory0().subarray(ptr + offset, ptr + len); 60 | const ret = encodeString(arg, view); 61 | 62 | offset += ret.written; 63 | } 64 | 65 | WASM_VECTOR_LEN = offset; 66 | return ptr; 67 | } 68 | 69 | let cachedInt32Memory0 = new Int32Array(); 70 | 71 | function getInt32Memory0() { 72 | if (cachedInt32Memory0.byteLength === 0) { 73 | cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); 74 | } 75 | return cachedInt32Memory0; 76 | } 77 | 78 | const lTextDecoder = typeof TextDecoder === 'undefined' ? (0, module.require)('util').TextDecoder : TextDecoder; 79 | 80 | let cachedTextDecoder = new lTextDecoder('utf-8', { ignoreBOM: true, fatal: true }); 81 | 82 | cachedTextDecoder.decode(); 83 | 84 | function getStringFromWasm0(ptr, len) { 85 | return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); 86 | } 87 | /** 88 | * @param {string} program 89 | * @returns {string} 90 | */ 91 | export function run(program) { 92 | try { 93 | const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); 94 | const ptr0 = passStringToWasm0(program, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 95 | const len0 = WASM_VECTOR_LEN; 96 | wasm.run(retptr, ptr0, len0); 97 | var r0 = getInt32Memory0()[retptr / 4 + 0]; 98 | var r1 = getInt32Memory0()[retptr / 4 + 1]; 99 | return getStringFromWasm0(r0, r1); 100 | } finally { 101 | wasm.__wbindgen_add_to_stack_pointer(16); 102 | wasm.__wbindgen_free(r0, r1); 103 | } 104 | } 105 | 106 | -------------------------------------------------------------------------------- /docs/micromodal.css: -------------------------------------------------------------------------------- 1 | .modal { 2 | display: none; 3 | } 4 | 5 | .modal.is-open { 6 | display: block; 7 | } 8 | 9 | .modal__overlay { 10 | position: fixed; 11 | top: 0; 12 | left: 0; 13 | right: 0; 14 | bottom: 0; 15 | background: rgba(0, 0, 0, 0.6); 16 | display: flex; 17 | justify-content: center; 18 | align-items: center; 19 | } 20 | 21 | .modal__container { 22 | background-color: #fff; 23 | padding: 30px; 24 | max-width: 50%; 25 | max-height: 100vh; 26 | border-radius: 4px; 27 | overflow-y: auto; 28 | box-sizing: border-box; 29 | } 30 | 31 | .modal__header { 32 | display: flex; 33 | justify-content: space-between; 34 | align-items: center; 35 | } 36 | 37 | .modal__title { 38 | margin-top: 0; 39 | margin-bottom: 0; 40 | font-weight: 600; 41 | color: #00449e; 42 | box-sizing: border-box; 43 | } 44 | 45 | .modal__close { 46 | background: transparent; 47 | border: 0; 48 | } 49 | 50 | .modal__header .modal__close:before { 51 | content: "\2715"; 52 | } 53 | 54 | .modal__content { 55 | margin-top: 2rem; 56 | margin-bottom: 2rem; 57 | line-height: 1.5; 58 | color: rgba(0, 0, 0, 0.8); 59 | } 60 | 61 | .modal__btn { 62 | font-size: 0.875rem; 63 | padding-left: 1rem; 64 | padding-right: 1rem; 65 | padding-top: 0.5rem; 66 | padding-bottom: 0.5rem; 67 | background-color: #e6e6e6; 68 | color: rgba(0, 0, 0, 0.8); 69 | border-radius: 0.25rem; 70 | border-style: none; 71 | border-width: 0; 72 | cursor: pointer; 73 | -webkit-appearance: button; 74 | text-transform: none; 75 | overflow: visible; 76 | line-height: 1.15; 77 | margin: 0; 78 | will-change: transform; 79 | -moz-osx-font-smoothing: grayscale; 80 | -webkit-backface-visibility: hidden; 81 | backface-visibility: hidden; 82 | -webkit-transform: translateZ(0); 83 | transform: translateZ(0); 84 | transition: -webkit-transform 0.25s ease-out; 85 | transition: transform 0.25s ease-out; 86 | transition: transform 0.25s ease-out, -webkit-transform 0.25s ease-out; 87 | } 88 | 89 | .modal__btn:focus, 90 | .modal__btn:hover { 91 | -webkit-transform: scale(1.05); 92 | transform: scale(1.05); 93 | } 94 | 95 | .modal__btn-primary { 96 | background-color: #00449e; 97 | color: #fff; 98 | } 99 | 100 | /**************************\ 101 | Demo Animation Style 102 | \**************************/ 103 | @keyframes mmfadeIn { 104 | from { 105 | opacity: 0; 106 | } 107 | to { 108 | opacity: 1; 109 | } 110 | } 111 | 112 | @keyframes mmfadeOut { 113 | from { 114 | opacity: 1; 115 | } 116 | to { 117 | opacity: 0; 118 | } 119 | } 120 | 121 | @keyframes mmslideIn { 122 | from { 123 | transform: translateY(15%); 124 | } 125 | to { 126 | transform: translateY(0); 127 | } 128 | } 129 | 130 | @keyframes mmslideOut { 131 | from { 132 | transform: translateY(0); 133 | } 134 | to { 135 | transform: translateY(-10%); 136 | } 137 | } 138 | 139 | .micromodal-slide { 140 | display: none; 141 | } 142 | 143 | .micromodal-slide.is-open { 144 | display: block; 145 | } 146 | 147 | .micromodal-slide[aria-hidden="false"] .modal__overlay { 148 | animation: mmfadeIn 0.3s cubic-bezier(0, 0, 0.2, 1); 149 | } 150 | 151 | .micromodal-slide[aria-hidden="false"] .modal__container { 152 | animation: mmslideIn 0.3s cubic-bezier(0, 0, 0.2, 1); 153 | } 154 | 155 | .micromodal-slide[aria-hidden="true"] .modal__overlay { 156 | animation: mmfadeOut 0.3s cubic-bezier(0, 0, 0.2, 1); 157 | } 158 | 159 | .micromodal-slide[aria-hidden="true"] .modal__container { 160 | animation: mmslideOut 0.3s cubic-bezier(0, 0, 0.2, 1); 161 | } 162 | 163 | .micromodal-slide .modal__container, 164 | .micromodal-slide .modal__overlay { 165 | will-change: transform; 166 | } 167 | -------------------------------------------------------------------------------- /src/token.rs: -------------------------------------------------------------------------------- 1 | use crate::types::{Number, SourceLocation}; 2 | 3 | #[derive(Debug, PartialEq, Clone)] 4 | pub enum TokenValue { 5 | // Single-character tokens. 6 | LeftParen, 7 | RightParen, 8 | LeftBrace, 9 | RightBrace, 10 | Colon, 11 | Comma, 12 | Dot, 13 | Minus, 14 | Plus, 15 | Question, 16 | Semicolon, 17 | Slash, 18 | Star, 19 | 20 | // One or two character tokens. 21 | Bang, 22 | BangEqual, 23 | Equal, 24 | EqualEqual, 25 | Greater, 26 | GreaterEqual, 27 | Less, 28 | LessEqual, 29 | 30 | // Literals. 31 | Identifier(String), 32 | String(String), 33 | Number(Number), 34 | 35 | // Keywords. 36 | And, 37 | Break, 38 | Class, 39 | Else, 40 | False, 41 | Fun, 42 | For, 43 | If, 44 | Nil, 45 | Or, 46 | Print, 47 | Return, 48 | Super, 49 | This, 50 | True, 51 | Var, 52 | While, 53 | 54 | Eof, 55 | } 56 | 57 | #[derive(Debug, Clone, PartialEq)] 58 | pub struct Token { 59 | pub value: TokenValue, 60 | pub lexeme: String, 61 | pub location: SourceLocation, 62 | } 63 | 64 | impl std::fmt::Display for TokenValue { 65 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 66 | match &self { 67 | TokenValue::LeftParen => f.write_str("("), 68 | TokenValue::RightParen => f.write_str(")"), 69 | TokenValue::LeftBrace => f.write_str("{"), 70 | TokenValue::RightBrace => f.write_str("}"), 71 | TokenValue::Colon => f.write_str(":"), 72 | TokenValue::Comma => f.write_str(","), 73 | TokenValue::Dot => f.write_str("."), 74 | TokenValue::Minus => f.write_str("-"), 75 | TokenValue::Plus => f.write_str("+"), 76 | TokenValue::Question => f.write_str("?"), 77 | TokenValue::Semicolon => f.write_str(";"), 78 | TokenValue::Slash => f.write_str("/"), 79 | TokenValue::Star => f.write_str("*"), 80 | TokenValue::Bang => f.write_str("!"), 81 | TokenValue::BangEqual => f.write_str("!="), 82 | TokenValue::Equal => f.write_str("="), 83 | TokenValue::EqualEqual => f.write_str("=="), 84 | TokenValue::Greater => f.write_str(">"), 85 | TokenValue::GreaterEqual => f.write_str(">="), 86 | TokenValue::Less => f.write_str("<"), 87 | TokenValue::LessEqual => f.write_str("<="), 88 | TokenValue::Identifier(s) => f.write_str(s), 89 | TokenValue::String(s) => s.fmt(f), 90 | TokenValue::Number(n) => n.fmt(f), 91 | TokenValue::And => f.write_str("and"), 92 | TokenValue::Break => f.write_str("break"), 93 | TokenValue::Class => f.write_str("class"), 94 | TokenValue::Else => f.write_str("else"), 95 | TokenValue::False => f.write_str("false"), 96 | TokenValue::Fun => f.write_str("fun"), 97 | TokenValue::For => f.write_str("for"), 98 | TokenValue::If => f.write_str("if"), 99 | TokenValue::Nil => f.write_str("nil"), 100 | TokenValue::Or => f.write_str("or"), 101 | TokenValue::Print => f.write_str("print"), 102 | TokenValue::Return => f.write_str("return"), 103 | TokenValue::Super => f.write_str("super"), 104 | TokenValue::This => f.write_str("this"), 105 | TokenValue::True => f.write_str("true"), 106 | TokenValue::Var => f.write_str("var"), 107 | TokenValue::While => f.write_str("while"), 108 | TokenValue::Eof => f.write_str("\\d"), 109 | } 110 | } 111 | } 112 | 113 | impl std::fmt::Display for Token { 114 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 115 | self.value.fmt(f) 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/environment.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::RefCell, collections::HashMap, rc::Rc}; 2 | 3 | use crate::{interpreter::Value, resolver::Binding, token::Token}; 4 | 5 | #[derive(Debug, PartialEq)] 6 | pub enum Variable { 7 | Uninitialized, 8 | Value(Rc>), 9 | } 10 | 11 | impl From>>> for Variable { 12 | fn from(x: Option>>) -> Self { 13 | match x { 14 | Some(v) => Self::Value(v), 15 | None => Self::Uninitialized, 16 | } 17 | } 18 | } 19 | 20 | impl std::fmt::Display for Variable { 21 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 22 | match self { 23 | Variable::Uninitialized => write!(f, ""), 24 | Variable::Value(v) => write!(f, "{}", v.borrow()), 25 | } 26 | } 27 | } 28 | #[derive(PartialEq, Debug, Default)] 29 | pub struct GlobalEnvironment { 30 | globals: HashMap>>, 31 | } 32 | 33 | #[derive(PartialEq, Debug, Default)] 34 | pub struct LocalEnvironment { 35 | data: Vec>>, 36 | parent: Option>>, 37 | } 38 | 39 | impl GlobalEnvironment { 40 | #[must_use] 41 | pub fn new() -> Self { 42 | Self::default() 43 | } 44 | 45 | pub fn define(&mut self, name: S, value: Option>>) { 46 | self.globals 47 | .insert(name.to_string(), Rc::new(RefCell::new(value.into()))); 48 | } 49 | 50 | #[must_use] 51 | pub fn assign(&mut self, name: &Token, value: Option>>) -> bool { 52 | if !self.globals.contains_key(&name.lexeme) { 53 | return false; 54 | } 55 | 56 | self.globals 57 | .insert(name.lexeme.clone(), Rc::new(RefCell::new(value.into()))); 58 | true 59 | } 60 | 61 | pub fn get(&self, name: &Token) -> Option>> { 62 | self.globals.get(&name.lexeme).map(Rc::clone) 63 | } 64 | } 65 | 66 | impl LocalEnvironment { 67 | pub fn new(parent: Option>>) -> Self { 68 | Self { 69 | parent, 70 | ..Default::default() 71 | } 72 | } 73 | 74 | pub fn nested(parent: Option>>, f: F) -> T 75 | where 76 | F: FnOnce(Rc>) -> T, 77 | { 78 | let child = Self::new(parent); 79 | f(Rc::new(RefCell::new(child))) 80 | } 81 | 82 | pub fn get_parent(&self) -> Option>> { 83 | self.parent.as_ref().map(Rc::clone) 84 | } 85 | 86 | // Possible optimization: remember (in `Resolver`?) the number of variables per scope, 87 | // create `self.data` accordingly 88 | pub fn assign(&mut self, binding: &Binding, value: Option>>) { 89 | if binding.scopes_up > 0 { 90 | return self 91 | .parent 92 | .as_mut() 93 | .expect("Ran out of assign scopes :(") 94 | .borrow_mut() 95 | .assign(&binding.less_one_scope(), value); 96 | } 97 | 98 | // There's room for micro-optimizations here, but w/e TBH 99 | while self.data.len() <= binding.index_in_scope { 100 | self.data 101 | .push(Rc::new(RefCell::new(Variable::Uninitialized))); 102 | } 103 | self.data[binding.index_in_scope] = Rc::new(RefCell::new(value.into())); 104 | } 105 | 106 | pub fn get(&self, binding: &Binding) -> Rc> { 107 | if binding.scopes_up > 0 { 108 | return self 109 | .parent 110 | .as_ref() 111 | .expect("Ran out of get scopes :(") 112 | .borrow() 113 | .get(&binding.less_one_scope()); 114 | } 115 | Rc::clone(&self.data[binding.index_in_scope]) 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::io::Write; 3 | use std::rc::Rc; 4 | use std::time::{SystemTime, UNIX_EPOCH}; 5 | 6 | use thiserror::Error; 7 | 8 | mod ast; 9 | mod environment; 10 | mod interpreter; 11 | mod parser; 12 | mod resolver; 13 | mod scanner; 14 | mod token; 15 | mod types; 16 | 17 | use crate::environment::GlobalEnvironment; 18 | use crate::interpreter::{Interpreter, NativeFunction, Value}; 19 | use crate::parser::Parser; 20 | use crate::resolver::{Resolver, ResolverConfig}; 21 | use crate::scanner::Scanner; 22 | use crate::types::ErrorLocationResolver; 23 | 24 | #[derive(Debug, Error)] 25 | pub enum Error { 26 | #[error("{}Scanning failed, see errors above.", .0.iter().map(|e| format!("{}\n", e)).collect::())] 27 | Scanner(Vec), 28 | 29 | #[error("{}Parsing failed, see errors above.", .0.iter().map(|e| format!("{}\n", e)).collect::())] 30 | Parser(Vec), 31 | 32 | #[error("{}Variable resolution failed, see errors above.", .0.iter().map(|e| format!("{}\n", e)).collect::())] 33 | Resolver(Vec), 34 | 35 | // Box because Clippy says the sizes of variants is otherwise large 36 | #[error(transparent)] 37 | Runtime(#[from] Box), 38 | 39 | #[error(transparent)] 40 | Io(#[from] std::io::Error), 41 | } 42 | 43 | type Result = std::result::Result; 44 | type Output<'a> = Box; 45 | 46 | pub struct Lox {} 47 | 48 | impl Lox { 49 | pub fn new() -> Self { 50 | Self {} 51 | } 52 | 53 | fn prepare_global_env() -> Rc> { 54 | let mut env = GlobalEnvironment::new(); 55 | env.define( 56 | "clock", 57 | Some(Rc::new(RefCell::new(Value::NativeFunction( 58 | NativeFunction { 59 | name: "clock".to_string(), 60 | arity: 0, 61 | fun: |_, _| { 62 | Value::Number( 63 | SystemTime::now() 64 | .duration_since(UNIX_EPOCH) 65 | .unwrap() 66 | .as_secs_f64(), 67 | ) 68 | }, 69 | }, 70 | )))), 71 | ); 72 | 73 | env.define( 74 | "type", 75 | Some(Rc::new(RefCell::new(Value::NativeFunction( 76 | NativeFunction { 77 | name: "type".to_string(), 78 | arity: 1, 79 | fun: |_, args| Value::String(args[0].borrow().type_of()), 80 | }, 81 | )))), 82 | ); 83 | 84 | Rc::new(RefCell::new(env)) 85 | } 86 | 87 | pub fn run_file(&mut self, path: &str, output: Output) -> Result { 88 | let contents = std::fs::read(path)?; 89 | self.run_program(contents, output) 90 | } 91 | 92 | pub fn run_program<'a>(&mut self, program: Vec, output: Output<'a>) -> Result { 93 | self.run( 94 | &mut Interpreter::<'a>::new(Self::prepare_global_env(), output), 95 | ResolverConfig { 96 | error_on_unused_locals: true, 97 | }, 98 | program, 99 | )?; 100 | Ok(()) 101 | } 102 | 103 | pub fn run_prompt(&mut self, output: Output) -> Result<()> { 104 | let mut interpreter = Interpreter::new(Self::prepare_global_env(), output); 105 | loop { 106 | print!("> "); 107 | std::io::stdout().flush()?; 108 | let mut line = String::new(); 109 | if std::io::stdin().read_line(&mut line)? > 0 { 110 | match self.run( 111 | &mut interpreter, 112 | ResolverConfig { 113 | error_on_unused_locals: false, 114 | }, 115 | line.into_bytes(), 116 | ) { 117 | Err(e) => { 118 | eprintln!("{}", e); 119 | } 120 | Ok(Some(v)) => println!("{}", v), 121 | Ok(None) => (), 122 | } 123 | interpreter.command += 1; 124 | } else { 125 | break; 126 | } 127 | } 128 | Ok(()) 129 | } 130 | 131 | fn run( 132 | &mut self, 133 | interpreter: &mut Interpreter, 134 | resolver_config: ResolverConfig, 135 | source: Vec, 136 | ) -> Result> { 137 | let error_location_resolver = ErrorLocationResolver::new(&source); 138 | 139 | let tokens = Scanner::new(&source, interpreter.command) 140 | .scan_tokens() 141 | .map_err(|e| error_location_resolver.resolve(e)) 142 | .map_err(Error::Scanner)?; 143 | 144 | let program = Parser::new(tokens) 145 | .parse() 146 | .map_err(|e| error_location_resolver.resolve(e)) 147 | .map_err(Error::Parser)?; 148 | 149 | let bindings = Resolver::new(resolver_config) 150 | .resolve(&program) 151 | .map_err(|e| error_location_resolver.resolve(e)) 152 | .map_err(Error::Resolver)?; 153 | interpreter.update_bindings(bindings); 154 | 155 | interpreter 156 | .interpret(&program) 157 | .map_err(|e| error_location_resolver.resolve(e)) 158 | .map_err(|e| Error::Runtime(Box::new(e))) 159 | .map(|opt| opt.map(|v| v.borrow().clone())) 160 | } 161 | } 162 | 163 | impl Default for Lox { 164 | fn default() -> Self { 165 | Self::new() 166 | } 167 | } 168 | 169 | #[cfg(target_arch = "wasm32")] 170 | #[wasm_bindgen::prelude::wasm_bindgen] 171 | pub fn run(program: &str) -> String { 172 | std::panic::set_hook(Box::new(console_error_panic_hook::hook)); 173 | let mut lox = Lox::new(); 174 | let mut output: Vec = Vec::new(); 175 | let result = lox.run_program(program.as_bytes().to_vec(), Box::new(&mut output)); 176 | match result { 177 | Ok(()) => std::str::from_utf8(&output).unwrap().to_string(), 178 | Err(e) => format!("{}", e), 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /docs/pkg/jlox_rs_lib.js: -------------------------------------------------------------------------------- 1 | 2 | let wasm; 3 | 4 | const heap = new Array(32).fill(undefined); 5 | 6 | heap.push(undefined, null, true, false); 7 | 8 | function getObject(idx) { return heap[idx]; } 9 | 10 | let heap_next = heap.length; 11 | 12 | function dropObject(idx) { 13 | if (idx < 36) return; 14 | heap[idx] = heap_next; 15 | heap_next = idx; 16 | } 17 | 18 | function takeObject(idx) { 19 | const ret = getObject(idx); 20 | dropObject(idx); 21 | return ret; 22 | } 23 | 24 | let WASM_VECTOR_LEN = 0; 25 | 26 | let cachedUint8Memory0 = new Uint8Array(); 27 | 28 | function getUint8Memory0() { 29 | if (cachedUint8Memory0.byteLength === 0) { 30 | cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); 31 | } 32 | return cachedUint8Memory0; 33 | } 34 | 35 | const cachedTextEncoder = new TextEncoder('utf-8'); 36 | 37 | const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' 38 | ? function (arg, view) { 39 | return cachedTextEncoder.encodeInto(arg, view); 40 | } 41 | : function (arg, view) { 42 | const buf = cachedTextEncoder.encode(arg); 43 | view.set(buf); 44 | return { 45 | read: arg.length, 46 | written: buf.length 47 | }; 48 | }); 49 | 50 | function passStringToWasm0(arg, malloc, realloc) { 51 | 52 | if (realloc === undefined) { 53 | const buf = cachedTextEncoder.encode(arg); 54 | const ptr = malloc(buf.length); 55 | getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf); 56 | WASM_VECTOR_LEN = buf.length; 57 | return ptr; 58 | } 59 | 60 | let len = arg.length; 61 | let ptr = malloc(len); 62 | 63 | const mem = getUint8Memory0(); 64 | 65 | let offset = 0; 66 | 67 | for (; offset < len; offset++) { 68 | const code = arg.charCodeAt(offset); 69 | if (code > 0x7F) break; 70 | mem[ptr + offset] = code; 71 | } 72 | 73 | if (offset !== len) { 74 | if (offset !== 0) { 75 | arg = arg.slice(offset); 76 | } 77 | ptr = realloc(ptr, len, len = offset + arg.length * 3); 78 | const view = getUint8Memory0().subarray(ptr + offset, ptr + len); 79 | const ret = encodeString(arg, view); 80 | 81 | offset += ret.written; 82 | } 83 | 84 | WASM_VECTOR_LEN = offset; 85 | return ptr; 86 | } 87 | 88 | let cachedInt32Memory0 = new Int32Array(); 89 | 90 | function getInt32Memory0() { 91 | if (cachedInt32Memory0.byteLength === 0) { 92 | cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); 93 | } 94 | return cachedInt32Memory0; 95 | } 96 | 97 | const cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); 98 | 99 | cachedTextDecoder.decode(); 100 | 101 | function getStringFromWasm0(ptr, len) { 102 | return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); 103 | } 104 | /** 105 | * @param {string} program 106 | * @returns {string} 107 | */ 108 | export function run(program) { 109 | try { 110 | const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); 111 | const ptr0 = passStringToWasm0(program, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 112 | const len0 = WASM_VECTOR_LEN; 113 | wasm.run(retptr, ptr0, len0); 114 | var r0 = getInt32Memory0()[retptr / 4 + 0]; 115 | var r1 = getInt32Memory0()[retptr / 4 + 1]; 116 | return getStringFromWasm0(r0, r1); 117 | } finally { 118 | wasm.__wbindgen_add_to_stack_pointer(16); 119 | wasm.__wbindgen_free(r0, r1); 120 | } 121 | } 122 | 123 | function addHeapObject(obj) { 124 | if (heap_next === heap.length) heap.push(heap.length + 1); 125 | const idx = heap_next; 126 | heap_next = heap[idx]; 127 | 128 | heap[idx] = obj; 129 | return idx; 130 | } 131 | 132 | async function load(module, imports) { 133 | if (typeof Response === 'function' && module instanceof Response) { 134 | if (typeof WebAssembly.instantiateStreaming === 'function') { 135 | try { 136 | return await WebAssembly.instantiateStreaming(module, imports); 137 | 138 | } catch (e) { 139 | if (module.headers.get('Content-Type') != 'application/wasm') { 140 | console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); 141 | 142 | } else { 143 | throw e; 144 | } 145 | } 146 | } 147 | 148 | const bytes = await module.arrayBuffer(); 149 | return await WebAssembly.instantiate(bytes, imports); 150 | 151 | } else { 152 | const instance = await WebAssembly.instantiate(module, imports); 153 | 154 | if (instance instanceof WebAssembly.Instance) { 155 | return { instance, module }; 156 | 157 | } else { 158 | return instance; 159 | } 160 | } 161 | } 162 | 163 | function getImports() { 164 | const imports = {}; 165 | imports.wbg = {}; 166 | imports.wbg.__wbg_new_abda76e883ba8a5f = function() { 167 | const ret = new Error(); 168 | return addHeapObject(ret); 169 | }; 170 | imports.wbg.__wbg_stack_658279fe44541cf6 = function(arg0, arg1) { 171 | const ret = getObject(arg1).stack; 172 | const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 173 | const len0 = WASM_VECTOR_LEN; 174 | getInt32Memory0()[arg0 / 4 + 1] = len0; 175 | getInt32Memory0()[arg0 / 4 + 0] = ptr0; 176 | }; 177 | imports.wbg.__wbg_error_f851667af71bcfc6 = function(arg0, arg1) { 178 | try { 179 | console.error(getStringFromWasm0(arg0, arg1)); 180 | } finally { 181 | wasm.__wbindgen_free(arg0, arg1); 182 | } 183 | }; 184 | imports.wbg.__wbindgen_object_drop_ref = function(arg0) { 185 | takeObject(arg0); 186 | }; 187 | 188 | return imports; 189 | } 190 | 191 | function initMemory(imports, maybe_memory) { 192 | 193 | } 194 | 195 | function finalizeInit(instance, module) { 196 | wasm = instance.exports; 197 | init.__wbindgen_wasm_module = module; 198 | cachedInt32Memory0 = new Int32Array(); 199 | cachedUint8Memory0 = new Uint8Array(); 200 | 201 | 202 | return wasm; 203 | } 204 | 205 | function initSync(module) { 206 | const imports = getImports(); 207 | 208 | initMemory(imports); 209 | 210 | if (!(module instanceof WebAssembly.Module)) { 211 | module = new WebAssembly.Module(module); 212 | } 213 | 214 | const instance = new WebAssembly.Instance(module, imports); 215 | 216 | return finalizeInit(instance, module); 217 | } 218 | 219 | async function init(input) { 220 | if (typeof input === 'undefined') { 221 | input = new URL('jlox_rs_lib_bg.wasm', import.meta.url); 222 | } 223 | const imports = getImports(); 224 | 225 | if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) { 226 | input = fetch(input); 227 | } 228 | 229 | initMemory(imports); 230 | 231 | const { instance, module } = await load(await input, imports); 232 | 233 | return finalizeInit(instance, module); 234 | } 235 | 236 | export { initSync } 237 | export default init; 238 | -------------------------------------------------------------------------------- /src/ast.rs: -------------------------------------------------------------------------------- 1 | use paste::paste; 2 | 3 | use crate::{token::Token, types::Number}; 4 | 5 | pub trait Walkable { 6 | fn walk(&self, visitor: V, state: S) -> T; 7 | } 8 | 9 | macro_rules! ast_root { 10 | (pub enum $r:ident { $($n:ident: $t:ident $b:tt),* $(,)? }) => { 11 | #[derive(Debug, Clone, PartialEq)] 12 | pub enum $r { 13 | $($n($n)),* 14 | } 15 | 16 | $( 17 | #[derive(Debug, Clone, PartialEq)] 18 | pub $t $n $b 19 | )* 20 | } 21 | } 22 | 23 | macro_rules! ast { 24 | ($(pub enum $r:ident { $($n:ident: $t:ident $b:tt),* $(,)? })*) => { paste! { 25 | $( 26 | ast_root!( 27 | pub enum $r { $($n: $t $b),* } 28 | ); 29 | 30 | pub trait [<$r Visitor>] { 31 | $( 32 | fn [](self, [<$r:lower>]: &$n, state: S) -> T; 33 | )* 34 | } 35 | 36 | impl]> Walkable for $r { 37 | fn walk(&self, visitor: V, state: S) -> T { 38 | match self { 39 | $( 40 | $r::$n(y) => visitor.[](y, state) 41 | ),* 42 | } 43 | } 44 | } 45 | )* 46 | }} 47 | } 48 | 49 | ast! { 50 | pub enum Expr { 51 | Literal: enum { 52 | Number(Number), 53 | String(String), 54 | True, 55 | False, 56 | Nil, 57 | }, 58 | Unary: struct { 59 | pub operator: Token, 60 | pub right: Box, 61 | }, 62 | Binary: struct { 63 | pub left: Box, 64 | pub operator: Token, 65 | pub right: Box, 66 | }, 67 | Call: struct { 68 | pub callee: Box, 69 | pub closing_paren: Token, 70 | pub arguments: Vec, 71 | }, 72 | Get: struct { 73 | pub object: Box, 74 | pub name: Token, 75 | }, 76 | Set: struct { 77 | pub object: Box, 78 | pub name: Token, 79 | pub value: Box, 80 | }, 81 | Super: struct { 82 | pub keyword: Token, 83 | pub method: Token, 84 | }, 85 | This: struct { 86 | pub keyword: Token 87 | }, 88 | Logical: struct { 89 | pub left: Box, 90 | pub operator: Token, 91 | pub right: Box, 92 | }, 93 | Lambda: struct { 94 | pub token: Token, 95 | pub params: Vec, 96 | pub body: Vec, 97 | }, 98 | Ternary: struct { 99 | pub left: Box, 100 | pub mid: Box, 101 | pub right: Box, 102 | }, 103 | Grouping: struct { 104 | pub expr: Box 105 | }, 106 | Variable: struct { 107 | pub name: Token 108 | }, 109 | Assign: struct { 110 | pub name: Token, 111 | pub value: Box 112 | } 113 | } 114 | 115 | pub enum Stmt { 116 | Block: struct { 117 | pub statements: Vec 118 | }, 119 | Expression: struct { 120 | pub expr: Expr 121 | }, 122 | Class: struct { 123 | pub name: Token, 124 | // Box to keep enum variant sizes similar 125 | pub superclass: Option>, 126 | pub left_brace: Token, // Used as the "definition" point of `this` 127 | pub methods: Vec, 128 | pub class_methods: Vec, 129 | pub getters: Vec, 130 | }, 131 | Function: struct { 132 | pub name: Token, 133 | pub params: Vec, 134 | pub body: Vec, 135 | }, 136 | If: struct { 137 | pub condition: Box, 138 | pub then_branch: Box, 139 | pub else_branch: Option> 140 | }, 141 | Print: struct { 142 | pub expr: Expr 143 | }, 144 | Return: struct { 145 | pub keyword: Token, 146 | pub value: Option>, 147 | }, 148 | Var: struct { 149 | pub name: Token, 150 | pub initializer: Option> 151 | }, 152 | While: struct { 153 | pub condition: Box, 154 | pub statement: Box 155 | }, 156 | Break: struct { 157 | pub token: Token 158 | } 159 | } 160 | } 161 | 162 | impl std::fmt::Display for Expr { 163 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 164 | match self { 165 | Expr::Literal(Literal::Number(n)) => n.fmt(f), 166 | Expr::Literal(Literal::String(s)) => s.fmt(f), 167 | Expr::Literal(Literal::False) => false.fmt(f), 168 | Expr::Literal(Literal::True) => true.fmt(f), 169 | Expr::Literal(Literal::Nil) => f.write_str("nil"), 170 | Expr::Unary(Unary { operator, right }) => { 171 | f.write_fmt(format_args!("{}{}", operator, right)) 172 | } 173 | Expr::Binary(Binary { 174 | left, 175 | operator, 176 | right, 177 | }) => f.write_fmt(format_args!("({} {} {})", left, operator, right)), 178 | Expr::Logical(Logical { 179 | left, 180 | operator, 181 | right, 182 | }) => f.write_fmt(format_args!("({} {} {})", left, operator, right)), 183 | Expr::Ternary(Ternary { left, mid, right }) => { 184 | f.write_fmt(format_args!("({} ? ({}) : {})", left, mid, right)) 185 | } 186 | Expr::Grouping(Grouping { expr }) => f.write_fmt(format_args!("({})", expr)), 187 | Expr::Variable(Variable { name }) => name.lexeme.fmt(f), 188 | Expr::Assign(Assign { name, value }) => write!(f, "{} = {}", name, value), 189 | Expr::Call(Call { 190 | callee, arguments, .. 191 | }) => { 192 | write!(f, "{}(", callee)?; 193 | let mut iter = arguments.iter().peekable(); 194 | while let Some(x) = iter.next() { 195 | write!(f, "{}", x)?; 196 | if iter.peek().is_some() { 197 | write!(f, ", ")?; 198 | } 199 | } 200 | write!(f, ")") 201 | } 202 | Expr::Lambda(Lambda { params, .. }) => { 203 | write!(f, "fun (")?; 204 | let mut iter = params.iter().peekable(); 205 | while let Some(x) = iter.next() { 206 | write!(f, "{}", x)?; 207 | if iter.peek().is_some() { 208 | write!(f, ", ")?; 209 | } 210 | } 211 | write!(f, ")") 212 | } 213 | Expr::Get(Get { object, name }) => write!(f, "{}.{}", object, name), 214 | Expr::Set(Set { 215 | object, 216 | name, 217 | value, 218 | }) => write!(f, "{}.{} = {}", object, name, value), 219 | Expr::This(_) => write!(f, "this"), 220 | Expr::Super(s) => write!(f, "super.{}", s.method), 221 | } 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /src/scanner.rs: -------------------------------------------------------------------------------- 1 | use macros::ResolveErrorLocation; 2 | use thiserror::Error; 3 | 4 | use crate::resolver::CommandIndex; 5 | use crate::token::Token; 6 | use crate::token::TokenValue; 7 | use crate::types::Number; 8 | use crate::types::SourceIndex; 9 | use crate::types::SourceLocation; 10 | 11 | #[derive(Error, Debug, ResolveErrorLocation)] 12 | pub enum Error { 13 | #[error("Invalid UTF-8 character at {location}")] 14 | InvalidUtf8Char { location: SourceLocation }, 15 | 16 | #[error("Unexpected character `{c}` at {location}")] 17 | UnexpectedCharacter { c: char, location: SourceLocation }, 18 | 19 | #[error("Unterminated string starting at {location}")] 20 | UnterminatedString { location: SourceLocation }, 21 | 22 | #[error("Unterminated /* block comment */ starting at {location}")] 23 | UnterminatedComment { location: SourceLocation }, 24 | } 25 | 26 | pub struct Scanner<'a> { 27 | source: &'a [u8], 28 | start: SourceIndex, 29 | current: SourceIndex, 30 | command: CommandIndex, 31 | } 32 | 33 | impl<'a> Scanner<'a> { 34 | pub fn new(source: &'a [u8], command: CommandIndex) -> Self { 35 | Scanner { 36 | source, 37 | command, 38 | start: 0, 39 | current: 0, 40 | } 41 | } 42 | 43 | pub fn scan_tokens(&mut self) -> Result, Vec> { 44 | let mut tokens = vec![]; 45 | let mut errors = vec![]; 46 | 47 | while !self.is_at_end() { 48 | match self 49 | .scan_token() 50 | .and_then(|ov| ov.map(|v| self.make_token(v)).transpose()) 51 | { 52 | Ok(Some(token)) => tokens.push(token), 53 | Ok(None) => {} 54 | Err(e) => errors.push(e), 55 | }; 56 | } 57 | 58 | tokens.push(Token { 59 | value: TokenValue::Eof, 60 | lexeme: String::new(), 61 | location: SourceLocation::new(self.command, self.current), 62 | }); 63 | 64 | if errors.is_empty() { 65 | Ok(tokens) 66 | } else { 67 | Err(errors) 68 | } 69 | } 70 | 71 | fn is_at_end(&self) -> bool { 72 | self.current >= self.source.len() 73 | } 74 | 75 | fn advance(&mut self) -> u8 { 76 | let c = self.source[self.current]; 77 | self.current += 1; 78 | c 79 | } 80 | 81 | fn match_(&mut self, c: u8) -> bool { 82 | if self.is_at_end() { 83 | return false; 84 | } 85 | if self.peek() != c { 86 | return false; 87 | } 88 | self.current += 1; 89 | true 90 | } 91 | 92 | fn peek_offset(&self, offset: SourceIndex) -> u8 { 93 | *self.source.get(self.current + offset).unwrap_or(&b'\0') 94 | } 95 | 96 | fn peek(&self) -> u8 { 97 | self.peek_offset(0) 98 | } 99 | 100 | fn peek_next(&self) -> u8 { 101 | self.peek_offset(1) 102 | } 103 | 104 | fn substring(&self, start: SourceIndex, end: SourceIndex) -> Result { 105 | String::from_utf8(self.source[start..end].to_vec()).map_err(|source| { 106 | Error::InvalidUtf8Char { 107 | location: SourceLocation::new( 108 | self.command, 109 | start + source.utf8_error().valid_up_to(), 110 | ), 111 | } 112 | }) 113 | } 114 | 115 | fn make_token(&mut self, value: TokenValue) -> Result { 116 | Ok(Token { 117 | value, 118 | lexeme: self.substring(self.start, self.current)?, 119 | location: SourceLocation::new(self.command, self.start), 120 | }) 121 | } 122 | 123 | fn string(&mut self) -> Result { 124 | while self.peek() != b'"' && !self.is_at_end() { 125 | self.advance(); 126 | } 127 | 128 | if self.is_at_end() { 129 | return Err(Error::UnterminatedString { 130 | location: SourceLocation::new(self.command, self.start), 131 | }); 132 | } 133 | 134 | // Closing " 135 | self.advance(); 136 | 137 | Ok(TokenValue::String( 138 | self.substring(self.start + 1, self.current - 1)?, 139 | )) 140 | } 141 | 142 | fn number(&mut self) -> Result { 143 | while self.peek().is_ascii_digit() { 144 | self.advance(); 145 | } 146 | 147 | // Optional fractional part 148 | if self.peek() == b'.' && self.peek_next().is_ascii_digit() { 149 | self.advance(); 150 | while self.peek().is_ascii_digit() { 151 | self.advance(); 152 | } 153 | } 154 | 155 | Ok(TokenValue::Number( 156 | self.substring(self.start, self.current)? 157 | .parse::() 158 | .expect("Weird, I'm super sure this ought to be a valid f64"), 159 | )) 160 | } 161 | 162 | fn identifier(&mut self) -> Result { 163 | while self.peek().is_ascii_alphanumeric() || self.peek() == b'_' { 164 | self.advance(); 165 | } 166 | self.substring(self.start, self.current) 167 | } 168 | 169 | fn scan_token(&mut self) -> Result, Error> { 170 | self.start = self.current; 171 | match self.advance() { 172 | b'(' => Ok(Some(TokenValue::LeftParen)), 173 | b')' => Ok(Some(TokenValue::RightParen)), 174 | b'{' => Ok(Some(TokenValue::LeftBrace)), 175 | b'}' => Ok(Some(TokenValue::RightBrace)), 176 | b',' => Ok(Some(TokenValue::Comma)), 177 | b'.' => Ok(Some(TokenValue::Dot)), 178 | b'-' => Ok(Some(TokenValue::Minus)), 179 | b'+' => Ok(Some(TokenValue::Plus)), 180 | b';' => Ok(Some(TokenValue::Semicolon)), 181 | b'*' => Ok(Some(TokenValue::Star)), 182 | b'?' => Ok(Some(TokenValue::Question)), 183 | b':' => Ok(Some(TokenValue::Colon)), 184 | 185 | b'!' => Ok(Some(if self.match_(b'=') { 186 | TokenValue::BangEqual 187 | } else { 188 | TokenValue::Bang 189 | })), 190 | 191 | b'=' => Ok(Some(if self.match_(b'=') { 192 | TokenValue::EqualEqual 193 | } else { 194 | TokenValue::Equal 195 | })), 196 | 197 | b'<' => Ok(Some(if self.match_(b'=') { 198 | TokenValue::LessEqual 199 | } else { 200 | TokenValue::Less 201 | })), 202 | 203 | b'>' => Ok(Some(if self.match_(b'=') { 204 | TokenValue::GreaterEqual 205 | } else { 206 | TokenValue::Greater 207 | })), 208 | 209 | b'/' => { 210 | if self.match_(b'/') { 211 | // Comment to the end of the line 212 | while self.peek() != b'\n' && !self.is_at_end() { 213 | self.advance(); 214 | } 215 | Ok(None) 216 | } else if self.match_(b'*') { 217 | while !(self.is_at_end() || (self.peek() == b'*' && self.peek_next() == b'/')) { 218 | self.advance(); 219 | } 220 | if self.is_at_end() { 221 | Err(Error::UnterminatedComment { 222 | location: SourceLocation::new(self.command, self.start), 223 | }) 224 | } else { 225 | self.advance(); 226 | self.advance(); 227 | Ok(None) 228 | } 229 | } else { 230 | Ok(Some(TokenValue::Slash)) 231 | } 232 | } 233 | 234 | b' ' | b'\r' | b'\t' | b'\n' => Ok(None), 235 | 236 | b'"' => self.string().map(Some), 237 | c if c.is_ascii_digit() => self.number().map(Some), 238 | c @ b'_' | c if c.is_ascii_alphabetic() => { 239 | self.identifier().map(|x| match x.as_str() { 240 | "and" => Some(TokenValue::And), 241 | "break" => Some(TokenValue::Break), 242 | "class" => Some(TokenValue::Class), 243 | "else" => Some(TokenValue::Else), 244 | "false" => Some(TokenValue::False), 245 | "fun" => Some(TokenValue::Fun), 246 | "for" => Some(TokenValue::For), 247 | "if" => Some(TokenValue::If), 248 | "nil" => Some(TokenValue::Nil), 249 | "or" => Some(TokenValue::Or), 250 | "print" => Some(TokenValue::Print), 251 | "return" => Some(TokenValue::Return), 252 | "super" => Some(TokenValue::Super), 253 | "this" => Some(TokenValue::This), 254 | "true" => Some(TokenValue::True), 255 | "var" => Some(TokenValue::Var), 256 | "while" => Some(TokenValue::While), 257 | _ => Some(TokenValue::Identifier(x)), 258 | }) 259 | } 260 | 261 | c => Err(Error::UnexpectedCharacter { 262 | c: c.into(), 263 | location: SourceLocation::new(self.command, self.current), 264 | }), 265 | } 266 | } 267 | } 268 | 269 | #[cfg(test)] 270 | mod test { 271 | use crate::token::TokenValue; 272 | 273 | #[test] 274 | fn test_tokens() { 275 | let source = b"(){},.-+;*!23!=42.42/* block \n comment */==<<==>/>=\"foo \nbar\"// this is a comment now".to_vec(); 276 | let mut scanner = super::Scanner::new(&source, 0); 277 | let tokens = scanner.scan_tokens().unwrap(); 278 | 279 | for (i, v) in [ 280 | TokenValue::LeftParen, 281 | TokenValue::RightParen, 282 | TokenValue::LeftBrace, 283 | TokenValue::RightBrace, 284 | TokenValue::Comma, 285 | TokenValue::Dot, 286 | TokenValue::Minus, 287 | TokenValue::Plus, 288 | TokenValue::Semicolon, 289 | TokenValue::Star, 290 | TokenValue::Bang, 291 | TokenValue::Number(23.0), 292 | TokenValue::BangEqual, 293 | TokenValue::Number(42.42), 294 | TokenValue::EqualEqual, 295 | TokenValue::Less, 296 | TokenValue::LessEqual, 297 | TokenValue::Equal, 298 | TokenValue::Greater, 299 | TokenValue::Slash, 300 | TokenValue::GreaterEqual, 301 | TokenValue::String("foo \nbar".to_string()), 302 | ] 303 | .iter() 304 | .enumerate() 305 | { 306 | assert_eq!(&tokens[i].value, v); 307 | } 308 | 309 | assert_eq!(tokens[12].lexeme, "!="); 310 | } 311 | 312 | #[test] 313 | fn test_unexpected_character() { 314 | let errs = super::Scanner::new(b"^", 0).scan_tokens().err().unwrap(); 315 | assert_eq!(errs.len(), 1); 316 | } 317 | 318 | // TODO test identifiers and reserved keywords 319 | // TODO Test each error case 320 | // TODO Test whitespace / multi line input 321 | } 322 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 13 | 14 | 20 | 21 | 27 | 28 | 29 | 30 | 31 | gh:abesto/jlox-rs 32 | 33 | 34 |
35 |
36 | 37 | 40 | 43 |
44 | 45 |
46 |
47 |

 48 |       
49 |
50 | 51 | 225 | 226 | 290 | 291 | 292 | -------------------------------------------------------------------------------- /tests/test_integration.rs: -------------------------------------------------------------------------------- 1 | use std::io::{Read, Write}; 2 | use std::process::{Child, ChildStderr, ChildStdin, ChildStdout, Command, Stdio}; 3 | 4 | use assert_cmd::prelude::CommandCargoExt; 5 | use paste::paste; 6 | 7 | // TODO: time out on reading after some reasonable time 8 | 9 | struct Lox { 10 | child: Option, 11 | stdin: Option, 12 | stdout: ChildStdout, 13 | stderr: ChildStderr, 14 | } 15 | 16 | impl Lox { 17 | fn script(args: I) -> Self 18 | where 19 | I: IntoIterator, 20 | S: AsRef, 21 | { 22 | let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap(); 23 | cmd.stdin(Stdio::piped()) 24 | .stdout(Stdio::piped()) 25 | .stderr(Stdio::piped()) 26 | .args(args); 27 | let mut child = cmd.spawn().unwrap(); 28 | Lox { 29 | stdin: Some(child.stdin.take().unwrap()), 30 | stdout: child.stdout.take().unwrap(), 31 | stderr: child.stderr.take().unwrap(), 32 | child: Some(child), 33 | } 34 | } 35 | 36 | fn repl() -> Self { 37 | let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap(); 38 | cmd.stdin(Stdio::piped()) 39 | .stdout(Stdio::piped()) 40 | .stderr(Stdio::piped()); 41 | let mut child = cmd.spawn().unwrap(); 42 | Lox { 43 | stdin: Some(child.stdin.take().unwrap()), 44 | stdout: child.stdout.take().unwrap(), 45 | stderr: child.stderr.take().unwrap(), 46 | child: Some(child), 47 | } 48 | } 49 | 50 | fn assert_stdout>(&mut self, expected: S) { 51 | let expected = expected.as_ref(); 52 | let mut buf = vec![b'\0'; expected.len()]; 53 | if self.stdout.read_exact(&mut buf).is_ok() { 54 | if let Ok(s) = std::str::from_utf8(&buf) { 55 | assert_eq!(expected, s); 56 | } 57 | } 58 | } 59 | 60 | fn assert_stderr>(&mut self, expected: S) { 61 | let expected = expected.as_ref(); 62 | let mut buf = vec![b'\0'; expected.len()]; 63 | if self.stderr.read_exact(&mut buf).is_ok() { 64 | if let Ok(s) = std::str::from_utf8(&buf) { 65 | assert_eq!(expected, s); 66 | } 67 | } 68 | } 69 | 70 | fn writeln>(&mut self, s: S) { 71 | if let Some(stdin) = self.stdin.as_mut() { 72 | stdin.write_all(s.as_ref().as_bytes()).unwrap(); 73 | stdin.write_all(b"\n").unwrap(); 74 | stdin.flush().unwrap(); 75 | } 76 | } 77 | 78 | fn assert_stderr_consumed(&mut self) { 79 | let mut buf = vec![]; 80 | if self.stderr.read_to_end(&mut buf).is_ok() { 81 | if let Ok(s) = std::str::from_utf8(buf.as_slice()) { 82 | assert_eq!("", s, "Unconsumed STDERR"); 83 | } 84 | } 85 | } 86 | 87 | fn assert_stdout_consumed(&mut self) { 88 | let mut buf = vec![]; 89 | if self.stdout.read_to_end(&mut buf).is_ok() { 90 | assert_eq!( 91 | "", 92 | std::str::from_utf8(buf.as_slice()).unwrap(), 93 | "Unconsumed STDOUT" 94 | ); 95 | } 96 | } 97 | } 98 | 99 | macro_rules! repl_test { 100 | (out $lox:ident, E $o:literal) => { 101 | $lox.assert_stderr($o); $lox.assert_stderr("\n"); 102 | }; 103 | 104 | (out $lox:ident, $o:literal) => { 105 | $lox.assert_stdout($o); $lox.assert_stdout("\n"); 106 | }; 107 | 108 | ($n:ident, { $( 109 | > $i:literal 110 | $( 111 | $($p:ident)? $o:literal 112 | )* 113 | )* }) => { paste! { 114 | #[test] 115 | fn []() { 116 | let mut lox = Lox::repl(); 117 | $( 118 | lox.assert_stdout("> "); 119 | lox.writeln($i); 120 | $(repl_test!(out lox, $($p)? $o);)* 121 | )* 122 | let mut child = std::mem::take(&mut lox.child).unwrap(); 123 | if let Some(stdin) = std::mem::take(&mut lox.stdin) { 124 | drop(stdin); 125 | } 126 | let _ = child.wait(); 127 | lox.assert_stderr_consumed(); 128 | lox.assert_stdout("> "); 129 | lox.assert_stdout_consumed(); 130 | } 131 | }}; 132 | } 133 | 134 | macro_rules! program_test { 135 | ($n:ident, { 136 | $( 137 | $($p:ident)? $o:literal 138 | )* 139 | }) => { paste! { 140 | #[test] 141 | fn []() { 142 | let mut lox = Lox::script(&[concat!("tests/programs/", stringify!($n), ".lox")]); 143 | 144 | let mut child = std::mem::take(&mut lox.child).unwrap(); 145 | if let Some(stdin) = std::mem::take(&mut lox.stdin) { 146 | drop(stdin); 147 | } 148 | let _ = child.wait(); 149 | 150 | $( 151 | repl_test!(out lox, $($p)? $o); 152 | )* 153 | 154 | lox.assert_stderr_consumed(); 155 | lox.assert_stdout_consumed(); 156 | } 157 | }}; 158 | } 159 | 160 | repl_test!(smoke, { 161 | > "print 3;" 162 | "3" 163 | > "print 1 + 2 != 3 ? \"bad\" : \"good\";" 164 | "good" 165 | }); 166 | 167 | repl_test!(variable_definition, { 168 | > "var x = 3;" 169 | > "print x == 3;" 170 | "true" 171 | }); 172 | 173 | repl_test!(undefined_variable, { 174 | > "var x = 4;" 175 | > "print y;" 176 | E "Undefined variable y at 0:6" 177 | }); 178 | 179 | repl_test!(assignment, { 180 | > "var x = true;" 181 | > "print x = 2;" 182 | "2" 183 | > "x = 3;" 184 | "3" 185 | > "print x;" 186 | "3" 187 | }); 188 | 189 | repl_test!(assignment_undefined_variable, { 190 | > "x = 2;" 191 | E "Undefined variable x at 0:0" 192 | }); 193 | 194 | repl_test!(lexical_scope_shadow, { 195 | > "var x = \"outer\"; { var x = \"inner\"; print x; } print x;" 196 | "inner" 197 | "outer" 198 | }); 199 | 200 | repl_test!(lexical_scope_assign, { 201 | > "var x = \"outer\"; { x = \"inner\"; } print x; " 202 | "inner" 203 | }); 204 | 205 | repl_test!(self_initializer, { 206 | > "var x = \"outer\"; { var x = \"inner \" + x; }" 207 | E "Can't read local variable `x` in its own initializer at 0:38" 208 | E "Variable resolution failed, see errors above." 209 | }); 210 | 211 | repl_test!(interpreter_prints_expression_result, { 212 | > "var x = 2; x = x + 1; x;" 213 | "3" 214 | }); 215 | 216 | repl_test!(uninitialized_variable, { 217 | > "var x; var y; x = 3; print x; print y;" 218 | "3" 219 | E "Uninitialized variable y at 0:36" 220 | }); 221 | 222 | repl_test!(conditional_if, { 223 | > "var x = 1;" 224 | > "if (x == 1) { print x; var y = 2; } print y;" 225 | "1" 226 | E "Undefined variable y at 0:42" 227 | }); 228 | 229 | repl_test!(conditional_if_else, { 230 | > "if (42) print \"yes\"; else print \"no\";" 231 | "yes" 232 | > "if (nil) print \"yes\"; else print \"no\";" 233 | "no" 234 | }); 235 | 236 | repl_test!(conditional_chain, { 237 | > "if (false) print 1; else if (false) print 2; else print 3;" 238 | "3" 239 | }); 240 | 241 | repl_test!(parsing_error_report, { 242 | > "+ 3;" 243 | E "LHS missing for `+` at 0:0" 244 | E "Expected expression, found: `;` at 0:3" 245 | E "Parsing failed, see errors above." 246 | }); 247 | 248 | repl_test!(short_circuit_logical, { 249 | > "1 == 2 and 3;" 250 | "false" 251 | > "1 == 1 or 3;" 252 | "true" 253 | > "1 == 2 and 3 or 4;" 254 | "4" 255 | }); 256 | 257 | repl_test!(while_loop, { 258 | > "var i = 0;" 259 | > "while (i < 3) { print i; i = i + 1; } print \"done\";" 260 | "0" 261 | "1" 262 | "2" 263 | "done" 264 | }); 265 | 266 | repl_test!(for_loop, { 267 | > "for (var i = 0; i < 5; i = i + 2) print i;" 268 | "0" 269 | "2" 270 | "4" 271 | }); 272 | 273 | repl_test!(break_statement, { 274 | > "var x = 0; while (true) { print x; while (true) break; x = x + 1; if (x >= 3) break; }" 275 | "0" 276 | "1" 277 | "2" 278 | > "while (false) {} break;" 279 | E "`break` outside loop at 0:7" 280 | E "Parsing failed, see errors above." 281 | }); 282 | 283 | repl_test!(clock, { 284 | > "type(clock());" 285 | "Number" 286 | }); 287 | 288 | repl_test!(call_non_callable, { 289 | > "13();" 290 | E "Can only call functions and classes, tried to call: `13` of type `Number` at 0:3" 291 | }); 292 | 293 | repl_test!(wrong_arity_native, { 294 | > "clock(12);" 295 | E "Expected 0 arguments but got 1 at 0:8" 296 | }); 297 | 298 | repl_test!(wrong_arity_user_function, { 299 | > "fun f(x) {}" 300 | > "f(1, 2);" 301 | E "Expected 1 arguments but got 2 at 0:6" 302 | }); 303 | 304 | program_test!(sayHi, { "Hi, Valued Customer!" }); 305 | 306 | program_test!(fib, { 307 | "2584" 308 | "4181" 309 | }); 310 | 311 | repl_test!(return_outside_function, { 312 | > "return 3;" 313 | E "`return` outside function at 0:0" 314 | E "Variable resolution failed, see errors above." 315 | }); 316 | 317 | program_test!(counter, { 318 | "1" 319 | "2" 320 | }); 321 | 322 | program_test!(lambda, { 323 | "1" 324 | "2" 325 | "3" 326 | }); 327 | 328 | // This one's non-trivial because naively the syntax clashes with normal function declarations. 329 | repl_test!(lambda_expr_statement, { 330 | > "fun () {};" 331 | "" 332 | > "fun () {}" 333 | E "Expected `;` after anonymous function expression statement at 1:0" 334 | E "Parsing failed, see errors above." 335 | }); 336 | 337 | program_test!(binding, { 338 | "global" 339 | "global" 340 | "block" 341 | }); 342 | 343 | repl_test!(repl_binding, { 344 | > "fun f() { var x = 1; return x; }" 345 | > "print f();" 346 | "1" 347 | }); 348 | 349 | repl_test!(double_declare_global, { 350 | > "var x = 1;" 351 | > "var x = 2;" 352 | > "print x;" 353 | "2" 354 | }); 355 | 356 | repl_test!(double_declare_local, { 357 | > "fun bad() { var x = 1; var x = 2; }" 358 | E "Variable `x` already exists in scope. This `var` statement: 0:27" 359 | E "Variable resolution failed, see errors above." 360 | }); 361 | 362 | repl_test!(local_shadow, { 363 | > "fun o() { var x = 1; fun i() { var x = 2; } return x; }" 364 | > "print o();" 365 | "1" 366 | }); 367 | 368 | program_test!(unused_local, { 369 | E "Unused local variable `x`, declared at 1:8" 370 | E "Variable resolution failed, see errors above." 371 | }); 372 | 373 | repl_test!(class_declaration, { 374 | > "class First {}" 375 | > "print First;" 376 | "" 377 | }); 378 | 379 | repl_test!(print_instance, { 380 | > "class C {}" 381 | > "var o = C();" 382 | > "print o;" 383 | "" 384 | }); 385 | 386 | repl_test!(property_get_on_non_object, { 387 | > "print 3.foo;" 388 | E "Tried to access property `foo` on non-object `3` of type `Number` at 0:8" 389 | }); 390 | 391 | repl_test!(property_set_on_non_object, { 392 | > "print 3.foo = 99;" 393 | E "Tried to access property `foo` on non-object `3` of type `Number` at 0:8" 394 | }); 395 | 396 | repl_test!(get_missing_property, { 397 | > "class C {}" 398 | > "var o = C();" 399 | > "print o.bar;" 400 | E "Undefined property `bar` on `` at 0:8" 401 | }); 402 | 403 | repl_test!(instance_fields, { 404 | > "class C {}" 405 | > "var a = C(); var b = C();" 406 | > "a.x = C(); b.x = a.x;" 407 | "" 408 | > "a.x.foo = 42;" 409 | "42" 410 | > "print a.x.foo;" 411 | "42" 412 | > "print b.x.foo;" 413 | "42" 414 | }); 415 | 416 | program_test!(simple_class, { 417 | "Crunch crunch crunch!" 418 | "Crunch crunch crunch!" 419 | }); 420 | 421 | program_test!(simple_method, { "Hi, I'm Wally" }); 422 | 423 | program_test!(callback_from_method, { "one two" }); 424 | 425 | repl_test!(this_outside_class, { 426 | > "print this;" 427 | E "`this` outside of a class at 0:6" 428 | E "Variable resolution failed, see errors above." 429 | }); 430 | 431 | program_test!(constructor, { "5" }); 432 | 433 | program_test!(constructor_error, { 434 | E "Division by zero at 2:19" 435 | }); 436 | 437 | program_test!(constructor_returns_this, { "" }); 438 | 439 | repl_test!(constructor_return_value, { 440 | > "class C { init() { return 1; } }" 441 | E "Return from initializer of `C` at 0:19" 442 | E "Variable resolution failed, see errors above." 443 | }); 444 | 445 | program_test!(constructor_returns_this_early, { 446 | "" 447 | "5" 448 | "" 449 | "7" 450 | }); 451 | 452 | program_test!(class_method, { "4" }); 453 | 454 | program_test!(class_method_this, { 455 | E "`this` in static method at 2:15" 456 | E "Variable resolution failed, see errors above." 457 | }); 458 | 459 | repl_test!(unknown_class_method, { 460 | > "class C {}" 461 | > "C.foo();" 462 | E "Undefined property `foo` on `` at 0:2" 463 | }); 464 | 465 | program_test!(getter, { "16" }); 466 | 467 | repl_test!(self_inheritance, { 468 | > "class Ohno < Ohno {}" 469 | E "Class `Ohno` inherits from itself at 0:13" 470 | E "Variable resolution failed, see errors above." 471 | }); 472 | 473 | program_test!(inherit_methods, { 474 | "10" 475 | "4" 476 | "15" 477 | "100" 478 | }); 479 | 480 | program_test!(super, { 481 | "A method" 482 | "A getter" 483 | }); 484 | 485 | repl_test!(missing_super_method, { 486 | > "class C {}" 487 | > "class D < C { f() { super.f(); } }" 488 | > "D().f();" 489 | E "Undefined property `f` on `` at 1:0" 490 | }); 491 | 492 | repl_test!(super_outside_class, { 493 | > "super.foo();" 494 | E "`super` outside of a class at 0:0" 495 | E "Variable resolution failed, see errors above." 496 | }); 497 | 498 | repl_test!(super_without_superclass, { 499 | > "class C { f() { super.f(); } }" 500 | E "`super` in class with no superclass at 0:16" 501 | E "Variable resolution failed, see errors above." 502 | }); 503 | -------------------------------------------------------------------------------- /src/resolver.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use macros::ResolveErrorLocation; 4 | use thiserror::Error; 5 | 6 | use crate::{ 7 | ast::{ExprVisitor, Stmt, StmtVisitor, Walkable}, 8 | token::Token, 9 | types::SourceLocation, 10 | }; 11 | 12 | #[derive(Error, Debug, ResolveErrorLocation)] 13 | pub enum Error { 14 | #[error("Can't read local variable `{name}` in its own initializer at {location}")] 15 | SelfReferencingInitializer { 16 | name: String, 17 | location: SourceLocation, 18 | }, 19 | 20 | #[error("Variable `{name}` already exists in scope. This `var` statement: {location}")] 21 | DoubleDeclaration { 22 | name: String, 23 | location: SourceLocation, 24 | }, 25 | 26 | #[error("`return` outside function at {location}")] 27 | ReturnOutsideFunction { location: SourceLocation }, 28 | 29 | #[error("Tried to set undeclared variable `{name}` at {location}")] 30 | DefineUndeclared { 31 | name: String, 32 | location: SourceLocation, 33 | }, 34 | 35 | #[error("Variable `{name}` used before initialization at {location}")] 36 | UseBeforeInit { 37 | name: String, 38 | location: SourceLocation, 39 | }, 40 | 41 | #[error("Unused local variable `{name}`, declared at {location}")] 42 | UnusedLocal { 43 | name: String, 44 | location: SourceLocation, 45 | }, 46 | 47 | #[error("`this` outside of a class at {location}")] 48 | ThisOutsideClass { location: SourceLocation }, 49 | 50 | #[error("`this` in static method at {location}")] 51 | ThisInStaticMethod { location: SourceLocation }, 52 | 53 | #[error("Return from initializer of `{class}` at {location}")] 54 | ReturnFromInitializer { 55 | class: String, 56 | location: SourceLocation, 57 | }, 58 | 59 | #[error("Class `{class}` inherits from itself at {location}")] 60 | SelfInheritance { 61 | class: String, 62 | location: SourceLocation, 63 | }, 64 | 65 | #[error("`super` outside of a class at {location}")] 66 | SuperOutsideClass { location: SourceLocation }, 67 | 68 | #[error("`super` in class with no superclass at {location}")] 69 | SuperWithoutSuperclass { location: SourceLocation }, 70 | } 71 | 72 | type Output = (); 73 | pub type CommandIndex = usize; 74 | 75 | #[derive(Debug, Clone)] 76 | pub struct Binding { 77 | pub scopes_up: usize, 78 | pub index_in_scope: usize, 79 | } 80 | 81 | impl Binding { 82 | pub fn less_one_scope(&self) -> Self { 83 | Self { 84 | scopes_up: self.scopes_up - 1, 85 | index_in_scope: self.index_in_scope, 86 | } 87 | } 88 | } 89 | 90 | pub type Bindings = HashMap; 91 | 92 | type State = Bindings; 93 | type Result> = std::result::Result; 94 | 95 | /// Concatenate list of errors if there are any, otherwise return the latest value 96 | fn combine_results(l: Result, r: Result) -> Result { 97 | match (l, r) { 98 | (Ok(_), x @ Ok(_)) | (x @ Err(_), Ok(_)) | (Ok(_), x @ Err(_)) => x, 99 | (Err(mut l), Err(mut r)) => { 100 | l.append(&mut r); 101 | Err(l) 102 | } 103 | } 104 | } 105 | 106 | fn combine_many_results(results: I) -> Result 107 | where 108 | I: IntoIterator, 109 | { 110 | results.into_iter().reduce(combine_results).unwrap() 111 | } 112 | 113 | /// Are we inside a function-like thing? 114 | /// Used to detect `return`s outside functions. 115 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] 116 | enum FunctionType { 117 | None, 118 | Function, 119 | Lambda, 120 | Method, 121 | ClassMethod, 122 | Initializer(String), 123 | } 124 | 125 | impl Default for FunctionType { 126 | fn default() -> Self { 127 | Self::None 128 | } 129 | } 130 | 131 | /// Are we inside a class? 132 | /// Used to detect `this`s outside classes. 133 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] 134 | enum ClassType { 135 | None, 136 | Class, 137 | Subclass, 138 | } 139 | 140 | impl Default for ClassType { 141 | fn default() -> Self { 142 | Self::None 143 | } 144 | } 145 | 146 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] 147 | enum VariableState { 148 | Declared, 149 | Defined, 150 | Used, 151 | } 152 | 153 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] 154 | struct VariableData { 155 | state: VariableState, 156 | declared_at: SourceLocation, 157 | index_in_scope: usize, 158 | } 159 | 160 | impl VariableData { 161 | #[must_use] 162 | fn new(declared_at: SourceLocation, index_in_scope: usize) -> Self { 163 | Self { 164 | state: VariableState::Declared, 165 | declared_at, 166 | index_in_scope, 167 | } 168 | } 169 | } 170 | 171 | #[derive(Default)] 172 | struct Scope { 173 | vars: HashMap, 174 | next_index: usize, 175 | } 176 | 177 | impl Scope { 178 | fn reserve(&mut self) -> usize { 179 | let n = self.next_index; 180 | self.next_index += 1; 181 | n 182 | } 183 | } 184 | 185 | #[derive(Default)] 186 | pub struct ResolverConfig { 187 | pub error_on_unused_locals: bool, 188 | } 189 | 190 | #[derive(Default)] 191 | pub struct Resolver { 192 | config: ResolverConfig, 193 | scopes: Vec, 194 | current_function: FunctionType, 195 | current_class: ClassType, 196 | } 197 | 198 | impl Resolver { 199 | #[must_use] 200 | pub fn new(config: ResolverConfig) -> Self { 201 | Self { 202 | config, 203 | ..Default::default() 204 | } 205 | } 206 | 207 | fn begin_scope(&mut self) { 208 | self.scopes.push(Default::default()); 209 | } 210 | 211 | fn end_scope(&mut self) -> Result { 212 | if let Some(scope) = self.scopes.pop() { 213 | if self.config.error_on_unused_locals { 214 | let errors: Vec = scope 215 | .vars 216 | .iter() 217 | .filter(|(_, v)| v.state != VariableState::Used) 218 | .map(|(k, v)| Error::UnusedLocal { 219 | name: k.clone(), 220 | location: v.declared_at, 221 | }) 222 | .collect(); 223 | if !errors.is_empty() { 224 | return Err(errors); 225 | } 226 | } 227 | } 228 | Ok(()) 229 | } 230 | 231 | pub fn resolve(&mut self, statements: &[Stmt]) -> Result { 232 | let mut state = State::new(); 233 | self.resolve_statements(statements, &mut state)?; 234 | Ok(state) 235 | } 236 | 237 | fn resolve_statements(&mut self, statements: &[Stmt], state: &mut State) -> Result { 238 | statements 239 | .iter() 240 | .map(|s| s.walk(&mut *self, state)) 241 | .reduce(combine_results) 242 | .unwrap_or(Ok(())) 243 | } 244 | 245 | fn resolve_local(&mut self, name: &Token, state: &mut State, is_use: bool) -> Result { 246 | let scopes_len = self.scopes.len(); 247 | for (i, scope) in self.scopes.iter_mut().enumerate().rev() { 248 | if let Some(var) = scope.vars.get_mut(&name.lexeme) { 249 | if var.state == VariableState::Declared { 250 | return Err(vec![Error::UseBeforeInit { 251 | name: name.lexeme.clone(), 252 | location: name.location, 253 | }]); 254 | } 255 | 256 | if is_use && var.state >= VariableState::Defined && var.state < VariableState::Used 257 | { 258 | var.state = VariableState::Used; 259 | } 260 | state.insert( 261 | name.location, 262 | Binding { 263 | scopes_up: scopes_len - 1 - i, 264 | index_in_scope: var.index_in_scope, 265 | }, 266 | ); 267 | return Ok(()); 268 | } 269 | } 270 | Ok(()) 271 | } 272 | 273 | fn declare(&mut self, name: &Token) -> Result { 274 | if let Some(scope) = self.scopes.last_mut() { 275 | if scope.vars.contains_key(&name.lexeme) { 276 | return Err(vec![Error::DoubleDeclaration { 277 | name: name.lexeme.clone(), 278 | location: name.location, 279 | }]); 280 | } 281 | let index_in_scope = scope.reserve(); 282 | scope.vars.insert( 283 | name.lexeme.clone(), 284 | VariableData::new(name.location, index_in_scope), 285 | ); 286 | } 287 | Ok(()) 288 | } 289 | 290 | fn define(&mut self, name: &Token) -> Result { 291 | if let Some(scope) = self.scopes.last_mut() { 292 | if let Some(var) = scope.vars.get_mut(&name.lexeme) { 293 | if var.state < VariableState::Defined { 294 | var.state = VariableState::Defined; 295 | } 296 | } else { 297 | return Err(vec![Error::DefineUndeclared { 298 | name: name.lexeme.clone(), 299 | location: name.location, 300 | }]); 301 | } 302 | } 303 | Ok(()) 304 | } 305 | 306 | fn resolve_function( 307 | &mut self, 308 | params: &[Token], 309 | body: &[Stmt], 310 | function_type: FunctionType, 311 | state: &mut State, 312 | ) -> Result { 313 | let enclosing_function = std::mem::replace(&mut self.current_function, function_type); 314 | self.begin_scope(); 315 | 316 | let mut result = Ok(()); 317 | for param in params { 318 | result = combine_many_results([ 319 | result, 320 | self.declare(param), 321 | self.define(param), 322 | self.resolve_local(param, state, true), 323 | ]); 324 | } 325 | result = combine_many_results([ 326 | result, 327 | self.resolve_statements(body, state), 328 | self.end_scope(), 329 | ]); 330 | 331 | self.current_function = enclosing_function; 332 | 333 | result 334 | } 335 | } 336 | 337 | impl ExprVisitor for &mut Resolver { 338 | fn visit_variable(self, expr: &crate::ast::Variable, state: &mut State) -> Result { 339 | if let Some(scope) = self.scopes.last() { 340 | if scope.vars.get(&expr.name.lexeme).map(|d| &d.state) == Some(&VariableState::Declared) 341 | { 342 | return Err(vec![Error::SelfReferencingInitializer { 343 | name: expr.name.lexeme.clone(), 344 | location: expr.name.location, 345 | }]); 346 | } 347 | } 348 | 349 | self.resolve_local(&expr.name, state, true) 350 | } 351 | 352 | fn visit_assign(self, expr: &crate::ast::Assign, state: &mut State) -> Result { 353 | combine_results( 354 | expr.value.walk(&mut *self, state), 355 | self.resolve_local(&expr.name, state, false), 356 | ) 357 | } 358 | 359 | fn visit_literal(self, _expr: &crate::ast::Literal, _state: &mut State) -> Result { 360 | Ok(()) 361 | } 362 | 363 | fn visit_unary(self, expr: &crate::ast::Unary, state: &mut State) -> Result { 364 | expr.right.walk(self, state) 365 | } 366 | 367 | fn visit_binary(self, expr: &crate::ast::Binary, state: &mut State) -> Result { 368 | combine_results( 369 | expr.left.walk(&mut *self, state), 370 | expr.right.walk(self, state), 371 | ) 372 | } 373 | 374 | fn visit_call(self, expr: &crate::ast::Call, state: &mut State) -> Result { 375 | let callee_result = expr.callee.walk(&mut *self, state); 376 | expr.arguments 377 | .iter() 378 | .map(|arg| arg.walk(&mut *self, state)) 379 | .fold(callee_result, combine_results) 380 | } 381 | 382 | fn visit_logical(self, expr: &crate::ast::Logical, state: &mut State) -> Result { 383 | combine_results( 384 | expr.left.walk(&mut *self, state), 385 | expr.right.walk(self, state), 386 | ) 387 | } 388 | 389 | fn visit_lambda(self, expr: &crate::ast::Lambda, state: &mut State) -> Result { 390 | self.resolve_function(&expr.params, &expr.body, FunctionType::Lambda, state) 391 | } 392 | 393 | fn visit_ternary(self, expr: &crate::ast::Ternary, state: &mut State) -> Result { 394 | combine_many_results( 395 | [&expr.left, &expr.mid, &expr.right] 396 | .into_iter() 397 | .map(|expr| expr.walk(&mut *self, state)), 398 | ) 399 | } 400 | 401 | fn visit_grouping(self, expr: &crate::ast::Grouping, state: &mut State) -> Result { 402 | expr.expr.walk(self, state) 403 | } 404 | 405 | fn visit_get(self, expr: &crate::ast::Get, state: &mut State) -> Result { 406 | expr.object.walk(self, state) 407 | } 408 | 409 | fn visit_set(self, expr: &crate::ast::Set, state: &mut State) -> Result { 410 | combine_results( 411 | expr.value.walk(&mut *self, state), 412 | expr.object.walk(self, state), 413 | ) 414 | } 415 | 416 | fn visit_this(self, expr: &crate::ast::This, state: &mut State) -> Result { 417 | if self.current_class == ClassType::None { 418 | Err(vec![Error::ThisOutsideClass { 419 | location: expr.keyword.location, 420 | }]) 421 | } else if self.current_function == FunctionType::ClassMethod { 422 | Err(vec![Error::ThisInStaticMethod { 423 | location: expr.keyword.location, 424 | }]) 425 | } else { 426 | self.resolve_local(&expr.keyword, state, true) 427 | } 428 | } 429 | 430 | fn visit_super(self, expr: &crate::ast::Super, state: &mut State) -> Result { 431 | let mut result = Ok(()); 432 | if self.current_class == ClassType::None { 433 | result = combine_results( 434 | result, 435 | Err(vec![Error::SuperOutsideClass { 436 | location: expr.keyword.location, 437 | }]), 438 | ); 439 | } else if self.current_class == ClassType::Class { 440 | result = combine_results( 441 | result, 442 | Err(vec![Error::SuperWithoutSuperclass { 443 | location: expr.keyword.location, 444 | }]), 445 | ); 446 | } 447 | combine_many_results([ 448 | result, 449 | self.resolve_local(&expr.keyword, state, true), 450 | self.resolve_local(&expr.method, state, true), 451 | ]) 452 | } 453 | } 454 | 455 | impl StmtVisitor for &mut Resolver { 456 | fn visit_block(self, stmt: &crate::ast::Block, state: &mut State) -> Result { 457 | self.begin_scope(); 458 | combine_results( 459 | self.resolve_statements(&stmt.statements, state), 460 | self.end_scope(), 461 | ) 462 | } 463 | 464 | fn visit_expression(self, stmt: &crate::ast::Expression, state: &mut State) -> Result { 465 | stmt.expr.walk(self, state) 466 | } 467 | 468 | fn visit_function(self, stmt: &crate::ast::Function, state: &mut State) -> Result { 469 | combine_many_results([ 470 | self.declare(&stmt.name), 471 | self.define(&stmt.name), 472 | self.resolve_function(&stmt.params, &stmt.body, FunctionType::Function, state), 473 | self.resolve_local(&stmt.name, state, false), 474 | ]) 475 | } 476 | 477 | fn visit_if(self, stmt: &crate::ast::If, state: &mut State) -> Result { 478 | let result = combine_results( 479 | stmt.condition.walk(&mut *self, state), 480 | stmt.then_branch.walk(&mut *self, state), 481 | ); 482 | if let Some(else_branch) = &stmt.else_branch { 483 | combine_results(result, else_branch.walk(self, state)) 484 | } else { 485 | result 486 | } 487 | } 488 | 489 | fn visit_print(self, stmt: &crate::ast::Print, state: &mut State) -> Result { 490 | stmt.expr.walk(self, state) 491 | } 492 | 493 | fn visit_return(self, stmt: &crate::ast::Return, state: &mut State) -> Result { 494 | if self.current_function == FunctionType::None { 495 | return Err(vec![Error::ReturnOutsideFunction { 496 | location: stmt.keyword.location, 497 | }]); 498 | } 499 | 500 | if let Some(expr) = &stmt.value { 501 | if let FunctionType::Initializer(class) = &self.current_function { 502 | return Err(vec![Error::ReturnFromInitializer { 503 | class: class.clone(), 504 | location: stmt.keyword.location, 505 | }]); 506 | } 507 | expr.walk(self, state) 508 | } else { 509 | Ok(()) 510 | } 511 | } 512 | 513 | fn visit_var(self, stmt: &crate::ast::Var, state: &mut State) -> Result { 514 | combine_many_results([ 515 | self.declare(&stmt.name), 516 | if let Some(initializer) = &stmt.initializer { 517 | initializer.walk(&mut *self, state) 518 | } else { 519 | Ok(()) 520 | }, 521 | self.define(&stmt.name), 522 | self.resolve_local(&stmt.name, state, false), 523 | ]) 524 | } 525 | 526 | fn visit_while(self, stmt: &crate::ast::While, state: &mut State) -> Result { 527 | combine_results( 528 | stmt.condition.walk(&mut *self, state), 529 | stmt.statement.walk(self, state), 530 | ) 531 | } 532 | 533 | fn visit_break(self, _stmt: &crate::ast::Break, _state: &mut State) -> Result { 534 | Ok(()) 535 | } 536 | 537 | fn visit_class(self, stmt: &crate::ast::Class, state: &mut State) -> Result { 538 | let enclosing_class = std::mem::replace(&mut self.current_class, ClassType::Class); 539 | 540 | let mut result = combine_many_results([ 541 | self.declare(&stmt.name), 542 | self.define(&stmt.name), 543 | self.resolve_local(&stmt.name, state, false), 544 | ]); 545 | 546 | if let Some(superclass) = &stmt.superclass { 547 | self.current_class = ClassType::Subclass; 548 | if superclass.1.name.lexeme == stmt.name.lexeme { 549 | result = combine_results( 550 | result, 551 | Err(vec![Error::SelfInheritance { 552 | class: stmt.name.lexeme.clone(), 553 | location: superclass.1.name.location, 554 | }]), 555 | ); 556 | } 557 | 558 | result = combine_results( 559 | result, 560 | crate::ast::Expr::Variable(superclass.1.clone()).walk(&mut *self, state), 561 | ); 562 | 563 | self.begin_scope(); 564 | let scope = self.scopes.last_mut().unwrap(); 565 | let index_in_scope = scope.reserve(); 566 | scope.vars.insert( 567 | "super".to_string(), 568 | VariableData { 569 | state: VariableState::Used, 570 | declared_at: superclass.0.location, 571 | index_in_scope, 572 | }, 573 | ); 574 | state.insert( 575 | superclass.0.location, 576 | Binding { 577 | scopes_up: 0, 578 | index_in_scope, 579 | }, 580 | ); 581 | } 582 | 583 | self.begin_scope(); 584 | { 585 | let scope = self.scopes.last_mut().unwrap(); 586 | let index_in_scope = scope.reserve(); 587 | scope.vars.insert( 588 | "this".to_string(), 589 | VariableData { 590 | state: VariableState::Used, 591 | declared_at: stmt.left_brace.location, 592 | index_in_scope, 593 | }, 594 | ); 595 | state.insert( 596 | stmt.left_brace.location, 597 | Binding { 598 | scopes_up: 0, 599 | index_in_scope, 600 | }, 601 | ); 602 | } 603 | 604 | for method in &stmt.methods { 605 | let declaration = if method.name.lexeme == "init" { 606 | FunctionType::Initializer(stmt.name.lexeme.clone()) 607 | } else { 608 | FunctionType::Method 609 | }; 610 | result = combine_results( 611 | result, 612 | self.resolve_function(&method.params, &method.body, declaration, state), 613 | ); 614 | } 615 | 616 | for class_method in &stmt.class_methods { 617 | result = combine_results( 618 | result, 619 | self.resolve_function( 620 | &class_method.params, 621 | &class_method.body, 622 | FunctionType::ClassMethod, 623 | state, 624 | ), 625 | ); 626 | } 627 | 628 | for getter in &stmt.getters { 629 | result = combine_results( 630 | result, 631 | self.resolve_function(&getter.params, &getter.body, FunctionType::Function, state), 632 | ); 633 | } 634 | 635 | if stmt.superclass.is_some() { 636 | result = combine_results(result, self.end_scope()); 637 | } 638 | 639 | result = combine_results(result, self.end_scope()); 640 | self.current_class = enclosing_class; 641 | result 642 | } 643 | } 644 | -------------------------------------------------------------------------------- /src/parser.rs: -------------------------------------------------------------------------------- 1 | use macros::ResolveErrorLocation; 2 | use thiserror::Error; 3 | 4 | use crate::ast::*; 5 | use crate::token::Token; 6 | use crate::token::TokenValue as TV; 7 | use crate::types::SourceLocation; 8 | 9 | #[derive(Error, Debug, ResolveErrorLocation)] 10 | pub enum Error { 11 | #[error("{msg} at {location}")] 12 | Bad { 13 | msg: String, 14 | location: SourceLocation, 15 | }, 16 | 17 | #[error("LHS missing for `{operator}` at {location}")] 18 | MissingLhs { 19 | operator: String, 20 | location: SourceLocation, 21 | }, 22 | 23 | #[error("Invalid assignment target at {location}: `{target}`")] 24 | InvalidAssignmentTarget { 25 | target: Expr, 26 | location: SourceLocation, 27 | }, 28 | 29 | #[error("`break` outside loop at {location}")] 30 | BreakOutsideLoop { location: SourceLocation }, 31 | 32 | #[error("Can't have more than 255 arguments. Call site: {location}")] 33 | TooManyArguments { location: SourceLocation }, 34 | } 35 | 36 | type Result = std::result::Result; 37 | 38 | /// program = declaration* EOF ; 39 | /// 40 | /// declaration = classDecl 41 | /// | funDecl 42 | /// | varDecl 43 | /// | statement ; 44 | /// classDecl = "class" IDENTIFIER ( "<" IDENTIFIER )? 45 | /// "{" method* "}" ; 46 | /// method = "class"? function 47 | /// | getter ; 48 | /// getter = IDENTIFIER block ; 49 | /// funDecl = "fun" function ; 50 | /// function = IDENTIFIER "(" parameters? ")" block ; 51 | /// parameters = IDENTIFIER ( "," IDENTIFIER )* ; 52 | /// varDecl = "var" IDENTIFIER ( "=" expression )? ";" ; 53 | /// 54 | /// statement = exprStmt 55 | /// | ifStmt 56 | /// | returnStmt 57 | /// | printStmt 58 | /// | whileStmt 59 | /// | forStmt 60 | /// | breakStmt 61 | /// | block ; 62 | /// exprStmt = expression ";" ; 63 | /// ifStmt = "if" "(" expression ")" statement 64 | /// ( "else" statement )? ; 65 | /// printStmt = "print" expression ";" ; 66 | /// whileStmt = "while" "(" expression ")" statement ; 67 | /// forStmt = "for" "(" 68 | /// (varDecl | exprStmt | ";") 69 | /// expression? ";" 70 | /// expression? 71 | /// ")" statement ; 72 | /// breakStmt = "break" ; 73 | /// block = "{" declaration "}" ; 74 | /// 75 | /// expression = comma ; 76 | /// comma = assignment ( ( "," ) assignment )* ; 77 | /// assignment = "fun" lambda 78 | /// | ( call "." )? IDENTIFIER "=" assignment 79 | /// | ternary ; 80 | /// lambda = "(" parameters? ")" block ; 81 | /// ternary = logic_or ( "?" expression ":" expression )* ; 82 | /// logic_or = logic_and ( "or" logic_and )* ; 83 | /// logic_and = equality ( "and" equality )* ; 84 | /// equality = comparison ( ( "!=" | "==" ) comparison )* 85 | /// comparison = term ( ( ">" | "<" | "<=" | ">=" ) term )* ; 86 | /// term = factor ( ( "-" | "+" ) factor )* ; 87 | /// factor = unary ( ( "/" | "*" ) unary )* ; 88 | /// unary = ( "!" | "-" ) unary 89 | /// | call ; 90 | /// call = primary ( "(" arguments ")" | "." IDENTIFIER )* ; 91 | /// arguments = expression ( "," expression )* ; 92 | /// primary = NUMBER | STRING | "true" | "false" | "nil" 93 | /// | "(" expression ")" 94 | /// | IDENTIFIER 95 | /// | "super" "." IDENTIFIER ; 96 | pub struct Parser { 97 | tokens: Vec, 98 | errors: Vec, 99 | current: usize, 100 | loop_depth: usize, // Used to raise a syntax error on `break` outside a loop 101 | } 102 | 103 | trait TokenPred { 104 | fn matches(&self, token: &Token) -> bool; 105 | } 106 | 107 | impl TokenPred for TV { 108 | fn matches(&self, token: &Token) -> bool { 109 | &token.value == self 110 | } 111 | } 112 | 113 | impl TokenPred for F 114 | where 115 | F: Fn(&Token) -> bool, 116 | { 117 | fn matches(&self, token: &Token) -> bool { 118 | self(token) 119 | } 120 | } 121 | 122 | impl Parser { 123 | #[must_use] 124 | pub fn new(tokens: Vec) -> Self { 125 | Self { 126 | tokens, 127 | errors: vec![], 128 | current: 0, 129 | loop_depth: 0, 130 | } 131 | } 132 | 133 | pub fn parse(&mut self) -> Result, Vec> { 134 | let mut statements = vec![]; 135 | 136 | while !self.is_at_end() { 137 | match self.declaration() { 138 | Ok(s) => { 139 | statements.push(s); 140 | } 141 | Err(e) => { 142 | self.errors.push(e); 143 | return Err(std::mem::take(&mut self.errors)); 144 | } 145 | } 146 | } 147 | 148 | if self.errors.is_empty() { 149 | Ok(statements) 150 | } else { 151 | Err(std::mem::take(&mut self.errors)) 152 | } 153 | } 154 | 155 | fn is_at_end(&self) -> bool { 156 | self.tokens[self.current].value == TV::Eof 157 | } 158 | 159 | fn match_(&mut self, preds: &[impl TokenPred]) -> bool { 160 | for pred in preds { 161 | if self.check(pred) { 162 | self.advance(); 163 | return true; 164 | } 165 | } 166 | false 167 | } 168 | 169 | fn consume(&mut self, pred: &impl TokenPred, msg: S) -> Result<&Token> { 170 | if self.check(pred) { 171 | Ok(self.advance()) 172 | } else { 173 | Err(Error::Bad { 174 | msg: msg.to_string(), 175 | location: self.peek().location, 176 | }) 177 | } 178 | } 179 | 180 | fn consume_identifier(&mut self, msg: S) -> Result<&Token> { 181 | self.consume(&|t: &Token| matches!(t.value, TV::Identifier(_)), msg) 182 | } 183 | 184 | fn check(&self, pred: &impl TokenPred) -> bool { 185 | self.tokens 186 | .get(self.current) 187 | .map_or(false, |t| pred.matches(t)) 188 | } 189 | 190 | fn advance(&mut self) -> &Token { 191 | if !self.is_at_end() { 192 | self.current += 1; 193 | } 194 | self.previous() 195 | } 196 | 197 | fn previous(&self) -> &Token { 198 | &self.tokens[self.current - 1] 199 | } 200 | 201 | fn peek(&self) -> &Token { 202 | &self.tokens[self.current] 203 | } 204 | 205 | fn peek_next(&self) -> Option<&Token> { 206 | self.tokens.get(self.current + 1) 207 | } 208 | 209 | fn declaration(&mut self) -> Result { 210 | let res = if self.match_(&[TV::Fun]) { 211 | self.function_statement() 212 | } else if self.match_(&[TV::Class]) { 213 | self.class_declaration() 214 | } else if self.match_(&[TV::Var]) { 215 | self.var_declaration() 216 | } else { 217 | self.statement() 218 | }; 219 | if res.is_err() { 220 | self.synchronize(); 221 | } 222 | res 223 | } 224 | 225 | fn function_statement(&mut self) -> Result { 226 | if !self.check(&|t: &Token| matches!(t.value, TV::Identifier(_))) { 227 | let lambda = self.lambda()?; 228 | self.consume( 229 | &TV::Semicolon, 230 | "Expected `;` after anonymous function expression statement", 231 | )?; 232 | return Ok(Stmt::Expression(Expression { expr: lambda })); 233 | } 234 | 235 | Ok(Stmt::Function(self.function("function")?)) 236 | } 237 | 238 | fn function(&mut self, kind: &str) -> Result { 239 | let name = self 240 | .consume_identifier(format!("Expected {} name", kind))? 241 | .clone(); 242 | self.consume(&TV::LeftParen, format!("Expected `(` after {} name", kind))?; 243 | 244 | let params = self.parameters(name.location)?; 245 | let body = self.body(kind)?; 246 | Ok(Function { name, params, body }) 247 | } 248 | 249 | fn parameters(&mut self, location: SourceLocation) -> Result> { 250 | let mut params = vec![]; 251 | if !self.check(&TV::RightParen) { 252 | loop { 253 | if params.len() >= 255 { 254 | return Err(Error::TooManyArguments { location }); 255 | } 256 | params.push( 257 | self.consume( 258 | &|t: &Token| matches!(t.value, TV::Identifier(_)), 259 | "Expected parameter name", 260 | )? 261 | .clone(), 262 | ); 263 | 264 | if !self.match_(&[TV::Comma]) { 265 | break; 266 | } 267 | } 268 | } 269 | self.consume(&TV::RightParen, "Expected `)` after parameters")?; 270 | Ok(params) 271 | } 272 | 273 | fn body(&mut self, kind: &str) -> Result> { 274 | self.consume( 275 | &TV::LeftBrace, 276 | format!("Expected `{{` before {} body", kind), 277 | )?; 278 | match self.block()? { 279 | Stmt::Block(Block { statements }) => Ok(statements), 280 | _ => { 281 | unreachable!("Internal error: `Parser::block` somehow returned NOT a `Stmt::Block`") 282 | } 283 | } 284 | } 285 | 286 | fn var_declaration(&mut self) -> Result { 287 | let name = self.consume_identifier("Expected variable name")?.clone(); 288 | let initializer = if self.match_(&[TV::Equal]) { 289 | Some(Box::new(self.expression()?)) 290 | } else { 291 | None 292 | }; 293 | self.consume(&TV::Semicolon, "Expected `;` after variable declaration")?; 294 | 295 | Ok(Stmt::Var(Var { name, initializer })) 296 | } 297 | 298 | fn class_declaration(&mut self) -> Result { 299 | let name = self.consume_identifier("Expected class name")?.clone(); 300 | 301 | let superclass = if self.match_(&[TV::Less]) { 302 | let less = self.previous().clone(); 303 | Some(( 304 | less, 305 | Variable { 306 | name: self.consume_identifier("Expected superclass name")?.clone(), 307 | }, 308 | )) 309 | } else { 310 | None 311 | }; 312 | 313 | let left_brace = self 314 | .consume(&TV::LeftBrace, "Expected `{` before class body")? 315 | .clone(); 316 | 317 | let mut methods = vec![]; 318 | let mut class_methods = vec![]; 319 | let mut getters = vec![]; 320 | 321 | while !self.check(&TV::RightBrace) && !self.is_at_end() { 322 | // class method 323 | if self.match_(&[TV::Class]) { 324 | class_methods.push(self.function("class method")?); 325 | 326 | // getter 327 | } else if matches!(self.peek_next(), Some(t) if t.value == TV::LeftBrace) { 328 | let name = self.consume_identifier("Expected getter name")?.clone(); 329 | let body = self.body("getter")?; 330 | getters.push(Function { 331 | name, 332 | params: vec![], 333 | body, 334 | }); 335 | 336 | // instance method 337 | } else { 338 | methods.push(self.function("method")?); 339 | } 340 | } 341 | 342 | self.consume(&TV::RightBrace, "Expected `}` after class body")?; 343 | Ok(Stmt::Class(Class { 344 | name, 345 | superclass: superclass.map(Box::new), 346 | methods, 347 | class_methods, 348 | getters, 349 | left_brace, 350 | })) 351 | } 352 | 353 | fn statement(&mut self) -> Result { 354 | if self.match_(&[TV::Print]) { 355 | self.print_statement() 356 | } else if self.match_(&[TV::LeftBrace]) { 357 | self.block() 358 | } else if self.match_(&[TV::If]) { 359 | self.if_statement() 360 | } else if self.match_(&[TV::Return]) { 361 | self.return_statement() 362 | } else if self.match_(&[TV::While]) { 363 | self.while_statement() 364 | } else if self.match_(&[TV::For]) { 365 | self.for_statement() 366 | } else if self.match_(&[TV::Break]) { 367 | self.break_statement() 368 | } else { 369 | self.expression_statement() 370 | } 371 | } 372 | 373 | fn print_statement(&mut self) -> Result { 374 | let expr = self.expression()?; 375 | self.consume(&TV::Semicolon, "Expected `;` after value")?; 376 | Ok(Stmt::Print(Print { expr })) 377 | } 378 | 379 | fn block(&mut self) -> Result { 380 | let mut statements = vec![]; 381 | while !self.check(&TV::RightBrace) && !self.is_at_end() { 382 | statements.push(self.declaration()?); 383 | } 384 | self.consume(&TV::RightBrace, "Expected `}` after block")?; 385 | 386 | Ok(Stmt::Block(Block { statements })) 387 | } 388 | 389 | fn if_statement(&mut self) -> Result { 390 | self.consume(&TV::LeftParen, "Expected `(` after `if`")?; 391 | let condition = self.expression()?; 392 | self.consume(&TV::RightParen, "Expected `)` after `if` condition")?; 393 | let then_branch = self.statement()?; 394 | let else_branch = if self.match_(&[TV::Else]) { 395 | Some(self.statement()?) 396 | } else { 397 | None 398 | }; 399 | Ok(Stmt::If(If { 400 | condition: Box::new(condition), 401 | then_branch: Box::new(then_branch), 402 | else_branch: else_branch.map(Box::new), 403 | })) 404 | } 405 | 406 | fn return_statement(&mut self) -> Result { 407 | let keyword = self.previous().clone(); 408 | let value = if self.check(&TV::Semicolon) { 409 | None 410 | } else { 411 | Some(self.expression()?) 412 | }; 413 | 414 | self.consume(&TV::Semicolon, "Expected `;` after return value")?; 415 | Ok(Stmt::Return(Return { 416 | keyword, 417 | value: value.map(Box::new), 418 | })) 419 | } 420 | 421 | fn while_statement(&mut self) -> Result { 422 | self.consume(&TV::LeftParen, "Expected `(` after `while`")?; 423 | let condition = self.expression()?; 424 | self.consume(&TV::RightParen, "Expected `)` after `while` condition")?; 425 | 426 | self.loop_depth += 1; 427 | let statement = self.statement()?; 428 | self.loop_depth -= 1; 429 | 430 | Ok(Stmt::While(While { 431 | condition: Box::new(condition), 432 | statement: Box::new(statement), 433 | })) 434 | } 435 | 436 | /// Desugar to `while` loop 437 | fn for_statement(&mut self) -> Result { 438 | self.consume(&TV::LeftParen, "Expected `(` after `for`")?; 439 | 440 | let initializer = if self.match_(&[TV::Semicolon]) { 441 | None 442 | } else if self.match_(&[TV::Var]) { 443 | Some(self.var_declaration()?) 444 | } else { 445 | Some(self.expression_statement()?) 446 | }; 447 | 448 | let condition = if !self.check(&TV::Semicolon) { 449 | self.expression()? 450 | } else { 451 | Expr::Literal(Literal::True) 452 | }; 453 | self.consume(&TV::Semicolon, "Expected `;` after `for` condition")?; 454 | 455 | let increment = if !self.check(&TV::RightParen) { 456 | Some(self.expression()?) 457 | } else { 458 | None 459 | }; 460 | self.consume(&TV::RightParen, "Expected `)` after `for` clauses")?; 461 | 462 | self.loop_depth += 1; 463 | let mut body = self.statement()?; 464 | self.loop_depth -= 1; 465 | 466 | if let Some(increment) = increment { 467 | body = Stmt::Block(Block { 468 | statements: vec![body, Stmt::Expression(Expression { expr: increment })], 469 | }); 470 | } 471 | body = Stmt::While(While { 472 | condition: Box::new(condition), 473 | statement: Box::new(body), 474 | }); 475 | if let Some(initializer) = initializer { 476 | body = Stmt::Block(Block { 477 | statements: vec![initializer, body], 478 | }); 479 | } 480 | 481 | Ok(body) 482 | } 483 | 484 | fn break_statement(&mut self) -> Result { 485 | if self.loop_depth == 0 { 486 | Err(Error::BreakOutsideLoop { 487 | location: SourceLocation::new(self.previous().location.command(), self.current), 488 | }) 489 | } else { 490 | self.consume(&TV::Semicolon, "Expected `;` after `break`")?; 491 | Ok(Stmt::Break(Break { 492 | token: self.previous().clone(), 493 | })) 494 | } 495 | } 496 | 497 | fn expression_statement(&mut self) -> Result { 498 | let expr = self.expression()?; 499 | self.consume(&TV::Semicolon, "Expected `;` after expression")?; 500 | Ok(Stmt::Expression(Expression { expr })) 501 | } 502 | 503 | fn expression(&mut self) -> Result { 504 | self.comma() 505 | } 506 | 507 | /// Error production: missing LHS for binary operator 508 | fn _missing_lhs( 509 | &mut self, 510 | operators: &[TV], 511 | operand: fn(&mut Self) -> Result, 512 | ) -> Result<()> { 513 | if self.match_(operators) { 514 | self.errors.push(Error::MissingLhs { 515 | operator: self.previous().lexeme.clone(), 516 | location: self.previous().location, 517 | }); 518 | // Skip RHS 519 | operand(self)?; 520 | } 521 | Ok(()) 522 | } 523 | 524 | fn _left_assoc_binary( 525 | &mut self, 526 | operators: &[TV], 527 | operand: fn(&mut Self) -> Result, 528 | ) -> Result { 529 | self._missing_lhs(operators, operand)?; 530 | let mut expr = operand(self)?; 531 | 532 | while self.match_(operators) { 533 | let operator = self.previous().clone(); 534 | let right = operand(self)?; 535 | expr = Expr::Binary(Binary { 536 | left: Box::new(expr), 537 | operator, 538 | right: Box::new(right), 539 | }); 540 | } 541 | 542 | Ok(expr) 543 | } 544 | 545 | fn comma(&mut self) -> Result { 546 | self._left_assoc_binary(&[TV::Comma], Self::assignment) 547 | } 548 | 549 | fn assignment(&mut self) -> Result { 550 | if self.match_(&[TV::Fun]) { 551 | return self.lambda(); 552 | } 553 | 554 | let start = self.current; 555 | let expr = self.ternary()?; 556 | 557 | if self.match_(&[TV::Equal]) { 558 | let value = self.assignment()?; 559 | 560 | if let Expr::Variable(v) = expr { 561 | let name = v.name; 562 | Ok(Expr::Assign(Assign { 563 | name, 564 | value: Box::new(value), 565 | })) 566 | } else if let Expr::Get(Get { object, name }) = expr { 567 | Ok(Expr::Set(Set { 568 | object, 569 | name, 570 | value: Box::new(value), 571 | })) 572 | } else { 573 | Err(Error::InvalidAssignmentTarget { 574 | target: expr, 575 | location: SourceLocation::new(self.previous().location.command(), start), 576 | }) 577 | } 578 | } else { 579 | Ok(expr) 580 | } 581 | } 582 | 583 | fn lambda(&mut self) -> Result { 584 | let token = self.previous().clone(); 585 | self.consume(&TV::LeftParen, "Expected `(` after anonymous `fun`")?; 586 | let params = 587 | self.parameters(SourceLocation::new(token.location.command(), self.current))?; 588 | let body = self.body("lambda")?; 589 | Ok(Expr::Lambda(Lambda { 590 | token, 591 | params, 592 | body, 593 | })) 594 | } 595 | 596 | fn ternary(&mut self) -> Result { 597 | let mut children = vec![self.or()?]; 598 | 599 | while self.match_(&[TV::Question]) { 600 | children.push(self.expression()?); 601 | self.consume(&TV::Colon, ": expected")?; 602 | children.push(self.expression()?); 603 | } 604 | 605 | if children.len() == 1 { 606 | return Ok(children.pop().unwrap()); 607 | } 608 | 609 | let mut expr = Expr::Ternary(Ternary { 610 | right: Box::new(children.pop().unwrap()), 611 | mid: Box::new(children.pop().unwrap()), 612 | left: Box::new(children.pop().unwrap()), 613 | }); 614 | 615 | while !children.is_empty() { 616 | expr = Expr::Ternary(Ternary { 617 | mid: Box::new(children.pop().unwrap()), 618 | left: Box::new(children.pop().unwrap()), 619 | right: Box::new(expr), 620 | }); 621 | } 622 | 623 | Ok(expr) 624 | } 625 | 626 | fn or(&mut self) -> Result { 627 | let mut expr = self.and()?; 628 | 629 | while self.match_(&[TV::Or]) { 630 | let operator = self.previous().clone(); 631 | let right = self.and()?; 632 | expr = Expr::Logical(Logical { 633 | left: Box::new(expr), 634 | operator, 635 | right: Box::new(right), 636 | }); 637 | } 638 | 639 | Ok(expr) 640 | } 641 | 642 | fn and(&mut self) -> Result { 643 | let mut expr = self.equality()?; 644 | 645 | while self.match_(&[TV::And]) { 646 | let operator = self.previous().clone(); 647 | let right = self.equality()?; 648 | expr = Expr::Logical(Logical { 649 | left: Box::new(expr), 650 | operator, 651 | right: Box::new(right), 652 | }); 653 | } 654 | 655 | Ok(expr) 656 | } 657 | 658 | fn equality(&mut self) -> Result { 659 | self._left_assoc_binary(&[TV::BangEqual, TV::EqualEqual], Self::comparison) 660 | } 661 | 662 | fn comparison(&mut self) -> Result { 663 | self._left_assoc_binary( 664 | &[TV::Greater, TV::GreaterEqual, TV::Less, TV::LessEqual], 665 | Self::term, 666 | ) 667 | } 668 | 669 | fn term(&mut self) -> Result { 670 | self._left_assoc_binary(&[TV::Minus, TV::Plus], Self::factor) 671 | } 672 | 673 | fn factor(&mut self) -> Result { 674 | self._left_assoc_binary(&[TV::Slash, TV::Star], Self::unary) 675 | } 676 | 677 | fn unary(&mut self) -> Result { 678 | if self.match_(&[TV::Bang, TV::Minus]) { 679 | let operator = self.previous().clone(); 680 | let right = self.unary()?; 681 | Ok(Expr::Unary(Unary { 682 | operator, 683 | right: Box::new(right), 684 | })) 685 | } else { 686 | self.call() 687 | } 688 | } 689 | 690 | fn call(&mut self) -> Result { 691 | let mut expr = self.primary()?; 692 | 693 | loop { 694 | if self.match_(&[TV::LeftParen]) { 695 | expr = self.finish_call(expr)?; 696 | } else if self.match_(&[TV::Dot]) { 697 | let name = self 698 | .consume_identifier("Expected property name after `.`")? 699 | .clone(); 700 | expr = Expr::Get(Get { 701 | name, 702 | object: Box::new(expr), 703 | }) 704 | } else { 705 | break; 706 | } 707 | } 708 | 709 | Ok(expr) 710 | } 711 | 712 | fn finish_call(&mut self, callee: Expr) -> Result { 713 | let mut arguments = vec![]; 714 | let start = self.current; 715 | if !self.check(&TV::RightParen) { 716 | loop { 717 | if arguments.len() >= 255 { 718 | return Err(Error::TooManyArguments { 719 | location: SourceLocation::new(self.previous().location.command(), start), 720 | }); 721 | } 722 | arguments.push(self.assignment()?); 723 | if !self.match_(&[TV::Comma]) { 724 | break; 725 | } 726 | } 727 | } 728 | let paren = self.consume(&TV::RightParen, "Expected `)` after arguments.")?; 729 | Ok(Expr::Call(Call { 730 | callee: Box::new(callee), 731 | arguments, 732 | closing_paren: paren.clone(), 733 | })) 734 | } 735 | 736 | fn primary(&mut self) -> Result { 737 | self.advance(); 738 | match &self.previous().value { 739 | TV::False => Ok(Expr::Literal(Literal::False)), 740 | TV::True => Ok(Expr::Literal(Literal::True)), 741 | TV::Nil => Ok(Expr::Literal(Literal::Nil)), 742 | TV::Number(n) => Ok(Expr::Literal(Literal::Number(*n))), 743 | TV::String(s) => Ok(Expr::Literal(Literal::String(s.clone()))), 744 | TV::LeftParen => { 745 | let expr = self.expression()?; 746 | self.consume(&TV::RightParen, "Expected `)` after expression.")?; 747 | Ok(Expr::Grouping(Grouping { 748 | expr: Box::new(expr), 749 | })) 750 | } 751 | TV::Identifier(_) => Ok(Expr::Variable(Variable { 752 | name: self.previous().clone(), 753 | })), 754 | TV::This => Ok(Expr::This(This { 755 | keyword: self.previous().clone(), 756 | })), 757 | TV::Super => { 758 | let keyword = self.previous().clone(); 759 | self.consume(&TV::Dot, "Expected `.` after `super`")?; 760 | let method = self 761 | .consume_identifier("Expected superclass method name")? 762 | .clone(); 763 | Ok(Expr::Super(Super { keyword, method })) 764 | } 765 | t => Err(Error::Bad { 766 | msg: format!("Expected expression, found: `{}`", t), 767 | location: self.previous().location, 768 | }), 769 | } 770 | } 771 | 772 | fn synchronize(&mut self) { 773 | self.advance(); 774 | 775 | while !self.is_at_end() { 776 | if self.previous().value == TV::Semicolon { 777 | return; 778 | } 779 | 780 | if matches!( 781 | self.peek().value, 782 | TV::Class 783 | | TV::Fun 784 | | TV::Var 785 | | TV::For 786 | | TV::If 787 | | TV::While 788 | | TV::Print 789 | | TV::Return 790 | ) { 791 | return; 792 | } 793 | 794 | self.advance(); 795 | } 796 | } 797 | } 798 | 799 | #[cfg(test)] 800 | mod test { 801 | use super::*; 802 | use crate::scanner::Scanner; 803 | use crate::types::ResolveErrorLocation; 804 | 805 | fn expr_parses_to(input: &str, expected: &str) { 806 | let tokens = Scanner::new(format!("{};", input).as_bytes(), 0) 807 | .scan_tokens() 808 | .unwrap(); 809 | let statements = Parser::new(tokens).parse().unwrap(); 810 | let expr = match &statements[0] { 811 | Stmt::Expression(Expression { expr }) => expr, 812 | _ => unreachable!(), 813 | }; 814 | assert_eq!(expected, format!("{}", expr)); 815 | } 816 | 817 | #[test] 818 | fn test_expression() { 819 | expr_parses_to( 820 | "1 + 2, (3 + 4) * 5 / 6 == 7", 821 | "((1 + 2) , (((((3 + 4)) * 5) / 6) == 7))", 822 | ); 823 | } 824 | 825 | #[test] 826 | fn test_math_left_assoc() { 827 | expr_parses_to("1 + 2 - 3 + 4", "(((1 + 2) - 3) + 4)"); 828 | expr_parses_to("1 * 2 / 3 * 4", "(((1 * 2) / 3) * 4)"); 829 | } 830 | 831 | #[test] 832 | fn test_equality_right_assoc() { 833 | expr_parses_to("1 == 2 != 3 == 4", "(((1 == 2) != 3) == 4)"); 834 | } 835 | 836 | #[test] 837 | fn test_comparison_right_assoc() { 838 | expr_parses_to("1 < 2 <= 3 > 4 >= 5", "((((1 < 2) <= 3) > 4) >= 5)"); 839 | } 840 | 841 | #[test] 842 | fn test_ternary() { 843 | expr_parses_to("1 < 2 ? 3 + 4 : 5 + 6", "((1 < 2) ? ((3 + 4)) : (5 + 6))"); 844 | } 845 | 846 | #[test] 847 | fn test_ternary_right_assoc() { 848 | expr_parses_to("0 ? 1 + 2 : 2 ? 3 : 4", "(0 ? ((1 + 2)) : (2 ? (3) : 4))"); 849 | } 850 | 851 | #[test] 852 | fn test_missing_lhs() { 853 | let input = "+ 3;"; 854 | let tokens = Scanner::new(input.as_bytes(), 0).scan_tokens().unwrap(); 855 | let mut errors = Parser::new(tokens).parse().err().unwrap(); 856 | assert_eq!( 857 | vec![ 858 | "LHS missing for `+` at 0:0".to_string(), 859 | "Expected expression, found: `;` at 0:3".to_string() 860 | ], 861 | errors 862 | .iter_mut() 863 | .map(|e| { 864 | e.resolve(input.as_bytes()); 865 | e.to_string() 866 | }) 867 | .collect::>() 868 | ); 869 | } 870 | } 871 | -------------------------------------------------------------------------------- /src/interpreter.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::RefCell, collections::HashMap, rc::Rc}; 2 | 3 | use derivative::Derivative; 4 | use macros::ResolveErrorLocation; 5 | use thiserror::Error; 6 | 7 | use crate::{ 8 | ast::{Expr, ExprVisitor, Literal, Stmt, StmtVisitor, Walkable}, 9 | environment::{GlobalEnvironment, LocalEnvironment, Variable}, 10 | resolver::{Binding, Bindings, CommandIndex}, 11 | token::{Token, TokenValue}, 12 | types::{Number, SourceLocation}, 13 | }; 14 | 15 | trait Callable { 16 | fn arity(&self) -> usize; 17 | fn call( 18 | &self, 19 | interpreter: &mut Interpreter, 20 | args: Vec>>, 21 | scope: Locals, 22 | ) -> Result>>; 23 | } 24 | 25 | fn call_common( 26 | interpreter: &mut Interpreter, 27 | args: Vec>>, 28 | scope: Option>>, 29 | params: &[Token], 30 | body: &[Stmt], 31 | ) -> Result>, Error> { 32 | LocalEnvironment::nested(OptRc::clone(&scope), |function_scope| { 33 | for (param, arg) in params.iter().zip(args) { 34 | function_scope 35 | .borrow_mut() 36 | .assign(interpreter.binding(param).unwrap(), Some(arg)) 37 | } 38 | interpreter 39 | .execute_block(body, Some(function_scope)) 40 | .map(Option::unwrap_or_default) 41 | }) 42 | } 43 | 44 | impl Callable for crate::ast::Function { 45 | fn arity(&self) -> usize { 46 | self.params.len() 47 | } 48 | 49 | fn call( 50 | &self, 51 | interpreter: &mut Interpreter, 52 | args: Vec>>, 53 | scope: Locals, 54 | ) -> Result>> { 55 | call_common(interpreter, args, scope, &self.params, &self.body) 56 | } 57 | } 58 | 59 | impl Callable for crate::ast::Lambda { 60 | fn arity(&self) -> usize { 61 | self.params.len() 62 | } 63 | 64 | fn call( 65 | &self, 66 | interpreter: &mut Interpreter, 67 | args: Vec>>, 68 | scope: Locals, 69 | ) -> Result>> { 70 | call_common(interpreter, args, scope, &self.params, &self.body) 71 | } 72 | } 73 | 74 | #[derive(Debug, PartialEq, Clone)] 75 | pub struct Class { 76 | name: String, 77 | superclass: Option>>, 78 | methods: HashMap>>, 79 | class_methods: HashMap>>, 80 | getters: HashMap>>, 81 | left_brace: Token, 82 | } 83 | 84 | impl std::fmt::Display for Class { 85 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 86 | write!(f, "", self.name) 87 | } 88 | } 89 | 90 | impl Class { 91 | fn find_method(&self, name: &str) -> Option>> { 92 | self.methods.get(name).map(Rc::clone).or_else(|| { 93 | self.superclass 94 | .as_ref() 95 | .and_then(|superclass| superclass.borrow().find_method(name)) 96 | }) 97 | } 98 | 99 | fn find_class_method(&self, name: &str) -> Option>> { 100 | self.class_methods.get(name).map(Rc::clone).or_else(|| { 101 | self.superclass 102 | .as_ref() 103 | .and_then(|superclass| superclass.borrow().find_class_method(name)) 104 | }) 105 | } 106 | 107 | fn find_getter(&self, name: &str) -> Option>> { 108 | self.getters.get(name).map(Rc::clone).or_else(|| { 109 | self.superclass 110 | .as_ref() 111 | .and_then(|superclass| superclass.borrow().find_getter(name)) 112 | }) 113 | } 114 | } 115 | 116 | impl Callable for Rc> { 117 | fn arity(&self) -> usize { 118 | if let Some(init) = self.borrow().find_method("init") { 119 | init.borrow().arity() 120 | } else { 121 | 0 122 | } 123 | } 124 | 125 | fn call( 126 | &self, 127 | interpreter: &mut Interpreter, 128 | args: Vec>>, 129 | scope: Locals, 130 | ) -> Result>> { 131 | let instance = Instance { 132 | class: Rc::clone(self), 133 | fields: Default::default(), 134 | }; 135 | let instance = Rc::new(RefCell::new(Value::Instance(instance))); 136 | if let Some(init) = self.borrow().find_method("init") { 137 | interpreter 138 | .bind_this(&init.borrow(), Rc::clone(&instance)) 139 | .call(interpreter, args, scope)?; 140 | } 141 | Ok(instance) 142 | } 143 | } 144 | 145 | #[derive(Debug, PartialEq, Clone)] 146 | pub struct Instance { 147 | class: Rc>, 148 | fields: HashMap>>, 149 | } 150 | 151 | impl Instance { 152 | fn set(&mut self, name: &Token, value: &Rc>) { 153 | self.fields.insert(name.lexeme.clone(), Rc::clone(value)); 154 | } 155 | } 156 | 157 | #[derive(Derivative)] 158 | #[derivative(Debug, PartialEq, Clone)] 159 | pub struct NativeFunction { 160 | pub name: String, 161 | pub arity: usize, 162 | #[derivative( 163 | Debug = "ignore", 164 | // Treat the implementation as always equal; we discriminate built-in functions by name 165 | PartialEq(compare_with = "always_equals"), 166 | )] 167 | #[allow(clippy::type_complexity)] 168 | pub fun: fn(&mut Interpreter, Vec>>) -> Value, 169 | } 170 | 171 | impl Callable for NativeFunction { 172 | fn arity(&self) -> usize { 173 | self.arity 174 | } 175 | 176 | fn call( 177 | &self, 178 | interpreter: &mut Interpreter, 179 | args: Vec>>, 180 | _scope: Locals, 181 | ) -> Result>> { 182 | Ok(Rc::new(RefCell::new((self.fun)(interpreter, args)))) 183 | } 184 | } 185 | 186 | #[derive(Debug, PartialEq, Clone)] 187 | pub struct Function { 188 | declaration: crate::ast::Function, 189 | closure: Locals, 190 | force_return: Option>>, // Return value from constructor calls 191 | } 192 | 193 | impl Callable for Function { 194 | fn arity(&self) -> usize { 195 | self.declaration.params.len() 196 | } 197 | 198 | fn call( 199 | &self, 200 | interpreter: &mut Interpreter, 201 | args: Vec>>, 202 | _scope: Locals, 203 | ) -> Result>> { 204 | self.declaration 205 | .call(interpreter, args, OptRc::clone(&self.closure)) 206 | .map_err(|e| match (&self.force_return, e) { 207 | (Some(value), Error::Return { location, .. }) => Error::Return { 208 | location, 209 | value: Rc::clone(value), 210 | }, 211 | (_, e) => e, 212 | }) 213 | } 214 | } 215 | 216 | #[derive(Debug, PartialEq, Clone)] 217 | pub struct Lambda { 218 | declaration: crate::ast::Lambda, 219 | closure: Locals, 220 | } 221 | 222 | impl Callable for Lambda { 223 | fn arity(&self) -> usize { 224 | self.declaration.params.len() 225 | } 226 | 227 | fn call( 228 | &self, 229 | interpreter: &mut Interpreter, 230 | args: Vec>>, 231 | _scope: Locals, 232 | ) -> Result>> { 233 | self.declaration 234 | .call(interpreter, args, OptRc::clone(&self.closure)) 235 | } 236 | } 237 | 238 | #[derive(Derivative)] 239 | #[derivative(Debug, PartialEq, Clone, Default)] 240 | pub enum Value { 241 | #[derivative(Default)] 242 | Nil, 243 | Boolean(bool), 244 | Number(Number), 245 | String(String), 246 | 247 | NativeFunction(NativeFunction), 248 | Function(Function), 249 | Lambda(Lambda), 250 | 251 | Class(Rc>), 252 | Instance(Instance), 253 | } 254 | 255 | fn always_equals(_: &T, _: &T) -> bool { 256 | true 257 | } 258 | 259 | impl Value { 260 | fn is_truthy(&self) -> bool { 261 | !matches!(self, Self::Nil | Self::Boolean(false)) 262 | } 263 | 264 | pub fn type_of(&self) -> String { 265 | match self { 266 | Self::Nil => "Nil".to_string(), 267 | Self::Boolean(_) => "Boolean".to_string(), 268 | Self::Number(_) => "Number".to_string(), 269 | Self::String(_) => "String".to_string(), 270 | Self::NativeFunction { .. } => "Function".to_string(), 271 | Self::Function { .. } => "Function".to_string(), 272 | Self::Lambda { .. } => "Function".to_string(), 273 | Self::Class(..) => "Class".to_string(), 274 | Self::Instance(Instance { class, .. }) => format!("{}", class.borrow()), 275 | } 276 | } 277 | 278 | fn repr(&self) -> String { 279 | match self { 280 | Self::String(s) => format!("{:?}", s), 281 | v => format!("{}", v), 282 | } 283 | } 284 | } 285 | 286 | impl std::fmt::Display for Value { 287 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 288 | match self { 289 | Self::Nil => f.write_str("nil"), 290 | Self::Boolean(b) => b.fmt(f), 291 | Self::Number(n) => n.fmt(f), 292 | Self::String(s) => s.fmt(f), 293 | Self::NativeFunction(NativeFunction { name, .. }) => write!(f, "", name), 294 | Self::Function(Function { declaration, .. }) => { 295 | write!(f, "", declaration.name.lexeme) 296 | } 297 | Self::Lambda { .. } => write!(f, ""), 298 | Self::Class(class) => class.borrow().fmt(f), 299 | Self::Instance(Instance { class, .. }) => write!(f, "<{} object>", class.borrow().name), 300 | } 301 | } 302 | } 303 | 304 | #[derive(Error, Debug, ResolveErrorLocation)] 305 | pub enum Error { 306 | #[error( 307 | "`{operator}` expected one of: [{}], found {} of type {} at {location}", 308 | .expected.join(", "), 309 | .actual.repr(), .actual.type_of()) 310 | ] 311 | InvalidOperand { 312 | operator: TokenValue, 313 | expected: Vec, 314 | actual: Value, 315 | location: SourceLocation, 316 | }, 317 | 318 | #[error("Division by zero at {location}")] 319 | DivisionByZero { location: SourceLocation }, 320 | 321 | #[error("Undefined variable {name} at {location}")] 322 | UndefinedVariable { 323 | name: String, 324 | location: SourceLocation, 325 | }, 326 | 327 | #[error("Uninitialized variable {name} at {location}")] 328 | UninitializedVariable { 329 | name: String, 330 | location: SourceLocation, 331 | }, 332 | 333 | #[error("`break` executed without enclosing loop o.0")] 334 | Break { location: SourceLocation }, 335 | 336 | #[error("Can only call functions and classes, tried to call: `{what}` of type `{}` at {location}", .what.type_of())] 337 | NotCallable { 338 | what: Value, 339 | location: SourceLocation, 340 | }, 341 | 342 | #[error("Expected {expected} arguments but got {actual} at {location}")] 343 | WrongArity { 344 | expected: usize, 345 | actual: usize, 346 | location: SourceLocation, 347 | }, 348 | 349 | #[error("`return` outside function at {location}")] 350 | Return { 351 | location: SourceLocation, 352 | value: Rc>, 353 | }, 354 | 355 | #[error("Tried to access property `{property}` on non-object `{non_object}` of type `{}` at {location}", .non_object.type_of())] 356 | PropertyOnNonObject { 357 | non_object: Value, 358 | property: String, 359 | location: SourceLocation, 360 | }, 361 | 362 | #[error("Undefined property `{property}` on `{object}` at {location}")] 363 | UndefinedProperty { 364 | object: Value, 365 | property: String, 366 | location: SourceLocation, 367 | }, 368 | 369 | #[error("Class `{class}` has superclass `{superclass}` of type `{}` which is not a class at {location}", .superclass.type_of())] 370 | SuperclassNotClass { 371 | class: String, 372 | superclass: Value, 373 | location: SourceLocation, 374 | }, 375 | } 376 | 377 | pub type Result>>, E = Error> = std::result::Result; 378 | type Globals = Rc>; 379 | type Locals = Option>>; 380 | 381 | // Convenience syntax: `OptRc::clone`, to remain explicit about how cloning a `Locals` 382 | // establishes a new `Rc` reference 383 | struct OptRc {} 384 | impl OptRc { 385 | fn clone(x: &Option>>) -> Option>> { 386 | x.as_ref().cloned() 387 | } 388 | } 389 | 390 | pub struct Interpreter<'a> { 391 | pub command: CommandIndex, 392 | output: Box, 393 | bindings: Bindings, 394 | globals: Globals, 395 | } 396 | 397 | impl<'a> Interpreter<'a> { 398 | #[must_use] 399 | pub fn new(globals: Globals, output: Box) -> Self { 400 | Self { 401 | globals, 402 | output, 403 | command: Default::default(), 404 | bindings: Default::default(), 405 | } 406 | } 407 | 408 | pub fn update_bindings(&mut self, bindings: Bindings) { 409 | self.bindings.extend(bindings.into_iter()); 410 | } 411 | 412 | pub fn binding(&self, name: &Token) -> Option<&Binding> { 413 | self.bindings.get(&name.location) 414 | } 415 | 416 | fn bind_this(&self, method: &Function, instance: Rc>) -> Function { 417 | if let Value::Instance(Instance { class, .. }) = &*instance.borrow() { 418 | LocalEnvironment::nested(OptRc::clone(&method.closure), |environment| { 419 | environment.borrow_mut().assign( 420 | self.binding(&class.borrow().left_brace).unwrap(), 421 | Some(Rc::clone(&instance)), 422 | ); 423 | Function { 424 | declaration: method.declaration.clone(), 425 | closure: Some(environment), 426 | force_return: if method.declaration.name.lexeme == "init" { 427 | Some(Rc::clone(&instance)) 428 | } else { 429 | None 430 | }, 431 | } 432 | }) 433 | } else { 434 | unreachable!("bind_this on non-instance") 435 | } 436 | } 437 | 438 | fn evaluate(&mut self, expr: &Expr, locals: Locals) -> Result>> { 439 | expr.walk(self, locals) 440 | } 441 | 442 | pub fn interpret(&mut self, program: &[Stmt]) -> Result { 443 | let mut ret = None; 444 | for stmt in program { 445 | ret = self.execute(stmt, None)?; 446 | } 447 | Ok(ret) 448 | } 449 | 450 | fn execute(&mut self, stmt: &Stmt, locals: Locals) -> Result { 451 | stmt.walk(self, locals) 452 | } 453 | 454 | fn err_invalid_operand( 455 | &self, 456 | op: &Token, 457 | expected: &[S], 458 | actual: Value, 459 | ) -> Result { 460 | Err(Error::InvalidOperand { 461 | operator: op.value.clone(), 462 | expected: Vec::from_iter(expected.iter().map(ToString::to_string)), 463 | actual, 464 | location: op.location, 465 | }) 466 | } 467 | 468 | fn execute_block(&mut self, statements: &[Stmt], locals: Locals) -> Result { 469 | statements 470 | .iter() 471 | .map(|stmt| self.execute(stmt, OptRc::clone(&locals))) 472 | .try_fold(None, |_, x| x) 473 | } 474 | } 475 | 476 | impl<'a> ExprVisitor>>, Locals> for &mut Interpreter<'a> { 477 | fn visit_literal(self, x: &crate::ast::Literal, _: Locals) -> Result>> { 478 | Ok(Rc::new(RefCell::new(match x { 479 | Literal::Nil => Value::Nil, 480 | Literal::False => Value::Boolean(false), 481 | Literal::True => Value::Boolean(true), 482 | Literal::Number(n) => Value::Number(*n), 483 | Literal::String(s) => Value::String(s.clone()), 484 | }))) 485 | } 486 | 487 | fn visit_unary(self, x: &crate::ast::Unary, locals: Locals) -> Result>> { 488 | let right = self.evaluate(&x.right, locals)?; 489 | 490 | match x.operator.value { 491 | TokenValue::Minus => match &*right.borrow() { 492 | Value::Number(n) => Ok(Rc::new(RefCell::new(Value::Number(-n)))), 493 | v => self.err_invalid_operand(&x.operator, &["Number"], v.clone()), 494 | }, 495 | TokenValue::Bang => Ok(Rc::new(RefCell::new(Value::Boolean( 496 | !right.borrow().is_truthy(), 497 | )))), 498 | _ => unreachable!("visit_unary on wrong TokenValue"), 499 | } 500 | } 501 | 502 | fn visit_binary(self, x: &crate::ast::Binary, locals: Locals) -> Result>> { 503 | let left_rc = self.evaluate(&x.left, OptRc::clone(&locals))?; 504 | let left = &*left_rc.borrow(); 505 | let right_rc = &*self.evaluate(&x.right, OptRc::clone(&locals))?; 506 | let right = &*right_rc.borrow(); 507 | let op = &x.operator; 508 | 509 | match &op.value { 510 | TokenValue::Minus => match (left, right) { 511 | (Value::Number(l), Value::Number(r)) => { 512 | Ok(Rc::new(RefCell::new(Value::Number(l - r)))) 513 | } 514 | (Value::Number(_), v) => self.err_invalid_operand(op, &["Number"], v.clone()), 515 | (v, _) => self.err_invalid_operand(op, &["Number"], v.clone()), 516 | }, 517 | 518 | TokenValue::Slash => match (left, right) { 519 | (Value::Number(_), Value::Number(r)) if *r == 0.0 => Err(Error::DivisionByZero { 520 | location: op.location, 521 | }), 522 | (Value::Number(l), Value::Number(r)) => { 523 | Ok(Rc::new(RefCell::new(Value::Number(l / r)))) 524 | } 525 | (Value::Number(_), v) => self.err_invalid_operand(op, &["Number"], v.clone()), 526 | (v, _) => self.err_invalid_operand(op, &["Number"], v.clone()), 527 | }, 528 | 529 | TokenValue::Star => match (left, right) { 530 | (Value::Number(l), Value::Number(r)) => { 531 | Ok(Rc::new(RefCell::new(Value::Number(l * r)))) 532 | } 533 | (Value::Number(_), v) => self.err_invalid_operand(op, &["Number"], v.clone()), 534 | (v, _) => self.err_invalid_operand(op, &["Number"], v.clone()), 535 | }, 536 | 537 | TokenValue::Plus => match (left, right) { 538 | (Value::Number(l), Value::Number(r)) => { 539 | Ok(Rc::new(RefCell::new(Value::Number(l + r)))) 540 | } 541 | (Value::String(_), _) | (_, Value::String(_)) => Ok(Rc::new(RefCell::new( 542 | Value::String(format!("{}{}", left, right)), 543 | ))), 544 | (Value::Number(_), _) => { 545 | self.err_invalid_operand(op, &["Number", "String"], right.clone()) 546 | } 547 | _ => self.err_invalid_operand(op, &["Number", "String"], left.clone()), 548 | }, 549 | 550 | TokenValue::Greater => match (left, right) { 551 | (Value::Number(l), Value::Number(r)) => { 552 | Ok(Rc::new(RefCell::new(Value::Boolean(l > r)))) 553 | } 554 | (Value::Number(_), v) => self.err_invalid_operand(op, &["Number"], v.clone()), 555 | (v, _) => self.err_invalid_operand(op, &["Number"], v.clone()), 556 | }, 557 | 558 | TokenValue::Less => match (left, right) { 559 | (Value::Number(l), Value::Number(r)) => { 560 | Ok(Rc::new(RefCell::new(Value::Boolean(l < r)))) 561 | } 562 | (Value::Number(_), v) => self.err_invalid_operand(op, &["Number"], v.clone()), 563 | (v, _) => self.err_invalid_operand(op, &["Number"], v.clone()), 564 | }, 565 | 566 | TokenValue::GreaterEqual => match (left, right) { 567 | (Value::Number(l), Value::Number(r)) => { 568 | Ok(Rc::new(RefCell::new(Value::Boolean(l >= r)))) 569 | } 570 | (Value::Number(_), v) => self.err_invalid_operand(op, &["Number"], v.clone()), 571 | (v, _) => self.err_invalid_operand(op, &["Number"], v.clone()), 572 | }, 573 | 574 | TokenValue::LessEqual => match (left, right) { 575 | (Value::Number(l), Value::Number(r)) => { 576 | Ok(Rc::new(RefCell::new(Value::Boolean(l <= r)))) 577 | } 578 | (Value::Number(_), v) => self.err_invalid_operand(op, &["Number"], v.clone()), 579 | (v, _) => self.err_invalid_operand(op, &["Number"], v.clone()), 580 | }, 581 | 582 | TokenValue::EqualEqual => Ok(Rc::new(RefCell::new(Value::Boolean(left == right)))), 583 | TokenValue::BangEqual => Ok(Rc::new(RefCell::new(Value::Boolean(left != right)))), 584 | 585 | x => unreachable!("Unrecognized binary operator `{}` at {}", x, op.location), 586 | } 587 | } 588 | 589 | fn visit_ternary(self, x: &crate::ast::Ternary, locals: Locals) -> Result>> { 590 | let cond = self.evaluate(&x.left, OptRc::clone(&locals))?; 591 | if cond.borrow().is_truthy() { 592 | self.evaluate(&x.mid, OptRc::clone(&locals)) 593 | } else { 594 | self.evaluate(&x.right, OptRc::clone(&locals)) 595 | } 596 | } 597 | 598 | fn visit_grouping( 599 | self, 600 | x: &crate::ast::Grouping, 601 | locals: Locals, 602 | ) -> Result>> { 603 | self.evaluate(&x.expr, locals) 604 | } 605 | 606 | fn visit_variable( 607 | self, 608 | x: &crate::ast::Variable, 609 | locals: Locals, 610 | ) -> Result>> { 611 | let var = match self.binding(&x.name) { 612 | Some(binding) => locals.unwrap().borrow().get(binding), 613 | None => match self.globals.borrow().get(&x.name) { 614 | Some(v) => v, 615 | None => { 616 | return Err(Error::UndefinedVariable { 617 | name: x.name.lexeme.clone(), 618 | location: x.name.location, 619 | }) 620 | } 621 | }, 622 | }; 623 | 624 | let var = var.borrow(); 625 | match &*var { 626 | Variable::Value(v) => Ok(Rc::clone(v)), 627 | Variable::Uninitialized => Err(Error::UninitializedVariable { 628 | name: x.name.lexeme.clone(), 629 | location: x.name.location, 630 | }), 631 | } 632 | } 633 | 634 | fn visit_assign(self, x: &crate::ast::Assign, locals: Locals) -> Result>> { 635 | let value = self.evaluate(&x.value, OptRc::clone(&locals))?; 636 | match self.binding(&x.name) { 637 | Some(binding) => { 638 | locals 639 | .unwrap() 640 | .borrow_mut() 641 | .assign(binding, Some(Rc::clone(&value))); 642 | } 643 | None => { 644 | if !self 645 | .globals 646 | .borrow_mut() 647 | .assign(&x.name, Some(Rc::clone(&value))) 648 | { 649 | return Err(Error::UndefinedVariable { 650 | name: x.name.lexeme.clone(), 651 | location: x.name.location, 652 | }); 653 | } 654 | } 655 | }; 656 | Ok(value) 657 | } 658 | 659 | fn visit_logical(self, x: &crate::ast::Logical, locals: Locals) -> Result>> { 660 | let left = self.evaluate(&x.left, OptRc::clone(&locals))?; 661 | if x.operator.value == TokenValue::Or { 662 | if left.borrow().is_truthy() { 663 | return Ok(left); 664 | } 665 | } else if !left.borrow().is_truthy() { 666 | return Ok(left); 667 | } 668 | self.evaluate(&x.right, locals) 669 | } 670 | 671 | fn visit_call(self, call: &crate::ast::Call, locals: Locals) -> Result>> { 672 | let callee_rc = self.evaluate(&call.callee, OptRc::clone(&locals))?; 673 | let callee = &*callee_rc.borrow(); 674 | 675 | let callable: Box<&dyn Callable> = match callee { 676 | Value::NativeFunction(f) => Box::new(f), 677 | Value::Function(f) => Box::new(f), 678 | Value::Lambda(f) => Box::new(f), 679 | Value::Class(c) => Box::new(c), 680 | _ => { 681 | return Err(Error::NotCallable { 682 | what: callee.clone(), 683 | location: call.closing_paren.location, 684 | }); 685 | } 686 | }; 687 | 688 | let arguments = call 689 | .arguments 690 | .iter() 691 | .map(|arg| self.evaluate(arg, OptRc::clone(&locals))) 692 | .collect::>>()?; 693 | 694 | if arguments.len() != callable.arity() { 695 | return Err(Error::WrongArity { 696 | expected: callable.arity(), 697 | actual: arguments.len(), 698 | location: call.closing_paren.location, 699 | }); 700 | } 701 | 702 | match callable.call(self, arguments, locals) { 703 | Err(Error::Return { value, .. }) => Ok(value), 704 | x => x, 705 | } 706 | } 707 | 708 | fn visit_lambda(self, x: &crate::ast::Lambda, locals: Locals) -> Result>> { 709 | Ok(Rc::new(RefCell::new(Value::Lambda(Lambda { 710 | declaration: x.clone(), 711 | closure: locals, 712 | })))) 713 | } 714 | 715 | fn visit_get(self, expr: &crate::ast::Get, state: Locals) -> Result>> { 716 | let instance_rc = self.evaluate(&expr.object, OptRc::clone(&state))?; 717 | let instance = instance_rc.borrow(); 718 | 719 | match &*instance { 720 | Value::Instance(Instance { fields, class }) => { 721 | // Field 722 | if let Some(field) = fields.get(&expr.name.lexeme) { 723 | Ok(Rc::clone(field)) 724 | 725 | // Method 726 | } else if let Some(method_def) = class.borrow().find_method(&expr.name.lexeme) { 727 | Ok(Rc::new(RefCell::new(Value::Function(self.bind_this( 728 | &method_def.borrow(), 729 | Rc::clone(&instance_rc), 730 | ))))) 731 | 732 | // Getter 733 | } else if let Some(getter) = class.borrow().find_getter(&expr.name.lexeme) { 734 | match self 735 | .bind_this(&getter.borrow(), Rc::clone(&instance_rc)) 736 | .call(self, vec![], state) 737 | { 738 | Err(Error::Return { value, .. }) => Ok(value), 739 | x => x, 740 | } 741 | } else { 742 | Err(Error::UndefinedProperty { 743 | object: instance.clone(), 744 | property: expr.name.lexeme.clone(), 745 | location: expr.name.location, 746 | }) 747 | } 748 | } 749 | 750 | Value::Class(class) => { 751 | if let Some(method) = class.borrow().find_class_method(&expr.name.lexeme) { 752 | Ok(method) 753 | } else { 754 | Err(Error::UndefinedProperty { 755 | object: instance.clone(), 756 | property: expr.name.lexeme.clone(), 757 | location: expr.name.location, 758 | }) 759 | } 760 | } 761 | 762 | _ => Err(Error::PropertyOnNonObject { 763 | non_object: instance.clone(), 764 | property: expr.name.lexeme.clone(), 765 | location: expr.name.location, 766 | }), 767 | } 768 | } 769 | 770 | fn visit_set(self, expr: &crate::ast::Set, state: Locals) -> Result>> { 771 | let object_rc = self.evaluate(&expr.object, OptRc::clone(&state))?; 772 | let mut object = object_rc.borrow_mut(); 773 | 774 | match &mut *object { 775 | Value::Instance(instance) => { 776 | let value = self.evaluate(&expr.value, state)?; 777 | instance.set(&expr.name, &value); 778 | Ok(value) 779 | } 780 | _ => Err(Error::PropertyOnNonObject { 781 | non_object: object.clone(), 782 | property: expr.name.lexeme.clone(), 783 | location: expr.name.location, 784 | }), 785 | } 786 | } 787 | 788 | fn visit_this(self, expr: &crate::ast::This, state: Locals) -> Result>> { 789 | self.visit_variable( 790 | &crate::ast::Variable { 791 | name: expr.keyword.clone(), 792 | }, 793 | state, 794 | ) 795 | } 796 | 797 | fn visit_super(self, expr: &crate::ast::Super, state: Locals) -> Result>> { 798 | let binding = self.binding(&expr.keyword).unwrap(); 799 | let superclass = match &*OptRc::clone(&state).unwrap().borrow().get(binding).borrow() { 800 | Variable::Value(value_rc_ref) => match &*Rc::clone(value_rc_ref).borrow() { 801 | Value::Class(c) => Rc::clone(c), 802 | _ => unreachable!("visit_super on non-class"), 803 | }, 804 | _ => unreachable!("visit_super on uninitialized class"), 805 | }; 806 | 807 | let object = match &*OptRc::clone(&state) 808 | .unwrap() 809 | .borrow() 810 | .get(&Binding { 811 | scopes_up: binding.scopes_up - 1, 812 | index_in_scope: 0, // `this` is always the first thing bound in this scope 813 | }) 814 | .borrow() 815 | { 816 | Variable::Value(value) => Rc::clone(value), 817 | _ => unreachable!("visit_super on uninitialized instance"), 818 | }; 819 | 820 | let method = superclass.borrow().find_method(&expr.method.lexeme); 821 | match method { 822 | Some(method) => { 823 | return Ok(Rc::new(RefCell::new(Value::Function( 824 | self.bind_this(&method.borrow(), object), 825 | )))) 826 | } 827 | None => { 828 | // TODO dedup this and `visit_get` 829 | let getter = superclass.borrow().find_getter(&expr.method.lexeme); 830 | match getter { 831 | Some(getter) => { 832 | match self.bind_this(&getter.borrow(), Rc::clone(&object)).call( 833 | self, 834 | vec![], 835 | state, 836 | ) { 837 | Err(Error::Return { value, .. }) => Ok(value), 838 | x => x, 839 | } 840 | } 841 | None => Err(Error::UndefinedProperty { 842 | object: Value::Class(Rc::clone(&superclass)), 843 | property: expr.method.lexeme.clone(), 844 | location: expr.method.location, 845 | }), 846 | } 847 | } 848 | } 849 | } 850 | } 851 | 852 | impl<'a> StmtVisitor>>>, Locals> for &mut Interpreter<'a> { 853 | fn visit_block( 854 | self, 855 | x: &crate::ast::Block, 856 | locals: Locals, 857 | ) -> Result>>> { 858 | LocalEnvironment::nested(OptRc::clone(&locals), |block_env| { 859 | self.execute_block(&x.statements, Some(block_env)) 860 | }) 861 | } 862 | 863 | fn visit_expression( 864 | self, 865 | x: &crate::ast::Expression, 866 | locals: Locals, 867 | ) -> Result>>> { 868 | self.evaluate(&x.expr, locals).map(Some) 869 | } 870 | 871 | fn visit_if(self, x: &crate::ast::If, locals: Locals) -> Result>>> { 872 | if self 873 | .evaluate(&x.condition, OptRc::clone(&locals))? 874 | .borrow() 875 | .is_truthy() 876 | { 877 | self.execute(&x.then_branch, locals) 878 | } else if let Some(branch) = &x.else_branch { 879 | self.execute(branch, locals) 880 | } else { 881 | Ok(None) 882 | } 883 | } 884 | 885 | fn visit_print( 886 | self, 887 | x: &crate::ast::Print, 888 | locals: Locals, 889 | ) -> Result>>> { 890 | let output = self.evaluate(&x.expr, locals)?; 891 | writeln!(self.output, "{}", output.borrow()).expect("printing failed"); 892 | Ok(None) 893 | } 894 | 895 | fn visit_var(self, x: &crate::ast::Var, locals: Locals) -> Result>>> { 896 | let value = match &x.initializer { 897 | Some(expr) => Some(self.evaluate(expr, OptRc::clone(&locals))?), 898 | None => None, 899 | }; 900 | 901 | match self.binding(&x.name) { 902 | None => self.globals.borrow_mut().define(&x.name.lexeme, value), 903 | Some(binding) => locals.unwrap().borrow_mut().assign(binding, value), 904 | } 905 | Ok(None) 906 | } 907 | 908 | fn visit_while( 909 | self, 910 | x: &crate::ast::While, 911 | locals: Locals, 912 | ) -> Result>>> { 913 | while self 914 | .evaluate(&x.condition, OptRc::clone(&locals))? 915 | .borrow() 916 | .is_truthy() 917 | { 918 | match self.execute(&x.statement, OptRc::clone(&locals)) { 919 | Err(Error::Break { .. }) => return Ok(None), 920 | x => x?, 921 | }; 922 | } 923 | Ok(None) 924 | } 925 | 926 | fn visit_break( 927 | self, 928 | x: &crate::ast::Break, 929 | _locals: Locals, 930 | ) -> Result>>> { 931 | Err(Error::Break { 932 | location: x.token.location, 933 | }) 934 | } 935 | 936 | fn visit_function( 937 | self, 938 | x: &crate::ast::Function, 939 | locals: Locals, 940 | ) -> Result>>> { 941 | let fun = Value::Function(Function { 942 | declaration: x.clone(), 943 | closure: OptRc::clone(&locals), 944 | force_return: None, 945 | }); 946 | 947 | let value = Some(Rc::new(RefCell::new(fun))); 948 | match self.binding(&x.name) { 949 | None => self 950 | .globals 951 | .borrow_mut() 952 | .define(x.name.lexeme.clone(), value), 953 | Some(binding) => locals.unwrap().borrow_mut().assign(binding, value), 954 | }; 955 | Ok(None) 956 | } 957 | 958 | fn visit_return( 959 | self, 960 | ret: &crate::ast::Return, 961 | locals: Locals, 962 | ) -> Result>>> { 963 | let value = if let Some(value_expr) = &ret.value { 964 | self.evaluate(value_expr, locals)? 965 | } else { 966 | Rc::new(RefCell::new(Value::Nil)) 967 | }; 968 | 969 | Err(Error::Return { 970 | location: ret.keyword.location, 971 | value, 972 | }) 973 | } 974 | 975 | fn visit_class( 976 | self, 977 | stmt: &crate::ast::Class, 978 | state: Locals, 979 | ) -> Result>>> { 980 | let binding = self.binding(&stmt.name).cloned(); 981 | if let Some(binding) = &binding { 982 | state.as_ref().unwrap().borrow_mut().assign(binding, None); 983 | } else { 984 | self.globals.borrow_mut().define(&stmt.name, None); 985 | } 986 | 987 | let (state, superclass) = match &stmt.superclass { 988 | Some(superclass_var) => { 989 | let superclass_value_rc = 990 | self.visit_variable(&superclass_var.1, OptRc::clone(&state))?; 991 | let superclass_value = superclass_value_rc.borrow(); 992 | 993 | let state = match &stmt.superclass { 994 | Some(_) => { 995 | let env = LocalEnvironment::new(OptRc::clone(&state)); 996 | Some(Rc::new(RefCell::new(env))) 997 | } 998 | None => state, 999 | }; 1000 | 1001 | match &*superclass_value { 1002 | Value::Class(superclass) => { 1003 | let binding = self.binding(&superclass_var.0).unwrap(); 1004 | state.as_ref().unwrap().borrow_mut().assign( 1005 | binding, 1006 | Some(Rc::new(RefCell::new(Value::Class(Rc::clone(superclass))))), 1007 | ); 1008 | 1009 | (state, Some(Rc::clone(superclass))) 1010 | } 1011 | _ => { 1012 | return Err(Error::SuperclassNotClass { 1013 | class: stmt.name.lexeme.clone(), 1014 | superclass: superclass_value.clone(), 1015 | location: superclass_var.1.name.location, 1016 | }) 1017 | } 1018 | } 1019 | } 1020 | None => (state, None), 1021 | }; 1022 | 1023 | let mut methods = HashMap::new(); 1024 | for method in &stmt.methods { 1025 | methods.insert( 1026 | method.name.lexeme.clone(), 1027 | Rc::new(RefCell::new(Function { 1028 | declaration: method.clone(), 1029 | closure: OptRc::clone(&state), 1030 | force_return: None, 1031 | })), 1032 | ); 1033 | } 1034 | let methods = methods; 1035 | 1036 | let mut getters = HashMap::new(); 1037 | for getter in &stmt.getters { 1038 | getters.insert( 1039 | getter.name.lexeme.clone(), 1040 | Rc::new(RefCell::new(Function { 1041 | declaration: getter.clone(), 1042 | closure: OptRc::clone(&state), 1043 | force_return: None, 1044 | })), 1045 | ); 1046 | } 1047 | let getters = getters; 1048 | 1049 | let mut class_methods = HashMap::new(); 1050 | for class_method in &stmt.class_methods { 1051 | class_methods.insert( 1052 | class_method.name.lexeme.clone(), 1053 | Rc::new(RefCell::new(Value::Function(Function { 1054 | declaration: class_method.clone(), 1055 | closure: OptRc::clone(&state), 1056 | force_return: None, 1057 | }))), 1058 | ); 1059 | } 1060 | let class_methods = class_methods; 1061 | let has_superclass = superclass.is_some(); 1062 | 1063 | let class = Class { 1064 | name: stmt.name.lexeme.clone(), 1065 | superclass, 1066 | methods, 1067 | class_methods, 1068 | getters, 1069 | left_brace: stmt.left_brace.clone(), 1070 | }; 1071 | let class = Value::Class(Rc::new(RefCell::new(class))); 1072 | let class = Rc::new(RefCell::new(class)); 1073 | 1074 | let state = if has_superclass { 1075 | state.unwrap().borrow().get_parent() 1076 | } else { 1077 | state 1078 | }; 1079 | 1080 | if let Some(binding) = &binding { 1081 | state.unwrap().borrow_mut().assign(binding, Some(class)); 1082 | } else { 1083 | let _ = self.globals.borrow_mut().assign(&stmt.name, Some(class)); 1084 | } 1085 | 1086 | Ok(None) 1087 | } 1088 | } 1089 | --------------------------------------------------------------------------------