├── .gitignore ├── .github └── workflows │ └── rust.yml ├── README.md ├── src ├── vm │ ├── frame.rs │ ├── errors.rs │ ├── io.rs │ ├── gc.rs │ ├── list.rs │ ├── mod.rs │ └── run.rs ├── compiler │ ├── token.rs │ ├── wrapper.rs │ ├── errors.rs │ ├── scanner.rs │ └── mod.rs ├── bin │ └── loxi.rs ├── lox_core │ ├── obj.rs │ ├── chunk.rs │ ├── ops.rs │ └── mod.rs ├── lib.rs └── lox_std │ └── mod.rs ├── tests ├── utils │ └── mod.rs ├── function.rs ├── closure.rs └── classes.rs └── Cargo.toml /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ dev ] 6 | pull_request: 7 | branches: [ dev ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Build 17 | run: cargo build --verbose 18 | - name: Run tests 19 | run: cargo test --verbose 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lox-lang: a Lox runtime for Rust projects 2 | 3 | Lox is a (mostly) toy scripting language designed by Bob Nystrom for his book 4 | [Crafting Interpreters](http://craftinginterpreters.com). This library (and the 5 | included wrapper binary) implement a virtual machine runtime for it that can be 6 | used on its own or embedded into a larger application. See the crate 7 | documentation for usage information. 8 | -------------------------------------------------------------------------------- /src/vm/frame.rs: -------------------------------------------------------------------------------- 1 | use std::any::Any; 2 | 3 | use crate::{Chunk, Fun, Gc, UpvalueRef}; 4 | 5 | #[derive(Clone, Debug)] 6 | pub(crate) struct CallFrame { 7 | pub(crate) func: Gc, 8 | pub(crate) inst: usize, 9 | pub(crate) base: usize, 10 | pub(crate) upvalues: Box<[Gc]>, 11 | } 12 | 13 | impl CallFrame { 14 | pub(crate) fn chunk(&self) -> &Chunk { 15 | &(*self.func).chunk 16 | } 17 | 18 | pub(crate) fn get_upvalue(&self, index: usize) -> Option> { 19 | self.upvalues.get(index).copied() 20 | } 21 | 22 | pub(crate) fn mark(&self, grays: &mut Vec>) { 23 | self.func.mark(); 24 | grays.push(self.func.as_any()); 25 | 26 | for u_val in self.upvalues.iter() { 27 | u_val.mark(); 28 | grays.push(u_val.as_any()); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/compiler/token.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Debug)] 2 | pub(crate) struct Token<'a> { 3 | pub(crate) r#type: TokenType, 4 | pub(crate) text: &'a str, 5 | pub(crate) line: usize, 6 | } 7 | 8 | #[derive(Clone, Copy, Debug, PartialEq)] 9 | pub(crate) enum TokenType { 10 | LeftParen, 11 | RightParen, 12 | LeftBrace, 13 | RightBrace, 14 | Comma, 15 | Dot, 16 | Minus, 17 | Plus, 18 | Semi, 19 | Slash, 20 | Star, 21 | 22 | Bang, 23 | BangEqual, 24 | Equal, 25 | DoubleEqual, 26 | Greater, 27 | GreaterEqual, 28 | Less, 29 | LessEqual, 30 | 31 | Identifier, 32 | r#String, 33 | Number, 34 | 35 | And, 36 | Break, 37 | Case, 38 | Class, 39 | Continue, 40 | r#Default, 41 | Else, 42 | False, 43 | For, 44 | Fun, 45 | If, 46 | Nil, 47 | Or, 48 | Print, 49 | Return, 50 | Super, 51 | Switch, 52 | This, 53 | True, 54 | Var, 55 | While, 56 | } 57 | -------------------------------------------------------------------------------- /tests/utils/mod.rs: -------------------------------------------------------------------------------- 1 | #![macro_use] 2 | 3 | use simplelog::{ConfigBuilder, LevelFilter, SimpleLogger}; 4 | 5 | macro_rules! run { 6 | ($source: literal -> $( $expected: literal ),*) => {{ 7 | use std::io::Read; 8 | 9 | let mut vm = lox_lang::VM::default(); 10 | vm.buffer_output(true); 11 | 12 | vm.interpret($source).map_err(|mut v| v.pop().unwrap())?; 13 | 14 | let mut buffer = String::new(); 15 | vm.read_to_string(&mut buffer).unwrap(); 16 | assert_eq!(buffer, concat!($( $expected, "\n" ),*)); 17 | Ok(()) 18 | }}; 19 | } 20 | 21 | pub type Result = std::result::Result<(), lox_lang::Error>; 22 | 23 | #[allow(dead_code)] 24 | pub fn setup_logger() { 25 | let _ = SimpleLogger::init( 26 | LevelFilter::Debug, 27 | ConfigBuilder::new() 28 | .set_thread_level(LevelFilter::Off) 29 | .set_time_level(LevelFilter::Off) 30 | .set_location_level(LevelFilter::Debug) 31 | .build(), 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lox-lang" 3 | description = "A Lox language runtime for Rust" 4 | readme = "./README.md" 5 | version = "0.0.3" 6 | authors = ["George Kaplan "] 7 | edition = "2018" 8 | license = "MIT" 9 | repository = "https://github.com/g-s-k/lox-lang" 10 | keywords = ["interpreter", "lox", "compiler", "embeddable", "script"] 11 | categories = ["parser-implementations", "command-line-utilities"] 12 | default-run = "loxi" 13 | 14 | [features] 15 | default = [] 16 | 17 | # log messages from scanner 18 | trace-scanning = [] 19 | # log messages during compilation 20 | trace-compilation = [] 21 | # log debug traces of each instruction at runtime 22 | trace-execution = [] 23 | # log garbage collection activity 24 | trace-gc = [] 25 | # run garbage collector on every allocation 26 | stress-test-gc = [] 27 | 28 | [dependencies] 29 | clap = { version = "3.2", features = ["derive"] } 30 | log = "0.4.8" 31 | simplelog = "0.7.6" 32 | jemallocator = "0.3.2" 33 | 34 | [profile.release] 35 | lto = "fat" 36 | codegen-units = 1 37 | debug = true 38 | panic = "abort" 39 | -------------------------------------------------------------------------------- /tests/function.rs: -------------------------------------------------------------------------------- 1 | mod utils; 2 | 3 | #[test] 4 | fn print() -> utils::Result { 5 | run!( 6 | r#" 7 | fun areWeHavingItYet() { 8 | print "Yes we are!"; 9 | } 10 | 11 | print areWeHavingItYet; 12 | "# -> 13 | "#" 14 | ) 15 | } 16 | 17 | #[test] 18 | fn sum() -> utils::Result { 19 | run!( 20 | r#" 21 | fun sum(a, b, c) { 22 | return a + b + c; 23 | } 24 | 25 | print 4 + sum(5, 6, 7); 26 | "# -> 27 | "22" 28 | ) 29 | } 30 | 31 | #[test] 32 | #[should_panic] 33 | fn not_a_function() { 34 | let mut vm = lox_lang::VM::default(); 35 | vm.interpret( 36 | r#" 37 | var notAFunction = 123; 38 | notAFunction(); 39 | "#, 40 | ) 41 | .unwrap(); 42 | } 43 | 44 | #[test] 45 | fn no_return() -> utils::Result { 46 | run!( 47 | r#" 48 | fun noReturn() { 49 | print "Do stuff"; 50 | // No return here. 51 | } 52 | 53 | print noReturn(); // ??? 54 | "# -> 55 | "Do stuff", 56 | "nil" 57 | ) 58 | } 59 | 60 | #[test] 61 | #[should_panic] 62 | fn top_level_return() { 63 | let mut vm = lox_lang::VM::default(); 64 | vm.interpret( 65 | r#" 66 | return "What?!"; 67 | "#, 68 | ) 69 | .unwrap(); 70 | } 71 | -------------------------------------------------------------------------------- /tests/closure.rs: -------------------------------------------------------------------------------- 1 | mod utils; 2 | 3 | #[test] 4 | fn inner_outer() -> utils::Result { 5 | // utils::setup_logger(); 6 | run!( 7 | r#" 8 | var x = "global"; 9 | 10 | fun outer() { 11 | var x = "outer"; 12 | 13 | fun inner() { 14 | print x; 15 | } 16 | 17 | inner(); 18 | } 19 | 20 | outer(); 21 | "# -> 22 | "outer" 23 | ) 24 | } 25 | 26 | #[test] 27 | fn local() -> utils::Result { 28 | // utils::setup_logger(); 29 | run!( 30 | r#" 31 | fun makeClosure() { 32 | var local = "local"; 33 | 34 | fun closure() { 35 | print local; 36 | } 37 | 38 | return closure; 39 | } 40 | 41 | var closure = makeClosure(); 42 | closure(); 43 | "# -> 44 | "local" 45 | ) 46 | } 47 | 48 | #[test] 49 | fn fun_argument() -> utils::Result { 50 | // utils::setup_logger(); 51 | run!( 52 | r#" 53 | fun makeClosure(value) { 54 | fun closure() { 55 | print value; 56 | } 57 | return closure; 58 | } 59 | 60 | var doughnut = makeClosure("doughnut"); 61 | var bagel = makeClosure("bagel"); 62 | doughnut(); 63 | bagel(); 64 | "# -> 65 | "doughnut", 66 | "bagel" 67 | ) 68 | } 69 | 70 | #[test] 71 | fn deferred_declaration() -> utils::Result { 72 | // utils::setup_logger(); 73 | run!(r#" 74 | fun outer() { 75 | var x = "value"; 76 | fun middle() { 77 | fun inner() { 78 | print x; 79 | } 80 | 81 | print "create inner closure"; 82 | return inner; 83 | } 84 | 85 | print "return from outer"; 86 | return middle; 87 | } 88 | 89 | var mid = outer(); 90 | var in = mid(); 91 | in(); 92 | "# -> 93 | "return from outer", 94 | "create inner closure", 95 | "value" 96 | ) 97 | } 98 | -------------------------------------------------------------------------------- /src/vm/errors.rs: -------------------------------------------------------------------------------- 1 | use std::{error, fmt}; 2 | 3 | #[derive(Debug)] 4 | pub(crate) enum RuntimeError { 5 | ArgumentTypes, 6 | StackEmpty, 7 | BadStackIndex(usize, usize), 8 | UndefinedGlobal(String), 9 | NotCallable, 10 | ArityMismatch(u8, u8), 11 | UndefinedProperty(String), 12 | NativeFunError(Box), 13 | } 14 | 15 | impl error::Error for RuntimeError { 16 | fn source(&self) -> Option<&(dyn error::Error + 'static)> { 17 | match self { 18 | Self::NativeFunError(e) => Some(e.as_ref()), 19 | _ => None, 20 | } 21 | } 22 | } 23 | 24 | impl fmt::Display for RuntimeError { 25 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 26 | match self { 27 | Self::ArgumentTypes => write!(f, "incompatible types for operation"), 28 | Self::StackEmpty => write!(f, "tried to pop value from empty stack"), 29 | Self::BadStackIndex(wanted, len) => write!( 30 | f, 31 | "tried to access value at index {} beyond end of stack (height {})", 32 | wanted, len 33 | ), 34 | Self::UndefinedGlobal(name) => { 35 | write!(f, "tried to access undefined variable `{}`", name) 36 | } 37 | Self::NotCallable => write!(f, "tried to call a non-callable value"), 38 | Self::ArityMismatch(expected, got) => { 39 | write!(f, "expected {} arguments but got {}", expected, got) 40 | } 41 | Self::UndefinedProperty(name) => write!( 42 | f, 43 | "tried to access undefined property `{}` on instance", 44 | name 45 | ), 46 | Self::NativeFunError(inner) => { 47 | write!(f, "native function returned an error: {}", inner) 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/bin/loxi.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fs, 3 | io::{self, BufRead, Write}, 4 | path::{Path, PathBuf}, 5 | process, 6 | }; 7 | 8 | #[global_allocator] 9 | static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc; 10 | 11 | use { 12 | clap::Parser, 13 | lox_lang::{Error, ErrorCategory, VM}, 14 | simplelog::{ConfigBuilder, LevelFilter, SimpleLogger}, 15 | }; 16 | 17 | #[derive(Parser)] 18 | struct Params { 19 | script: Option, 20 | } 21 | 22 | fn main() { 23 | let args = Params::parse(); 24 | SimpleLogger::init( 25 | LevelFilter::Debug, 26 | ConfigBuilder::new() 27 | .set_thread_level(LevelFilter::Off) 28 | .set_time_level(LevelFilter::Off) 29 | .set_location_level(LevelFilter::Debug) 30 | .build(), 31 | ) 32 | .unwrap(); 33 | 34 | if let Some(path) = args.script { 35 | if let Err(e) = run_file(&path) { 36 | process::exit(match e.category() { 37 | ErrorCategory::Compilation => 65, 38 | ErrorCategory::Runtime => 70, 39 | _ => 1, 40 | }) 41 | }; 42 | } else { 43 | repl() 44 | } 45 | } 46 | 47 | fn repl() { 48 | let mut vm = vm_with_globals(); 49 | let mut buffer = String::new(); 50 | loop { 51 | buffer.clear(); 52 | print!("> "); 53 | io::stdout().flush().unwrap(); 54 | match io::stdin().lock().read_line(&mut buffer) { 55 | Err(e) => { 56 | eprintln!("Error reading input: {}", e); 57 | break; 58 | } 59 | Ok(0) => { 60 | // EOF 61 | println!("bye"); 62 | break; 63 | } 64 | _ => (), 65 | } 66 | 67 | let _ = vm.interpret(buffer.clone()); 68 | } 69 | } 70 | 71 | fn run_file(path: &Path) -> Result<(), Error> { 72 | let source = match fs::read_to_string(path) { 73 | Ok(code) => code, 74 | Err(e) => { 75 | eprintln!("Could not read file {}: {}", path.to_string_lossy(), e); 76 | process::exit(74); 77 | } 78 | }; 79 | 80 | vm_with_globals() 81 | .interpret(source) 82 | .map_err(|mut errs| errs.pop().unwrap()) 83 | } 84 | 85 | fn vm_with_globals() -> VM { 86 | let mut vm = VM::default(); 87 | vm.add_std_globals(); 88 | vm 89 | } 90 | -------------------------------------------------------------------------------- /src/lox_core/obj.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | any::{self, Any}, 3 | cell::Cell, 4 | fmt, 5 | ops::{Deref, DerefMut}, 6 | }; 7 | 8 | #[derive(Debug)] 9 | struct ObjBox { 10 | mark: Cell, 11 | value: T, 12 | } 13 | 14 | #[derive(Debug)] 15 | pub struct Gc(*mut ObjBox); 16 | 17 | impl Gc { 18 | pub(crate) fn new(value: T) -> Self { 19 | Self(Box::into_raw(Box::new(ObjBox { 20 | mark: Cell::new(false), 21 | value, 22 | }))) 23 | } 24 | } 25 | 26 | impl Gc { 27 | fn report_null(&self) -> ! { 28 | panic!( 29 | "Holding null reference to type {} at address {:p}.", 30 | any::type_name::(), 31 | self.0 32 | ); 33 | } 34 | 35 | fn deref_non_null(&self) -> &ObjBox { 36 | if self.0.is_null() { 37 | self.report_null(); 38 | } else { 39 | unsafe { &*self.0 } 40 | } 41 | } 42 | 43 | pub(crate) fn is_marked(&self) -> bool { 44 | self.deref_non_null().mark.get() 45 | } 46 | 47 | pub(crate) fn clear_mark(&self) { 48 | self.deref_non_null().mark.set(false); 49 | } 50 | 51 | pub(crate) fn mark(&self) { 52 | #[cfg(feature = "trace-gc")] 53 | log::debug!("{:p} mark", self.0); 54 | 55 | self.deref_non_null().mark.set(true); 56 | } 57 | 58 | pub(crate) fn free(self) { 59 | #[cfg(feature = "trace-gc")] 60 | log::debug!("{:p} free", self.0); 61 | 62 | unsafe { 63 | // drop inner wrapper, and thus the value it owns 64 | Box::from_raw(self.0); 65 | } 66 | } 67 | } 68 | 69 | impl Gc { 70 | pub(crate) fn as_any(self) -> Gc { 71 | Gc(self.0 as *mut ObjBox) 72 | } 73 | } 74 | 75 | impl Clone for Gc { 76 | fn clone(&self) -> Self { 77 | Self(self.0) 78 | } 79 | } 80 | 81 | impl Copy for Gc {} 82 | 83 | impl Deref for Gc { 84 | type Target = T; 85 | 86 | fn deref(&self) -> &Self::Target { 87 | &self.deref_non_null().value 88 | } 89 | } 90 | 91 | impl DerefMut for Gc { 92 | fn deref_mut(&mut self) -> &mut Self::Target { 93 | if self.0.is_null() { 94 | self.report_null(); 95 | } else { 96 | &mut unsafe { &mut (*self.0) }.value 97 | } 98 | } 99 | } 100 | 101 | impl fmt::Pointer for Gc { 102 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 103 | self.0.fmt(f) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/vm/io.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | cmp, fmt, 3 | io::{Error, ErrorKind, Read, Result, Write}, 4 | mem, 5 | }; 6 | 7 | use super::VM; 8 | 9 | impl VM { 10 | /// Print directly to stdout, or buffer output internally. 11 | /// 12 | /// Pass `true` to this method to buffer print output internally. This output can then be 13 | /// accessed through this struct's [`Read`] implementation. 14 | /// 15 | /// Pass `false` (the default mode) to sink all program output directly to [`Stdout`]. 16 | /// 17 | /// ### Example 18 | /// 19 | /// ``` 20 | /// use std::io::Read; 21 | /// 22 | /// let mut vm = lox_lang::VM::default(); 23 | /// vm.buffer_output(true); 24 | /// 25 | /// vm.interpret("print nil == true;"); 26 | /// 27 | /// let mut buffer = String::new(); 28 | /// vm.read_to_string(&mut buffer).unwrap(); 29 | /// assert_eq!(buffer, "false\n"); 30 | /// 31 | /// vm.interpret("for (var a = 3; a < 12; a = a + 3) print a;"); 32 | /// 33 | /// buffer.clear(); 34 | /// vm.read_to_string(&mut buffer).unwrap(); 35 | /// assert_eq!(buffer, "3\n6\n9\n"); 36 | /// ``` 37 | /// 38 | /// [`Stdout`]: https://doc.rust-lang.org/std/io/struct.Stdout.html 39 | /// [`Read`]: https://doc.rust-lang.org/std/io/trait.Read.html 40 | pub fn buffer_output(&mut self, should_buffer: bool) { 41 | if should_buffer && self.stdout.is_none() { 42 | self.stdout = Some(Vec::new()); 43 | } else if !should_buffer && self.stdout.is_some() { 44 | self.stdout = None; 45 | } 46 | } 47 | 48 | pub(super) fn print(&mut self, args: fmt::Arguments) { 49 | if let Some(ref mut out) = self.stdout { 50 | out.write_fmt(args).unwrap(); 51 | } else { 52 | print!("{}", args); 53 | } 54 | } 55 | 56 | fn try_read_buf(&mut self) -> Result<&mut Vec> { 57 | if let Some(ref mut my_buf) = &mut self.stdout { 58 | Ok(my_buf) 59 | } else { 60 | Err(Error::new( 61 | ErrorKind::Other, 62 | "Currently logging directly to stdout.", 63 | )) 64 | } 65 | } 66 | } 67 | 68 | impl Read for VM { 69 | fn read(&mut self, buf: &mut [u8]) -> Result { 70 | let my_buf = self.try_read_buf()?; 71 | 72 | if my_buf.is_empty() { 73 | return Ok(0); 74 | } 75 | 76 | let common_len = cmp::min(my_buf.len(), buf.len()); 77 | 78 | let new_vec = my_buf.split_off(common_len); 79 | let old_vec = mem::replace(my_buf, new_vec); 80 | 81 | buf[0..common_len].copy_from_slice(&old_vec); 82 | 83 | Ok(common_len) 84 | } 85 | 86 | fn read_to_end(&mut self, buf: &mut Vec) -> Result { 87 | let my_buf = self.try_read_buf()?; 88 | 89 | if my_buf.is_empty() { 90 | return Ok(0); 91 | } 92 | 93 | let len = my_buf.len(); 94 | buf.append(my_buf); 95 | 96 | Ok(len) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/compiler/wrapper.rs: -------------------------------------------------------------------------------- 1 | use crate::Fun; 2 | 3 | #[derive(Clone, Copy)] 4 | pub(super) enum FunType { 5 | Script, 6 | Function, 7 | Method, 8 | Initializer, 9 | } 10 | 11 | #[derive(Debug)] 12 | pub(super) struct Local<'compile> { 13 | pub(super) name: &'compile str, 14 | pub(super) depth: usize, 15 | pub(super) is_defined: bool, 16 | pub(super) is_captured: bool, 17 | } 18 | 19 | #[derive(Clone, Copy, Debug)] 20 | pub(crate) struct Upvalue { 21 | pub index: usize, 22 | pub is_local: bool, 23 | } 24 | 25 | pub(super) struct FunWrapper<'compile> { 26 | pub(super) enclosing: Option>, 27 | pub(super) inner: Fun, 28 | pub(super) r#type: FunType, 29 | pub(super) locals: Vec>, 30 | pub(super) upvalues: Vec, 31 | pub(super) scope_depth: usize, 32 | } 33 | 34 | impl<'compile> FunWrapper<'compile> { 35 | pub(super) fn new(name: &T, r#type: FunType) -> Self { 36 | Self { 37 | enclosing: None, 38 | inner: Fun::new(name, 0), 39 | r#type, 40 | locals: vec![Local { 41 | name: if let FunType::Method | FunType::Initializer = r#type { 42 | "this" 43 | } else { 44 | "" 45 | }, 46 | depth: 0, 47 | is_defined: true, 48 | is_captured: false, 49 | }], 50 | upvalues: Vec::new(), 51 | scope_depth: 0, 52 | } 53 | } 54 | 55 | pub(super) fn resolve_local(&self, query: &str) -> Option<(usize, bool)> { 56 | for ( 57 | index, 58 | Local { 59 | name, is_defined, .. 60 | }, 61 | ) in self.locals.iter().enumerate().rev() 62 | { 63 | if *name == query { 64 | return Some((index, *is_defined)); 65 | } 66 | } 67 | 68 | None 69 | } 70 | 71 | pub(super) fn resolve_upvalue(&mut self, query: &str) -> Option { 72 | if let Some(parent) = &mut self.enclosing { 73 | if let Some((index, _)) = parent.resolve_local(query) { 74 | parent.locals[index].is_captured = true; 75 | return Some(self.add_upvalue(index, true)); 76 | } else if let Some(index) = parent.resolve_upvalue(query) { 77 | return Some(self.add_upvalue(index, false)); 78 | } 79 | } 80 | 81 | None 82 | } 83 | 84 | pub(super) fn add_upvalue(&mut self, local_index: usize, is_local: bool) -> usize { 85 | for (idx, u_val) in self.upvalues.iter().enumerate() { 86 | if u_val.index == local_index && u_val.is_local == is_local { 87 | return idx; 88 | } 89 | } 90 | 91 | self.upvalues.push(Upvalue { 92 | index: local_index, 93 | is_local, 94 | }); 95 | self.upvalues.len() - 1 96 | } 97 | } 98 | 99 | #[derive(Debug)] 100 | pub(super) struct ClassWrapper { 101 | pub(super) enclosing: Option>, 102 | pub(super) has_superclass: bool, 103 | } 104 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(clippy::all)] 2 | #![warn(clippy::pedantic)] 3 | 4 | //! An implementation of the Lox programming language. 5 | //! 6 | //! ## About Lox 7 | //! 8 | //! Lox is a dynamically typed, interpreted scripting language. It was designed by Bob Nystrom for 9 | //! his book [Crafting Interpreters](http://craftinginterpreters.com). 10 | //! 11 | //! ## About this implementation 12 | //! 13 | //! This library aims to implement Lox faithfully to the quasi-specification laid out in the book, 14 | //! as well as some language extensions suggested as exercises to the reader. It is loosely based 15 | //! off of the book's third part, which is a guide to implementing an interpreter in C. 16 | //! 17 | //! ## Usage 18 | //! 19 | //! Included in this package (and installable via `cargo install lox_lang`) is a small wrapper 20 | //! executable named `loxi`. It is the simplest route to trying out the Lox language. 21 | //! 22 | //! If you want to embed Lox in a larger Rust project, you will need to create an instance of 23 | //! [`VM`](struct.VM.html) and use its [`interpret`](struct.VM.html#method.interpret) method to run 24 | //! your code: 25 | //! 26 | //! ``` 27 | //! let mut my_vm = lox_lang::VM::default(); 28 | //! my_vm.interpret(r#" print "hello " + "world"; "#).unwrap(); 29 | //! ``` 30 | 31 | use std::{convert::From, error, fmt}; 32 | 33 | mod compiler; 34 | mod lox_core; 35 | pub mod lox_std; 36 | mod vm; 37 | 38 | pub use lox_core::{NativeFun, Value}; 39 | pub use vm::VM; 40 | 41 | use { 42 | compiler::{CompileError, CompileErrorType, Compiler, Upvalue}, 43 | lox_core::{Chunk, Class, Fun, Gc, Instance, Op, UpvalueRef}, 44 | vm::RuntimeError, 45 | }; 46 | 47 | /// Compilation and runtime errors in the Lox VM. 48 | #[derive(Debug)] 49 | pub struct Error { 50 | inner: Box, 51 | category: ErrorCategory, 52 | line: usize, 53 | } 54 | 55 | impl Error { 56 | /// Which type of error was encountered 57 | #[must_use] 58 | pub fn category(&self) -> ErrorCategory { 59 | self.category 60 | } 61 | 62 | /// The line in the Lox source code where the error occurred 63 | #[must_use] 64 | pub fn line(&self) -> usize { 65 | self.line 66 | } 67 | 68 | fn from_runtime_error(err: RuntimeError, line: Option) -> Self { 69 | Self { 70 | inner: Box::new(err), 71 | category: ErrorCategory::Runtime, 72 | line: line.unwrap_or(1), 73 | } 74 | } 75 | } 76 | 77 | impl fmt::Display for Error { 78 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 79 | write!(f, "{}", self.inner) 80 | } 81 | } 82 | 83 | impl error::Error for Error { 84 | fn source(&self) -> Option<&(dyn error::Error + 'static)> { 85 | Some(&*self.inner) 86 | } 87 | } 88 | 89 | /// Where in the pipeline an error occurred. 90 | #[derive(Clone, Copy, Debug)] 91 | #[non_exhaustive] 92 | pub enum ErrorCategory { 93 | Compilation, 94 | Runtime, 95 | } 96 | 97 | impl From for Error { 98 | fn from(inner: CompileError) -> Self { 99 | Self { 100 | line: inner.line, 101 | inner: Box::new(inner), 102 | category: ErrorCategory::Compilation, 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/vm/gc.rs: -------------------------------------------------------------------------------- 1 | use std::{any::Any, mem}; 2 | 3 | use crate::Gc; 4 | 5 | impl super::VM { 6 | fn mark_roots(&mut self) -> Vec> { 7 | let mut gray_stack = Vec::new(); 8 | 9 | // locals and temporaries 10 | for el in &self.stack { 11 | el.mark(&mut gray_stack); 12 | } 13 | 14 | // globals 15 | for val in self.globals.values() { 16 | val.mark(&mut gray_stack); 17 | } 18 | 19 | // call frames 20 | for frame in &self.frames { 21 | frame.mark(&mut gray_stack); 22 | } 23 | 24 | // open upvalues 25 | for c in self.open_upvalues.iter() { 26 | c.mark(); 27 | gray_stack.push(c.as_any()); 28 | } 29 | 30 | // compiler roots 31 | for root in &self.compiler_roots { 32 | root.mark(&mut gray_stack); 33 | } 34 | 35 | gray_stack 36 | } 37 | 38 | fn trace_references(gray_stack: &mut Vec>) { 39 | while let Some(top_obj) = gray_stack.pop() { 40 | top_obj.blacken(gray_stack); 41 | } 42 | } 43 | 44 | fn sweep(&mut self) { 45 | let to_drop = self.objects.retain(|obj| { 46 | if obj.is_marked() { 47 | obj.clear_mark(); 48 | true 49 | } else { 50 | false 51 | } 52 | }); 53 | 54 | for ptr in to_drop { 55 | self.total_allocations -= mem::size_of_val(&*ptr); 56 | ptr.free(); 57 | } 58 | } 59 | 60 | fn collect_garbage(&mut self) { 61 | #[cfg(feature = "trace-gc")] 62 | let before = self.total_allocations; 63 | 64 | #[cfg(feature = "trace-gc")] 65 | log::debug!("gc begin :: total allocations {} bytes", before); 66 | 67 | let mut gray_stack = self.mark_roots(); 68 | Self::trace_references(&mut gray_stack); 69 | self.sweep(); 70 | 71 | // adjust threshold 72 | self.next_gc = self.total_allocations * 2; 73 | 74 | #[cfg(feature = "trace-gc")] 75 | log::debug!( 76 | "gc end :: collected {} bytes (total was {}, now {}) :: next at {}", 77 | before - self.total_allocations, 78 | before, 79 | self.total_allocations, 80 | self.next_gc 81 | ); 82 | } 83 | 84 | fn should_collect(&self) -> bool { 85 | cfg!(feature = "stress-test-gc") || self.total_allocations > self.next_gc 86 | } 87 | 88 | /// Allocate a garbage-collected value on the heap. 89 | /// 90 | /// This method is how to obtain a `Gc` pointer (not exported from this crate and has no public 91 | /// constructor). Values allocated with this method will be owned (and eventually freed) by the 92 | /// VM. If the value lives until the VM goes out of scope, it will be freed in the VM's `Drop` 93 | /// implementation. 94 | /// 95 | /// For a usage example, see [`NativeFun`](./type.NativeFun.html). 96 | pub fn alloc(&mut self, obj: T) -> Gc { 97 | if self.should_collect() { 98 | self.collect_garbage(); 99 | } 100 | 101 | let size = mem::size_of::(); 102 | self.total_allocations += size; 103 | 104 | let ptr = Gc::new(obj); 105 | self.objects.push(ptr.as_any()); 106 | 107 | #[cfg(feature = "trace-gc")] 108 | log::debug!( 109 | "{:p} allocate {} bytes for {}", 110 | ptr, 111 | size, 112 | std::any::type_name::() 113 | ); 114 | 115 | ptr 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/vm/list.rs: -------------------------------------------------------------------------------- 1 | //! general purpose linked list 2 | //! 3 | //! mostly cribbed from the immortal [Rust in Entirely Too Many Linked 4 | //! Lists](https://rust-unofficial.github.io/too-many-lists) but improved with reference passing 5 | //! and utility methods to get, insert, and delete conditionally 6 | 7 | #[derive(Debug)] 8 | pub(crate) struct List { 9 | head: Option>>, 10 | } 11 | 12 | #[derive(Debug)] 13 | struct Node { 14 | item: T, 15 | next: Option>>, 16 | } 17 | 18 | impl List { 19 | pub(crate) fn new() -> Self { 20 | Self { head: None } 21 | } 22 | 23 | #[allow(dead_code)] 24 | pub(crate) fn peek(&self) -> Option<&T> { 25 | self.head.as_ref().map(|node| &node.item) 26 | } 27 | 28 | #[allow(dead_code)] 29 | pub(crate) fn peek_mut(&mut self) -> Option<&mut T> { 30 | self.head.as_mut().map(|node| &mut node.item) 31 | } 32 | 33 | pub(crate) fn push(&mut self, item: T) -> &mut T { 34 | Node::splice_in(&mut self.head, item) 35 | } 36 | 37 | #[allow(dead_code)] 38 | pub(crate) fn pop(&mut self) -> Option { 39 | self.head.take().map(|node| node.item) 40 | } 41 | 42 | pub(crate) fn insert_before(&mut self, value: T, predicate: F) -> &mut T 43 | where 44 | F: Fn(&T) -> bool, 45 | { 46 | let mut current = &mut self.head; 47 | 48 | while current 49 | .as_ref() 50 | .filter(|node| predicate(&node.item)) 51 | .is_some() 52 | { 53 | current = &mut current.as_mut().unwrap().next; 54 | } 55 | 56 | Node::splice_in(current, value) 57 | } 58 | 59 | pub(crate) fn find(&self, predicate: F) -> Option<&T> 60 | where 61 | F: Fn(&T) -> bool, 62 | { 63 | let mut current = &self.head; 64 | 65 | while let Some(ref head) = ¤t { 66 | if predicate(&head.item) { 67 | break; 68 | } 69 | 70 | current = &head.next; 71 | } 72 | 73 | current.as_ref().map(|node| &node.item) 74 | } 75 | 76 | pub(crate) fn retain(&mut self, predicate: F) -> Vec 77 | where 78 | F: Fn(&T) -> bool, 79 | { 80 | let mut out = Vec::new(); 81 | 82 | let mut current = &mut self.head; 83 | 84 | while current.is_some() { 85 | if current 86 | .as_ref() 87 | .filter(|node| predicate(&node.item)) 88 | .is_some() 89 | { 90 | current = &mut current.as_mut().unwrap().next; 91 | } else if let Some(to_remove) = current.take() { 92 | *current = to_remove.next; 93 | out.push(to_remove.item); 94 | } 95 | } 96 | 97 | out 98 | } 99 | 100 | pub(crate) fn iter(&self) -> Iter<'_, T> { 101 | Iter { 102 | next: self.head.as_deref(), 103 | } 104 | } 105 | } 106 | 107 | pub(crate) struct Iter<'a, T> { 108 | next: Option<&'a Node>, 109 | } 110 | 111 | impl<'a, T> Iterator for Iter<'a, T> { 112 | type Item = &'a T; 113 | fn next(&mut self) -> Option { 114 | self.next.map(|node| { 115 | self.next = node.next.as_deref(); 116 | &node.item 117 | }) 118 | } 119 | } 120 | 121 | impl Node { 122 | fn splice_in(maybe_node: &mut Option>, item: T) -> &mut T { 123 | let new_node = Box::new(Node { 124 | item, 125 | next: maybe_node.take().and_then(|mut node| node.next.take()), 126 | }); 127 | 128 | &mut maybe_node.get_or_insert(new_node).item 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/compiler/errors.rs: -------------------------------------------------------------------------------- 1 | use std::{error, fmt}; 2 | 3 | #[derive(Debug)] 4 | pub(crate) struct CompileError { 5 | pub err: CompileErrorType, 6 | pub line: usize, 7 | pub text: Option, 8 | } 9 | 10 | impl error::Error for CompileError {} 11 | 12 | impl fmt::Display for CompileError { 13 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 14 | write!(f, "[line {}] Error", self.line)?; 15 | if let Some(t) = &self.text { 16 | write!(f, " at \"{}\"", t)?; 17 | } 18 | write!(f, ": {}", self.err) 19 | } 20 | } 21 | 22 | #[derive(Clone, Debug)] 23 | pub(crate) enum CompileErrorType { 24 | // compilation 25 | UnexpectedChar(char), 26 | UnterminatedString, 27 | MissingLeftParen(&'static str), 28 | MissingRightParen(&'static str), 29 | MissingLeftBrace(&'static str), 30 | MissingRightBrace(&'static str), 31 | MissingDot(&'static str), 32 | MissingPrefixExpr, 33 | MissingSemi, 34 | MissingVarName, 35 | MissingClassName, 36 | MissingPropertyName, 37 | MissingMethodName, 38 | InvalidThis, 39 | InvalidSuper(&'static str), 40 | ReturnFromInit, 41 | MissingSuperclass, 42 | InheritFromSelf, 43 | MissingFunName, 44 | MissingParamName, 45 | TooManyParams, 46 | DuplicateLocal(String), 47 | ReadBeforeDefined(String), 48 | JumpTooLarge(usize), 49 | TopLevelReturn, 50 | ExpectedEOF, 51 | } 52 | 53 | impl fmt::Display for CompileErrorType { 54 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 55 | match self { 56 | Self::UnexpectedChar(c) => write!(f, "unexpected character '{}'", c), 57 | Self::UnterminatedString => write!(f, "unterminated string"), 58 | Self::MissingLeftParen(location) => write!(f, "expected '(' {}", location), 59 | Self::MissingRightParen(location) => write!(f, "expected ')' {}", location), 60 | Self::MissingLeftBrace(location) => write!(f, "expected '{{' {}", location), 61 | Self::MissingRightBrace(location) => write!(f, "expected '}}' {}", location), 62 | Self::MissingDot(location) => write!(f, "expected '.' {}", location), 63 | Self::MissingPrefixExpr => write!(f, "expected expression"), 64 | Self::MissingSemi => write!(f, "expected ';' after statement"), 65 | Self::MissingVarName => write!(f, "expect variable name"), 66 | Self::MissingClassName => write!(f, "expect class name"), 67 | Self::MissingPropertyName => write!(f, "expect property name after '.'"), 68 | Self::MissingMethodName => write!(f, "expect method name"), 69 | Self::InvalidThis => write!(f, "cannot use 'this' outside of a class"), 70 | Self::InvalidSuper(location) => write!(f, "cannot use 'super' {}", location), 71 | Self::ReturnFromInit => write!(f, "cannot return a value from 'init' method"), 72 | Self::MissingSuperclass => write!(f, "expect superclass name after '<'"), 73 | Self::InheritFromSelf => write!(f, "a class cannot inherit from itself"), 74 | Self::MissingFunName => write!(f, "expect function name"), 75 | Self::MissingParamName => write!(f, "expect parameter name"), 76 | Self::TooManyParams => write!(f, "cannot exceed 255 parameters"), 77 | Self::DuplicateLocal(who) => { 78 | write!(f, "variable `{}` already declared in this scope", who) 79 | } 80 | Self::ReadBeforeDefined(who) => { 81 | write!(f, "tried to use variable `{}` in its own initializer", who) 82 | } 83 | Self::JumpTooLarge(distance) => write!( 84 | f, 85 | "jump distance {:x} is too large to fit in a `u16` (max value {:x})", 86 | distance, 87 | std::u16::MAX 88 | ), 89 | Self::TopLevelReturn => write!(f, "return statement outside a function body"), 90 | Self::ExpectedEOF => write!(f, "expected end of input"), 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /tests/classes.rs: -------------------------------------------------------------------------------- 1 | mod utils; 2 | 3 | #[test] 4 | fn object() -> utils::Result { 5 | run!( 6 | r#" 7 | class Brioche {} 8 | print Brioche; 9 | "# -> 10 | "Brioche" 11 | ) 12 | } 13 | 14 | #[test] 15 | fn instance() -> utils::Result { 16 | run!( 17 | r#" 18 | class Brioche {} 19 | print Brioche(); 20 | "# -> 21 | "Brioche instance" 22 | ) 23 | } 24 | 25 | #[test] 26 | #[should_panic] 27 | fn bad_getter() { 28 | let mut vm = lox_lang::VM::default(); 29 | vm.interpret( 30 | r#" 31 | var obj = "not an instance"; 32 | print obj.field; 33 | "#, 34 | ) 35 | .unwrap(); 36 | } 37 | 38 | #[test] 39 | fn setter() -> utils::Result { 40 | run!( 41 | r#" 42 | class Toast {} 43 | var toast = Toast(); 44 | print toast.jam = "grape"; 45 | "# -> 46 | "grape" 47 | ) 48 | } 49 | 50 | #[test] 51 | fn properties() -> utils::Result { 52 | run!( 53 | r#" 54 | class Pair {} 55 | 56 | var pair = Pair(); 57 | pair.first = 1; 58 | pair.second = 2; 59 | print pair.first + pair.second; 60 | "# -> 61 | "3" 62 | ) 63 | } 64 | 65 | #[test] 66 | fn define_methods() -> utils::Result { 67 | run!( 68 | r#" 69 | class Brunch { 70 | eggs() {} 71 | bacon() {} 72 | } 73 | "# -> 74 | ) 75 | } 76 | 77 | #[test] 78 | fn no_this() -> utils::Result { 79 | run!( 80 | r#" 81 | class Scone { 82 | topping(first, second) { 83 | print "scone with " + first + " and " + second; 84 | } 85 | } 86 | 87 | var scone = Scone(); 88 | scone.topping("berries", "cream"); 89 | "# -> 90 | "scone with berries and cream" 91 | ) 92 | } 93 | 94 | #[test] 95 | fn this() -> utils::Result { 96 | run!( 97 | r#" 98 | class Nested { 99 | method() { 100 | fun function() { 101 | print this; 102 | } 103 | 104 | function(); 105 | } 106 | } 107 | 108 | Nested().method(); 109 | "# -> 110 | "Nested instance" 111 | ) 112 | } 113 | 114 | #[test] 115 | fn say_name() -> utils::Result { 116 | run!( 117 | r#" 118 | class Person { 119 | sayName() { 120 | print this.name; 121 | } 122 | } 123 | 124 | var jane = Person(); 125 | jane.name = "Jane"; 126 | 127 | var method = jane.sayName; 128 | method(); 129 | "# -> 130 | "Jane" 131 | ) 132 | } 133 | 134 | #[test] 135 | fn init() -> utils::Result { 136 | run!( 137 | r#" 138 | class CoffeeMaker { 139 | init(coffee) { 140 | this.coffee = coffee; 141 | } 142 | 143 | brew() { 144 | print "Enjoy your cup of " + this.coffee; 145 | 146 | // No reusing the grounds! 147 | this.coffee = nil; 148 | } 149 | } 150 | 151 | var maker = CoffeeMaker("coffee and chicory"); 152 | maker.brew(); 153 | "# -> 154 | "Enjoy your cup of coffee and chicory" 155 | ) 156 | } 157 | 158 | #[test] 159 | fn invoke_field() -> utils::Result { 160 | run!( 161 | r#" 162 | class Oops { 163 | init() { 164 | fun f() { 165 | print "not a method"; 166 | } 167 | 168 | this.field = f; 169 | } 170 | } 171 | 172 | var oops = Oops(); 173 | oops.field(); 174 | "# -> 175 | "not a method" 176 | ) 177 | } 178 | 179 | #[test] 180 | fn inherit() -> utils::Result { 181 | run!( 182 | r#" 183 | class Doughnut { 184 | cook() { 185 | print "Dunk in the fryer."; 186 | } 187 | } 188 | 189 | class Cruller < Doughnut { 190 | finish() { 191 | print "Glaze with icing"; 192 | } 193 | } 194 | "# -> 195 | ) 196 | } 197 | 198 | #[test] 199 | fn super_call() -> utils::Result { 200 | run!( 201 | r#" 202 | class A { 203 | method() { 204 | print "A method"; 205 | } 206 | } 207 | 208 | class B < A { 209 | method() { 210 | print "B method"; 211 | } 212 | 213 | test() { 214 | super.method(); 215 | } 216 | } 217 | 218 | class C < B {} 219 | 220 | C().test(); 221 | "# -> 222 | "A method" 223 | ) 224 | } 225 | 226 | #[test] 227 | fn super_access() -> utils::Result { 228 | run!( 229 | r#" 230 | class A { 231 | method() { 232 | print "A"; 233 | } 234 | } 235 | 236 | class B < A { 237 | method() { 238 | var closure = super.method; 239 | closure(); // Prints "A". 240 | } 241 | } 242 | 243 | B().method(); 244 | "# -> 245 | "A" 246 | ) 247 | } 248 | 249 | #[test] 250 | fn super_doughnut() -> utils::Result { 251 | run!( 252 | r#" 253 | class Doughnut { 254 | cook() { 255 | print "Dunk in the fryer."; 256 | this.finish(); 257 | } 258 | 259 | finish(ingredient) { 260 | print "Finish with " + ingredient; 261 | } 262 | } 263 | 264 | class Cruller < Doughnut { 265 | finish() { 266 | super.finish("icing"); 267 | } 268 | } 269 | 270 | Cruller().cook(); 271 | "# -> 272 | "Dunk in the fryer.", 273 | "Finish with icing" 274 | ) 275 | } 276 | -------------------------------------------------------------------------------- /src/vm/mod.rs: -------------------------------------------------------------------------------- 1 | use std::{any::Any, collections::HashMap, fmt::Write}; 2 | 3 | mod errors; 4 | mod frame; 5 | mod gc; 6 | mod io; 7 | mod list; 8 | mod run; 9 | 10 | pub(crate) use errors::RuntimeError; 11 | 12 | use { 13 | crate::{Compiler, Error, Gc, UpvalueRef, Value}, 14 | frame::CallFrame, 15 | list::List, 16 | }; 17 | 18 | /// The Lox virtual machine. 19 | /// 20 | /// ### Example 21 | /// 22 | /// ``` 23 | /// # use lox_lang::VM; 24 | /// let mut vm = VM::default(); 25 | /// vm.interpret("3 + 3 == 6"); // true 26 | /// ``` 27 | pub struct VM { 28 | stdout: Option>, 29 | 30 | stack: Vec, 31 | frames: Vec, 32 | globals: HashMap, Value>, 33 | open_upvalues: List>, 34 | 35 | objects: List>, 36 | compiler_roots: Vec, 37 | total_allocations: usize, 38 | next_gc: usize, 39 | } 40 | 41 | impl Default for VM { 42 | /// The `VM` constructor. 43 | /// 44 | /// Program output defaults to [`Stdout`]. To customize 45 | /// this behavior, see the [`buffer_output`] method. 46 | /// 47 | /// [`buffer_output`]: #method.buffer_output 48 | /// [`Stdout`]: https://doc.rust-lang.org/std/io/struct.Stdout.html 49 | fn default() -> Self { 50 | VM { 51 | stdout: None, 52 | stack: Vec::with_capacity(256), 53 | frames: Vec::with_capacity(256), 54 | globals: HashMap::new(), 55 | open_upvalues: List::new(), 56 | objects: List::new(), 57 | compiler_roots: Vec::new(), 58 | total_allocations: 0, 59 | next_gc: 1 << 20, 60 | } 61 | } 62 | } 63 | 64 | impl<'source> VM { 65 | /// Compile and run Lox code from a source string. 66 | /// 67 | /// ### Errors 68 | /// 69 | /// Errors are returned in a `Vec`. This is because compilation (not runtime) is able to 70 | /// continue after error(s) are encountered. 71 | pub fn interpret + 'source>(&mut self, source: T) -> Result<(), Vec> { 72 | self.compiler_roots.clear(); 73 | 74 | let fun = Compiler::compile(source.as_ref(), &mut |f| { 75 | let val = Value::Fun(self.alloc(f)); 76 | self.compiler_roots.push(val.clone()); 77 | val 78 | }) 79 | .map_err(|errs| errs.into_iter().map(Into::into).collect::>())?; 80 | 81 | let fun = Value::Fun(self.alloc(fun)); 82 | 83 | self.stack.push(fun); 84 | self.compiler_roots.clear(); 85 | 86 | let _value = self.call_value_from_stack(0); 87 | 88 | self.run().map_err(|err| { 89 | let mut line_no = None; 90 | let mut backtrace = format!("{}", err); 91 | 92 | for frame in self.frames.iter().rev() { 93 | let function = &(*frame.func).name; 94 | let (line, _) = frame.chunk().find_line(frame.inst); 95 | 96 | if line_no.is_none() { 97 | line_no = Some(line); 98 | } 99 | 100 | let _shouldnt_fail = write!(backtrace, "\n=>> line {} in `{}`", line, function); 101 | } 102 | 103 | log::error!("{}", backtrace); 104 | 105 | vec![Error::from_runtime_error(err, line_no)] 106 | }) 107 | } 108 | 109 | /// Define a global variable inside the runtime. 110 | /// 111 | /// Since globals are late bound in Lox, functions that reference the provided name will see 112 | /// the provided value, **even if they were declared in the runtime _before_ calling this 113 | /// method**. 114 | /// 115 | /// ### Example 116 | /// 117 | /// ``` 118 | /// use std::io::Read; 119 | /// 120 | /// let mut vm = lox_lang::VM::default(); 121 | /// vm.buffer_output(true); 122 | /// 123 | /// vm.interpret("fun hello() { print world; }"); 124 | /// vm.define_global("world", "greetings, Earthling.".into()); 125 | /// vm.interpret("hello();"); 126 | /// 127 | /// let mut buffer = String::new(); 128 | /// vm.read_to_string(&mut buffer).unwrap(); 129 | /// assert_eq!(buffer, "greetings, Earthling.\n"); 130 | /// ``` 131 | pub fn define_global(&mut self, name: &T, value: Value) { 132 | self.globals 133 | .insert(name.to_string().into_boxed_str(), value); 134 | } 135 | } 136 | 137 | impl Drop for VM { 138 | fn drop(&mut self) { 139 | // we free all objects here, as the VM is ultimately an arena for them. all other 140 | // references to those objects are weak ones. 141 | // 142 | // note that we do not touch the `open_upvalues` list - they are actually owned by 143 | // `objects`. 144 | while let Some(obj) = self.objects.pop() { 145 | obj.free(); 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/lox_core/chunk.rs: -------------------------------------------------------------------------------- 1 | use std::{convert::TryInto, fmt}; 2 | 3 | use { 4 | super::{Op, Value}, 5 | crate::CompileErrorType, 6 | }; 7 | 8 | #[derive(Clone, Debug)] 9 | struct LineRecord { 10 | line: usize, 11 | count: usize, 12 | } 13 | 14 | #[derive(Clone, Debug)] 15 | pub(crate) struct Chunk { 16 | name: String, 17 | pub constants: Vec, 18 | pub code: Vec, 19 | lines: Vec, 20 | } 21 | 22 | impl Chunk { 23 | pub fn new(name: &T) -> Self { 24 | Self { 25 | name: name.to_string(), 26 | constants: Vec::new(), 27 | code: Vec::new(), 28 | lines: Vec::new(), 29 | } 30 | } 31 | 32 | pub fn write(&mut self, op: Op, line: usize) -> usize { 33 | self.code.push(op); 34 | 35 | match self.lines.last() { 36 | None => self.push_line_record(line), 37 | Some(rec) if rec.line < line => self.push_line_record(line), 38 | Some(rec) if rec.line == line => { 39 | // a little weird looking, but seems like the idiomatic way to update an Option's 40 | // wrapped value in place 41 | for last in self.lines.last_mut().iter_mut() { 42 | last.count += 1; 43 | } 44 | } 45 | _ => unreachable!("Line number stack should not go backward"), 46 | } 47 | 48 | self.code.len() - 1 49 | } 50 | 51 | fn push_line_record(&mut self, line: usize) { 52 | self.lines.push(LineRecord { line, count: 1 }); 53 | } 54 | 55 | /// Adds the value to the Chunk's constant table and returns its index 56 | pub fn add_constant(&mut self, value: Value) -> usize { 57 | self.constants.push(value); 58 | self.constants.len() - 1 59 | } 60 | 61 | fn disassemble_instruction(&self, index: usize, f: &mut W) -> fmt::Result { 62 | write!(f, "{:04} ", index)?; 63 | 64 | let (line, is_first) = self.find_line(index); 65 | 66 | if is_first { 67 | write!(f, "{:4} ", line)?; 68 | } else { 69 | write!(f, " | ")?; 70 | } 71 | 72 | self.code[index].disassemble(self, f) 73 | } 74 | 75 | pub fn find_line(&self, instruction_index: usize) -> (usize, bool) { 76 | let mut line_num = 1; 77 | let mut is_first = true; 78 | 79 | let mut idx_counter = 0; 80 | 'outer: for LineRecord { line, count } in &self.lines { 81 | line_num = *line; 82 | is_first = true; 83 | 84 | for _ in 0..*count { 85 | if idx_counter == instruction_index { 86 | break 'outer; 87 | } 88 | 89 | idx_counter += 1; 90 | is_first = false; 91 | } 92 | } 93 | 94 | (line_num, is_first) 95 | } 96 | 97 | pub fn read_constant(&self, index: usize) -> &Value { 98 | &self.constants[index] 99 | } 100 | 101 | pub fn patch_jump(&mut self, index: usize) -> Result<(), CompileErrorType> { 102 | let distance = self.code.len() - index - 1; 103 | 104 | let distance = match distance.try_into() { 105 | Err(e) => { 106 | log::error!("{}", e); 107 | return Err(CompileErrorType::JumpTooLarge(distance)); 108 | } 109 | Ok(d) => d, 110 | }; 111 | 112 | match self.code.get_mut(index) { 113 | Some(Op::Jump(ref mut old) | Op::JumpIfFalse(ref mut old)) => *old = distance, 114 | other => unreachable!( 115 | "attempted to patch jump instruction with wrong index ({:05x} => {:?})", 116 | index, other, 117 | ), 118 | } 119 | 120 | Ok(()) 121 | } 122 | } 123 | 124 | impl fmt::Display for Chunk { 125 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 126 | writeln!(f, "== {} ==", self.name)?; 127 | 128 | let mut lines = self.lines.iter(); 129 | let mut line; 130 | let mut line_count = 0; // immediately hooks into advancement below 131 | for (idx, op) in self.code.iter().enumerate() { 132 | write!(f, "{:04} ", idx)?; 133 | 134 | if line_count == 0 { 135 | // advance 136 | if let Some(LineRecord { line: l, count: c }) = lines.next() { 137 | line = l; 138 | line_count = *c; 139 | } else { 140 | unreachable!("Should not run out of lines before running out of instructions."); 141 | } 142 | 143 | write!(f, "{:4} ", line)?; 144 | } else { 145 | write!(f, " | ")?; 146 | } 147 | 148 | line_count -= 1; 149 | 150 | op.disassemble(self, f)?; 151 | writeln!(f)?; 152 | } 153 | Ok(()) 154 | } 155 | } 156 | 157 | /// A terrible, shameful, but ultimately necessary thing to do 158 | impl fmt::LowerHex for Chunk { 159 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 160 | let index = f.width().unwrap_or_default(); 161 | 162 | self.disassemble_instruction(index, f) 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/lox_core/ops.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use crate::{Chunk, Upvalue}; 4 | 5 | #[derive(Clone, Debug)] 6 | pub(crate) enum Op { 7 | // constants 8 | Constant(u8), 9 | ConstantLong(u16), 10 | 11 | // immediates 12 | Nil, 13 | True, 14 | False, 15 | 16 | // actions 17 | Pop, 18 | GetGlobal(u8), 19 | GetGlobalLong(u16), 20 | DefineGlobal(u8), 21 | DefineGlobalLong(u16), 22 | SetGlobal(u8), 23 | SetGlobalLong(u16), 24 | GetLocal(u8), 25 | GetLocalLong(u16), 26 | SetLocal(u8), 27 | SetLocalLong(u16), 28 | GetUpvalue(u8), 29 | GetUpvalueLong(u16), 30 | SetUpvalue(u8), 31 | SetUpvalueLong(u16), 32 | GetProperty(u8), 33 | GetPropertyLong(u16), 34 | SetProperty(u8), 35 | SetPropertyLong(u16), 36 | GetSuper(u8), 37 | GetSuperLong(u16), 38 | 39 | // operators 40 | Equal, 41 | Greater, 42 | Less, 43 | Add, 44 | Subtract, 45 | Multiply, 46 | Divide, 47 | Not, 48 | Negate, 49 | 50 | // control flow 51 | Print, 52 | Jump(u16), 53 | JumpIfFalse(u16), 54 | Loop(u16), 55 | Call(u8), 56 | Invoke(u8, u8), 57 | InvokeLong(u16, u8), 58 | SuperInvoke(u8, u8), 59 | SuperInvokeLong(u16, u8), 60 | Closure(u8, Box<[Upvalue]>), 61 | ClosureLong(u16, Box<[Upvalue]>), 62 | CloseUpvalue, 63 | Return, 64 | Class(u8), 65 | ClassLong(u16), 66 | Inherit, 67 | Method(u8), 68 | MethodLong(u16), 69 | } 70 | 71 | impl Op { 72 | pub fn disassemble(&self, chunk: &Chunk, f: &mut W) -> fmt::Result { 73 | macro_rules! fmt { 74 | ($c: expr) => { 75 | write!(f, "{:16} {}", self, $c) 76 | }; 77 | ($c1: expr, $c2: expr) => {{ 78 | fmt!($c1)?; 79 | write!(f, " ({})", $c2) 80 | }}; 81 | } 82 | 83 | macro_rules! closure { 84 | ($i: expr, $u: expr) => {{ 85 | fmt!(chunk.constants[*$i as usize])?; 86 | 87 | write!(f, "\t {{")?; 88 | let mut first = true; 89 | for Upvalue { index, is_local } in $u.iter() { 90 | if first { 91 | first = false; 92 | } else { 93 | write!(f, ",")?; 94 | } 95 | 96 | if *is_local { 97 | write!(f, "local")?; 98 | } else { 99 | write!(f, "upvalue")?; 100 | } 101 | 102 | write!(f, ": {}", index)?; 103 | } 104 | write!(f, "}}") 105 | }}; 106 | } 107 | 108 | match self { 109 | Op::Closure(i, u_vals) => closure!(i, u_vals), 110 | Op::ClosureLong(i, u_vals) => closure!(i, u_vals), 111 | 112 | Op::Invoke(i, c) | Op::SuperInvoke(i, c) => fmt!(chunk.constants[*i as usize], c), 113 | Op::InvokeLong(i, c) | Op::SuperInvokeLong(i, c) => { 114 | fmt!(chunk.constants[*i as usize], c) 115 | } 116 | 117 | Op::Constant(i) 118 | | Op::GetGlobal(i) 119 | | Op::DefineGlobal(i) 120 | | Op::SetGlobal(i) 121 | | Op::Class(i) 122 | | Op::GetProperty(i) 123 | | Op::SetProperty(i) 124 | | Op::GetSuper(i) 125 | | Op::Method(i) => fmt!(chunk.constants[*i as usize]), 126 | 127 | Op::ConstantLong(i) 128 | | Op::GetGlobalLong(i) 129 | | Op::DefineGlobalLong(i) 130 | | Op::SetGlobalLong(i) 131 | | Op::ClassLong(i) 132 | | Op::GetPropertyLong(i) 133 | | Op::SetPropertyLong(i) 134 | | Op::GetSuperLong(i) 135 | | Op::MethodLong(i) => fmt!(chunk.constants[*i as usize]), 136 | 137 | Op::GetLocalLong(c) 138 | | Op::SetLocalLong(c) 139 | | Op::GetUpvalueLong(c) 140 | | Op::SetUpvalueLong(c) 141 | | Op::Jump(c) 142 | | Op::JumpIfFalse(c) 143 | | Op::Loop(c) => fmt!(c), 144 | 145 | Op::GetLocal(c) 146 | | Op::SetLocal(c) 147 | | Op::GetUpvalue(c) 148 | | Op::SetUpvalue(c) 149 | | Op::Call(c) => fmt!(c), 150 | 151 | _ => write!(f, "{}", self), 152 | } 153 | } 154 | } 155 | 156 | impl fmt::Display for Op { 157 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 158 | write!( 159 | f, 160 | "{:width$}", 161 | match self { 162 | Op::Constant(_) => "CONSTANT", 163 | Op::ConstantLong(_) => "CONSTANT_LONG", 164 | 165 | Op::Nil => "NIL", 166 | Op::True => "TRUE", 167 | Op::False => "FALSE", 168 | 169 | Op::Pop => "POP", 170 | Op::GetGlobal(_) => "GET_GLOBAL", 171 | Op::GetGlobalLong(_) => "GET_GLOBAL_LONG", 172 | Op::DefineGlobal(_) => "DEF_GLOBAL", 173 | Op::DefineGlobalLong(_) => "DEF_GLOBAL_LONG", 174 | Op::SetGlobal(_) => "SET_GLOBAL", 175 | Op::SetGlobalLong(_) => "SET_GLOBAL_LONG", 176 | Op::GetLocal(_) => "GET_LOCAL", 177 | Op::GetLocalLong(_) => "GET_LOCAL_LONG", 178 | Op::SetLocal(_) => "SET_LOCAL", 179 | Op::SetLocalLong(_) => "SET_LOCAL_LONG", 180 | Op::GetUpvalue(_) => "GET_UPVALUE", 181 | Op::GetUpvalueLong(_) => "GET_UPVALUE_LONG", 182 | Op::SetUpvalue(_) => "SET_UPVALUE", 183 | Op::SetUpvalueLong(_) => "SET_UPVALUE_LONG", 184 | Op::GetProperty(_) => "GET_PROPERTY", 185 | Op::GetPropertyLong(_) => "GET_PROPERTY_LONG", 186 | Op::SetProperty(_) => "SET_PROPERTY", 187 | Op::SetPropertyLong(_) => "SET_PROPERTY_LONG", 188 | Op::GetSuper(_) => "GET_SUPER", 189 | Op::GetSuperLong(_) => "GET_SUPER_LONG", 190 | 191 | Op::Equal => "EQUAL", 192 | Op::Greater => "GREATER", 193 | Op::Less => "LESS", 194 | Op::Add => "ADD", 195 | Op::Subtract => "SUBTRACT", 196 | Op::Multiply => "MULTIPLY", 197 | Op::Divide => "DIVIDE", 198 | Op::Not => "NOT", 199 | Op::Negate => "NEGATE", 200 | 201 | Op::Print => "PRINT", 202 | Op::Jump(_) => "JMP", 203 | Op::JumpIfFalse(_) => "JMP_FALSE", 204 | Op::Loop(_) => "LOOP", 205 | Op::Call(_) => "CALL", 206 | Op::Invoke(_, _) => "INVOKE", 207 | Op::InvokeLong(_, _) => "INVOKE_LONG", 208 | Op::SuperInvoke(_, _) => "SUPER_INVOKE", 209 | Op::SuperInvokeLong(_, _) => "SUPER_INVOKE_LONG", 210 | Op::Closure(_, _) => "CLOSURE", 211 | Op::ClosureLong(_, _) => "CLOSURE_LONG", 212 | Op::CloseUpvalue => "CLOSE_UPVALUE", 213 | Op::Return => "RETURN", 214 | Op::Class(_) => "CLASS", 215 | Op::ClassLong(_) => "CLASS_LONG", 216 | Op::Inherit => "INHERIT", 217 | Op::Method(_) => "METHOD", 218 | Op::MethodLong(_) => "METHOD_LONG", 219 | }, 220 | width = f.width().unwrap_or_default(), 221 | ) 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /src/compiler/scanner.rs: -------------------------------------------------------------------------------- 1 | use std::str::CharIndices; 2 | 3 | use super::{CompileErrorType, Token, TokenType}; 4 | 5 | type Iter<'a> = CharIndices<'a>; 6 | type Entry = Option<(usize, char)>; 7 | pub(crate) type ScanError = (usize, CompileErrorType); 8 | 9 | pub(crate) struct Scanner<'a> { 10 | source: &'a str, 11 | iter: Iter<'a>, 12 | from: usize, 13 | line: usize, 14 | previous: Entry, 15 | current: Entry, 16 | next: Entry, 17 | } 18 | 19 | impl<'a> Iterator for Scanner<'a> { 20 | type Item = Result, ScanError>; 21 | 22 | fn next(&mut self) -> Option { 23 | self.trim_whitespace(); 24 | self.next(); 25 | 26 | let c = if let Some((_, c)) = self.previous { 27 | c 28 | } else { 29 | return None; 30 | }; 31 | 32 | let token = match c { 33 | 'A'..='Z' | 'a'..='z' | '_' => self.identifier(c), 34 | '0'..='9' => { 35 | self.number(); 36 | TokenType::Number 37 | } 38 | '(' => TokenType::LeftParen, 39 | ')' => TokenType::RightParen, 40 | '{' => TokenType::LeftBrace, 41 | '}' => TokenType::RightBrace, 42 | ';' => TokenType::Semi, 43 | ',' => TokenType::Comma, 44 | '.' => TokenType::Dot, 45 | '-' => TokenType::Minus, 46 | '+' => TokenType::Plus, 47 | '/' => TokenType::Slash, 48 | '*' => TokenType::Star, 49 | '!' if self.peek('=') => { 50 | self.next(); 51 | TokenType::BangEqual 52 | } 53 | '=' if self.peek('=') => { 54 | self.next(); 55 | TokenType::DoubleEqual 56 | } 57 | '<' if self.peek('=') => { 58 | self.next(); 59 | TokenType::LessEqual 60 | } 61 | '>' if self.peek('=') => { 62 | self.next(); 63 | TokenType::GreaterEqual 64 | } 65 | '!' => TokenType::Bang, 66 | '=' => TokenType::Equal, 67 | '<' => TokenType::Less, 68 | '>' => TokenType::Greater, 69 | '"' => { 70 | if let Err(e) = self.string() { 71 | return Some(Err(e)); 72 | } 73 | TokenType::r#String 74 | } 75 | _ => return Some(Err((self.line, CompileErrorType::UnexpectedChar(c)))), 76 | }; 77 | 78 | let text = self.advance(); 79 | 80 | #[cfg(feature = "trace-scanning")] 81 | log::debug!("[line {}] {:?} \"{}\"", self.line, token, text); 82 | 83 | Some(Ok(Token { 84 | r#type: token, 85 | line: self.line, 86 | text, 87 | })) 88 | } 89 | } 90 | 91 | impl<'a> Scanner<'a> { 92 | pub fn new(source: &'a str) -> Self { 93 | let mut new = Self { 94 | source, 95 | iter: source.char_indices(), 96 | from: 0, 97 | line: 1, 98 | previous: None, 99 | current: None, 100 | next: None, 101 | }; 102 | 103 | new.next(); 104 | new.next(); 105 | 106 | new 107 | } 108 | 109 | fn advance(&mut self) -> &'a str { 110 | let head; 111 | if let Some((idx, _)) = self.current { 112 | head = &self.source[self.from..idx]; 113 | self.from = idx; 114 | } else { 115 | head = &self.source[self.from..]; 116 | } 117 | 118 | head 119 | } 120 | 121 | fn next(&mut self) { 122 | self.previous = self.current.take(); 123 | self.current = self.next.take(); 124 | self.next = self.iter.next(); 125 | } 126 | 127 | fn peek(&self, to_match: char) -> bool { 128 | if let Some((_, c)) = self.current { 129 | c == to_match 130 | } else { 131 | false 132 | } 133 | } 134 | 135 | fn peek_next(&self, to_match: char) -> bool { 136 | if let Some((_, c)) = self.next { 137 | c == to_match 138 | } else { 139 | false 140 | } 141 | } 142 | 143 | fn trim_whitespace(&mut self) { 144 | let mut in_comment = false; 145 | 146 | while let Some((_, c)) = self.current { 147 | match c { 148 | '\n' => { 149 | self.line += 1; 150 | 151 | if in_comment { 152 | in_comment = false; 153 | } 154 | } 155 | '/' if self.peek_next('/') => in_comment = true, 156 | _ if in_comment => (), 157 | _ if c.is_whitespace() => (), 158 | _ => break, 159 | } 160 | 161 | self.next(); 162 | } 163 | 164 | self.advance(); 165 | } 166 | 167 | fn string(&mut self) -> Result<(), ScanError> { 168 | while let Some((_, c)) = self.current { 169 | match c { 170 | '\n' => self.line += 1, 171 | '"' => { 172 | self.next(); 173 | break; 174 | } 175 | _ => (), 176 | } 177 | 178 | if self.next.is_none() { 179 | return Err((self.line, CompileErrorType::UnterminatedString)); 180 | } 181 | 182 | self.next(); 183 | } 184 | 185 | Ok(()) 186 | } 187 | 188 | fn number(&mut self) { 189 | while let Some((_, '0'..='9')) = self.current { 190 | self.next(); 191 | } 192 | 193 | if let (Some((_, '.')), Some((_, '0'..='9'))) = (self.current, self.next) { 194 | self.next(); 195 | } 196 | 197 | while let Some((_, '0'..='9')) = self.current { 198 | self.next(); 199 | } 200 | } 201 | 202 | fn current_is_identifier_token(&self) -> bool { 203 | matches!(self.current, Some((_, 'A'..='Z' | 'a'..='z' | '_' | '0'..='9'))) 204 | } 205 | 206 | fn identifier(&mut self, first_char: char) -> TokenType { 207 | if let Some(t) = match first_char { 208 | 'a' => self.maybe_keyword("nd", TokenType::And), 209 | 'b' => self.maybe_keyword("reak", TokenType::Break), 210 | 'd' => self.maybe_keyword("efault", TokenType::r#Default), 211 | 'e' => self.maybe_keyword("lse", TokenType::Else), 212 | 'i' => self.maybe_keyword("f", TokenType::If), 213 | 'n' => self.maybe_keyword("il", TokenType::Nil), 214 | 'o' => self.maybe_keyword("r", TokenType::Or), 215 | 'p' => self.maybe_keyword("rint", TokenType::Print), 216 | 'r' => self.maybe_keyword("eturn", TokenType::Return), 217 | 'v' => self.maybe_keyword("ar", TokenType::Var), 218 | 'w' => self.maybe_keyword("hile", TokenType::While), 219 | 'c' if self.peek('a') => self.maybe_keyword("ase", TokenType::Case), 220 | 'c' if self.peek('l') => self.maybe_keyword("lass", TokenType::Class), 221 | 'c' if self.peek('o') => self.maybe_keyword("ontinue", TokenType::Continue), 222 | 'f' if self.peek('a') => self.maybe_keyword("alse", TokenType::False), 223 | 'f' if self.peek('o') => self.maybe_keyword("or", TokenType::For), 224 | 'f' if self.peek('u') => self.maybe_keyword("un", TokenType::Fun), 225 | 's' if self.peek('u') => self.maybe_keyword("uper", TokenType::Super), 226 | 's' if self.peek('w') => self.maybe_keyword("witch", TokenType::Switch), 227 | 't' if self.peek('h') => self.maybe_keyword("his", TokenType::This), 228 | 't' if self.peek('r') => self.maybe_keyword("rue", TokenType::True), 229 | _ => None, 230 | } { 231 | return t; 232 | } 233 | 234 | while self.current_is_identifier_token() { 235 | self.next(); 236 | } 237 | 238 | TokenType::Identifier 239 | } 240 | 241 | fn maybe_keyword(&mut self, rest: &'static str, success_type: TokenType) -> Option { 242 | for r_c in rest.chars() { 243 | match self.current { 244 | Some((_, c)) if c == r_c => { 245 | self.next(); 246 | } 247 | _ => return None, 248 | } 249 | } 250 | 251 | if self.current_is_identifier_token() { 252 | None 253 | } else { 254 | Some(success_type) 255 | } 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /src/lox_std/mod.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | convert::TryInto, env, error::Error, ffi::OsString, fmt, fs, process::Command, time::Instant, 3 | }; 4 | 5 | use crate::{Class, Instance, NativeFun, Value, VM}; 6 | 7 | type StaticNativeFun = fn(&[Value]) -> Result>; 8 | 9 | impl VM { 10 | pub fn add_std_globals(&mut self) { 11 | // free functions 12 | self.define_closure_value("clock", create_clock_fn()); 13 | self.define_fn_value("readFile", read_file); 14 | self.define_fn_value("writeFile", write_file); 15 | self.define_fn_value("shell", shell); 16 | self.define_fn_value("env", environment_var); 17 | self.define_fn_value("setEnv", set_environment_var); 18 | 19 | // global instances 20 | self.add_arguments_instance(); 21 | } 22 | 23 | fn define_fn_value(&mut self, name: &'static str, f: StaticNativeFun) { 24 | let fn_value = Value::NativeFun(self.alloc(Box::new(f))); 25 | self.define_global(name, fn_value); 26 | } 27 | 28 | fn define_closure_value(&mut self, name: &'static str, c: NativeFun) { 29 | let fn_value = Value::NativeFun(self.alloc(c)); 30 | self.define_global(name, fn_value); 31 | } 32 | 33 | pub fn add_arguments_instance(&mut self) { 34 | let args = env::args_os().collect::>(); 35 | let count = args.len(); 36 | 37 | let get_arg = Box::new(move |a: &[Value]| match a { 38 | [Value::Number(n)] if n.is_finite() && n.is_sign_positive() => Ok(args 39 | .get(n.round().abs() as usize) 40 | .map_or(Value::Nil, |val: &OsString| { 41 | val.to_string_lossy().as_ref().into() 42 | })), 43 | [Value::Number(_)] => Err(Box::new(LoxStdErr::InvalidArgument { 44 | expected: "a positive, finite number as argument index", 45 | }) as Box), 46 | _ if a.len() != 1 => Err(Box::new(LoxStdErr::ArityMismatch { 47 | expected: 1, 48 | got: a.len().try_into().unwrap_or(255), 49 | }) as Box), 50 | _ => Err(Box::new(LoxStdErr::ArgumentTypes { 51 | expected: "a number (command line argument index)", 52 | }) as Box), 53 | }); 54 | 55 | let mut arg_class = Class::new(&"Arguments"); 56 | arg_class 57 | .methods 58 | .insert("get".into(), Value::NativeFun(self.alloc(get_arg))); 59 | 60 | let mut arg_inst = Instance::new(self.alloc(arg_class)); 61 | arg_inst 62 | .fields 63 | .insert("count".into(), Value::Number(count as f64)); 64 | let arg_value = Value::Instance(self.alloc(arg_inst)); 65 | self.define_global("arguments", arg_value); 66 | } 67 | } 68 | 69 | #[derive(Debug)] 70 | enum LoxStdErr { 71 | ArityMismatch { expected: u8, got: u8 }, 72 | ArgumentTypes { expected: &'static str }, 73 | InvalidArgument { expected: &'static str }, 74 | } 75 | 76 | impl fmt::Display for LoxStdErr { 77 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 78 | match self { 79 | Self::ArityMismatch { expected, got } => { 80 | write!(f, "expected {} arguments, got {}", expected, got) 81 | } 82 | Self::ArgumentTypes { expected } => { 83 | write!(f, "wrong argument type(s) supplied: expected {}", expected) 84 | } 85 | Self::InvalidArgument { expected } => write!( 86 | f, 87 | "invalid argument value(s) supplied: expected {}", 88 | expected 89 | ), 90 | } 91 | } 92 | } 93 | 94 | impl Error for LoxStdErr {} 95 | 96 | /// Create a stateful `clock` function. 97 | /// 98 | /// Comparable to the `clock` function in `time.h`. When invoked inside the runtime, it returns a 99 | /// floating-point number representing the number of seconds elapsed since the closure was created. 100 | #[must_use] 101 | pub fn create_clock_fn() -> NativeFun { 102 | let start_time = Instant::now(); 103 | let clock_cls = move |_: &[Value]| Ok(start_time.elapsed().as_secs_f64().into()); 104 | Box::new(clock_cls) as NativeFun 105 | } 106 | 107 | /// Read a file into a string. 108 | /// 109 | /// ### Errors 110 | /// 111 | /// An `Err` will be returned if: 112 | /// 113 | /// - The specified file is not found. 114 | /// - The specified file is not valid UTF-8. 115 | pub fn read_file(args: &[Value]) -> Result> { 116 | match args { 117 | [Value::r#String(s)] => Ok(fs::read_to_string(s.as_ref())?.into()), 118 | _ if args.len() != 1 => Err(Box::new(LoxStdErr::ArityMismatch { 119 | expected: 1, 120 | got: args.len().try_into().unwrap_or(255), 121 | })), 122 | _ => Err(Box::new(LoxStdErr::ArgumentTypes { 123 | expected: "string for filename", 124 | })), 125 | } 126 | } 127 | 128 | /// Write a string into a file. 129 | /// 130 | /// If the file does not already exist, it will be created. If it does exist, its contents will be 131 | /// replaced with the string passed in. 132 | /// 133 | /// ### Errors 134 | /// 135 | /// An `Err` will be returned if: 136 | /// 137 | /// - The specified file is not found. 138 | /// - The specified file is not valid UTF-8. 139 | pub fn write_file(args: &[Value]) -> Result> { 140 | match args { 141 | [Value::r#String(file_name), Value::r#String(contents)] => { 142 | fs::write(file_name.as_ref(), contents.as_ref())?; 143 | Ok(Value::Nil) 144 | } 145 | _ if args.len() != 2 => Err(Box::new(LoxStdErr::ArityMismatch { 146 | expected: 2, 147 | got: args.len().try_into().unwrap_or(255), 148 | })), 149 | _ => Err(Box::new(LoxStdErr::ArgumentTypes { 150 | expected: "string for filename", 151 | })), 152 | } 153 | } 154 | 155 | /// Run a shell command. 156 | /// 157 | /// ### Errors 158 | /// 159 | /// An `Err` will be returned if the command fails to execute. 160 | pub fn shell(args: &[Value]) -> Result> { 161 | match args { 162 | [Value::r#String(cmd)] => { 163 | let output = if cfg!(target_os = "windows") { 164 | Command::new("cmd").args(&["/C", cmd]).output()? 165 | } else { 166 | Command::new("sh").args(&["-c", cmd]).output()? 167 | }; 168 | 169 | Ok(String::from_utf8_lossy(&output.stdout).as_ref().into()) 170 | } 171 | _ if args.len() != 1 => Err(Box::new(LoxStdErr::ArityMismatch { 172 | expected: 1, 173 | got: args.len().try_into().unwrap_or(255), 174 | })), 175 | _ => Err(Box::new(LoxStdErr::ArgumentTypes { 176 | expected: "string for shell command", 177 | })), 178 | } 179 | } 180 | 181 | /// Get the value of an environment variable by name. 182 | /// 183 | /// If the requested variable does not exist, `nil` is returned. If the variable's value contains 184 | /// invalid Unicode, those characters will be replaced with the Unicode replacement character 185 | /// (`U+FFFD`). 186 | /// 187 | /// # Errors 188 | /// 189 | /// Returns an error if no variable name is provided. 190 | pub fn environment_var(args: &[Value]) -> Result> { 191 | match args { 192 | [Value::r#String(name)] => { 193 | Ok(env::var_os(name.as_ref()) 194 | .map_or(Value::Nil, |s| s.to_string_lossy().as_ref().into())) 195 | } 196 | _ if args.len() != 1 => Err(Box::new(LoxStdErr::ArityMismatch { 197 | expected: 1, 198 | got: args.len().try_into().unwrap_or(255), 199 | })), 200 | _ => Err(Box::new(LoxStdErr::ArgumentTypes { 201 | expected: "name of environment variable", 202 | })), 203 | } 204 | } 205 | 206 | /// Set the value of an environment variable. 207 | /// 208 | /// # Errors 209 | /// 210 | /// Returns an error if either variable name or value is omitted. 211 | pub fn set_environment_var(args: &[Value]) -> Result> { 212 | match args { 213 | [Value::r#String(name), Value::r#String(value)] => { 214 | env::set_var(name.as_ref(), value.as_ref()); 215 | Ok(Value::Nil) 216 | } 217 | _ if args.len() != 2 => Err(Box::new(LoxStdErr::ArityMismatch { 218 | expected: 2, 219 | got: args.len().try_into().unwrap_or(255), 220 | })), 221 | _ => Err(Box::new(LoxStdErr::ArgumentTypes { 222 | expected: "name of environment variable", 223 | })), 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /src/lox_core/mod.rs: -------------------------------------------------------------------------------- 1 | use std::{any::Any, cmp, collections::HashMap, error, fmt}; 2 | 3 | mod chunk; 4 | mod obj; 5 | mod ops; 6 | 7 | pub(crate) use {chunk::Chunk, obj::Gc, ops::Op}; 8 | 9 | use super::RuntimeError; 10 | 11 | /// Underlying representation of runtime values in Lox. 12 | #[derive(Clone)] 13 | #[non_exhaustive] 14 | pub enum Value { 15 | Nil, 16 | Boolean(bool), 17 | Number(f64), 18 | r#String(Box), 19 | NativeFun(Gc), 20 | 21 | #[doc(hidden)] 22 | Fun(Gc), 23 | #[doc(hidden)] 24 | Closure(Gc, Box<[Gc]>), 25 | #[doc(hidden)] 26 | BoundMethod { 27 | recv: Gc, 28 | fun: Gc, 29 | upvalues: Box<[Gc]>, 30 | }, 31 | #[doc(hidden)] 32 | Class(Gc), 33 | #[doc(hidden)] 34 | Instance(Gc), 35 | } 36 | 37 | impl fmt::Display for Value { 38 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 39 | match self { 40 | Self::Nil => write!(f, "nil"), 41 | Self::Boolean(b) => write!(f, "{}", b), 42 | Self::Number(v) => write!(f, "{}", v), 43 | Self::r#String(s) => write!(f, "{}", s), 44 | Self::Fun(fun) | Self::Closure(fun, _) | Self::BoundMethod { fun, .. } => { 45 | write!(f, "{}", **fun) 46 | } 47 | Self::Class(c) => write!(f, "{}", **c), 48 | Self::Instance(i) => write!(f, "{} instance", *i.class), 49 | Self::NativeFun(_) => write!(f, "#"), 50 | } 51 | } 52 | } 53 | 54 | impl fmt::Debug for Value { 55 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 56 | match self { 57 | Self::Nil => write!(f, "Nil"), 58 | Self::Boolean(b) => write!(f, "Boolean({})", b), 59 | Self::Number(v) => write!(f, "Number({})", v), 60 | Self::r#String(s) => write!(f, "String({})", s), 61 | Self::Fun(fun) => write!(f, "Fun({})", **fun), 62 | Self::Closure(fun, upvals) => write!(f, "Closure({}, {:#?})", **fun, upvals), 63 | Self::Class(c) => write!(f, "Class({})", **c), 64 | Self::Instance(i) => write!(f, "Instance({:?})", i), 65 | Self::BoundMethod { 66 | recv, 67 | fun, 68 | upvalues, 69 | } => write!( 70 | f, 71 | "BoundMethod {{ {:?}#{} {:#?} }}", 72 | **recv, **fun, upvalues 73 | ), 74 | Self::NativeFun(ptr) => write!(f, "NativeFun({:p})", ptr), 75 | } 76 | } 77 | } 78 | 79 | impl cmp::PartialEq for Value { 80 | fn eq(&self, other: &Self) -> bool { 81 | match (self, other) { 82 | (Self::Nil, Self::Nil) => true, 83 | (Self::Boolean(a), Self::Boolean(b)) => a == b, 84 | (Self::Number(a), Self::Number(b)) => a == b, 85 | (Self::r#String(a), Self::r#String(b)) => a == b, 86 | _ => false, 87 | } 88 | } 89 | } 90 | 91 | impl From for Value { 92 | fn from(b: bool) -> Self { 93 | Self::Boolean(b) 94 | } 95 | } 96 | 97 | impl From for Value { 98 | fn from(f: f64) -> Self { 99 | Self::Number(f) 100 | } 101 | } 102 | 103 | impl From<&str> for Value { 104 | fn from(s: &str) -> Self { 105 | s.to_string().into() 106 | } 107 | } 108 | 109 | impl From for Value { 110 | fn from(s: String) -> Self { 111 | Self::r#String(s.into_boxed_str()) 112 | } 113 | } 114 | 115 | impl Value { 116 | pub(crate) fn is_falsey(&self) -> bool { 117 | matches!(self, Self::Nil | Self::Boolean(false)) 118 | } 119 | 120 | pub(crate) fn negate(self) -> Result { 121 | if let Self::Number(a) = self { 122 | Ok(Self::Number(-a)) 123 | } else { 124 | Err(RuntimeError::ArgumentTypes) 125 | } 126 | } 127 | 128 | pub(crate) fn mark(&self, grays: &mut Vec>) { 129 | match self { 130 | Self::Fun(f) => { 131 | f.mark(); 132 | grays.push(f.as_any()); 133 | } 134 | Self::Closure(f, u) => { 135 | f.mark(); 136 | grays.push(f.as_any()); 137 | 138 | for u_val in u.iter() { 139 | u_val.mark(); 140 | grays.push(u_val.as_any()); 141 | } 142 | } 143 | Self::Class(c) => { 144 | c.mark(); 145 | grays.push(c.as_any()); 146 | } 147 | Self::Instance(i) => { 148 | i.mark(); 149 | grays.push(i.as_any()); 150 | } 151 | Self::BoundMethod { 152 | recv, 153 | fun, 154 | upvalues, 155 | } => { 156 | recv.mark(); 157 | grays.push(recv.as_any()); 158 | 159 | fun.mark(); 160 | grays.push(fun.as_any()); 161 | 162 | for u_val in upvalues.iter() { 163 | u_val.mark(); 164 | grays.push(u_val.as_any()); 165 | } 166 | } 167 | Self::NativeFun(nf) => { 168 | nf.mark(); 169 | grays.push(nf.as_any()); 170 | } 171 | _ => (), 172 | } 173 | } 174 | } 175 | 176 | impl Gc { 177 | pub(crate) fn blacken(self, gray_stack: &mut Vec>) { 178 | if self.is_marked() { 179 | return; 180 | } 181 | 182 | #[cfg(feature = "trace-gc")] 183 | log::debug!("{0:p} blacken\t{0:?}", self); 184 | 185 | if let Some(f) = self.downcast_ref::() { 186 | for c in &f.chunk.constants { 187 | c.mark(gray_stack); 188 | } 189 | } else if let Some(u) = self.downcast_ref::() { 190 | if let UpvalueRef::Captured(ref v) = *u { 191 | v.mark(gray_stack); 192 | } 193 | } else if let Some(Class { methods, .. }) = self.downcast_ref::() { 194 | for method in methods.values() { 195 | method.mark(gray_stack); 196 | } 197 | } else if let Some(Instance { class, fields }) = self.downcast_ref::() { 198 | class.mark(); 199 | gray_stack.push(class.as_any()); 200 | 201 | for value in fields.values() { 202 | value.mark(gray_stack); 203 | } 204 | } 205 | } 206 | } 207 | 208 | #[derive(Clone, Debug)] 209 | pub struct Fun { 210 | pub(crate) name: Box, 211 | pub(crate) arity: u8, 212 | pub(crate) chunk: Chunk, 213 | } 214 | 215 | impl fmt::Display for Fun { 216 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 217 | write!(f, "#", self.name, self.arity) 218 | } 219 | } 220 | 221 | impl Fun { 222 | pub(crate) fn new(name: &T, arity: u8) -> Self { 223 | Self { 224 | arity, 225 | name: name.to_string().into_boxed_str(), 226 | chunk: Chunk::new(name), 227 | } 228 | } 229 | } 230 | 231 | #[derive(Clone, Debug)] 232 | pub enum UpvalueRef { 233 | Live(usize), 234 | Captured(Box), 235 | } 236 | 237 | impl UpvalueRef { 238 | pub(crate) fn new(slot: usize) -> Self { 239 | Self::Live(slot) 240 | } 241 | 242 | pub(crate) fn close(&mut self, ptr: Value) { 243 | *self = Self::Captured(Box::new(ptr)); 244 | } 245 | } 246 | 247 | impl fmt::Display for UpvalueRef { 248 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 249 | match self { 250 | Self::Live(idx) => write!(f, "at stack index {}", idx), 251 | Self::Captured(ptr) => write!(f, "at address {:p}", ptr), 252 | } 253 | } 254 | } 255 | 256 | #[derive(Clone, Debug)] 257 | pub struct Class { 258 | pub(crate) name: Box, 259 | pub(crate) methods: HashMap, Value>, 260 | } 261 | 262 | impl Class { 263 | pub(crate) fn new(name: &T) -> Self { 264 | Self { 265 | name: name.to_string().into_boxed_str(), 266 | methods: HashMap::new(), 267 | } 268 | } 269 | } 270 | 271 | impl fmt::Display for Class { 272 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 273 | write!(f, "{}", self.name) 274 | } 275 | } 276 | 277 | #[derive(Clone, Debug)] 278 | pub struct Instance { 279 | pub(crate) class: Gc, 280 | pub(crate) fields: HashMap, Value>, 281 | } 282 | 283 | impl Instance { 284 | pub(crate) fn new(class: Gc) -> Self { 285 | Self { 286 | class, 287 | fields: HashMap::new(), 288 | } 289 | } 290 | } 291 | 292 | /// A natively-implemented function that can be called from Lox code. 293 | /// 294 | /// ## Example 295 | /// 296 | /// ``` 297 | /// # use {std::{error::Error, fmt, io::Read}, lox_lang::{Value, VM}}; 298 | /// # #[derive(Debug)] 299 | /// # struct MyError; 300 | /// # impl fmt::Display for MyError { 301 | /// # fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 302 | /// # write!(f, "wrong types") 303 | /// # } 304 | /// # } 305 | /// # impl Error for MyError {} 306 | /// # let mut vm = VM::default(); 307 | /// # vm.buffer_output(true); 308 | /// /// This function wraps `str::replace` for use in Lox 309 | /// let replace = vm.alloc(Box::new(|args: &[Value]| { 310 | /// match args { 311 | /// [Value::r#String(text), Value::r#String(pat), Value::r#String(rep)] => 312 | /// Ok(text.replace(pat.as_ref(), rep.as_ref()).into()), 313 | /// _ => Err(Box::new(MyError) as Box), 314 | /// } 315 | /// }) as lox_lang::NativeFun); 316 | /// 317 | /// vm.define_global("replace", Value::NativeFun(replace)); 318 | /// 319 | /// vm.interpret(r#" 320 | /// var proverb = "what is old becomes new again"; 321 | /// print replace(proverb, "new", "old"); 322 | /// "#); 323 | /// 324 | /// # let mut output = String::new(); 325 | /// # vm.read_to_string(&mut output).unwrap(); 326 | /// assert_eq!(output, "what is old becomes old again\n"); 327 | /// ``` 328 | pub type NativeFun = Box Result>>; 329 | -------------------------------------------------------------------------------- /src/vm/run.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use { 4 | super::frame::CallFrame, 5 | crate::{Class, Fun, Gc, Instance, Op, RuntimeError, Upvalue, UpvalueRef, Value}, 6 | }; 7 | 8 | type RunResult = Result<(), RuntimeError>; 9 | 10 | impl super::VM { 11 | pub(super) fn run(&mut self) -> RunResult { 12 | macro_rules! binary_op_body { 13 | ($op: tt, $variant: ident) => { 14 | if let (Value::Number(a), Value::Number(b)) = self.pop_pair()? { 15 | self.stack.push(Value::$variant(a $op b)); 16 | } 17 | }; 18 | } 19 | 20 | macro_rules! binary_op { 21 | ($op: tt, $variant: ident) => {{ 22 | match (self.peek(1)?, self.peek(0)?) { 23 | (Value::Number(_), Value::Number(_)) => (), 24 | _ => return Err(RuntimeError::ArgumentTypes), 25 | } 26 | 27 | // should be infallible now 28 | binary_op_body!($op, $variant) 29 | }}; 30 | } 31 | 32 | while let Some(instruction) = self.step() { 33 | { 34 | #![cfg(feature = "trace-execution")] 35 | // print stack before operation 36 | log::debug!( 37 | " {}", 38 | self.stack 39 | .iter() 40 | .map(|v| format!("[ {} ]", v)) 41 | .collect::() 42 | ); 43 | 44 | // print operation and arguments 45 | let frame = self.frame(); 46 | log::debug!("{:1$x}", frame.chunk(), frame.inst); 47 | } 48 | 49 | self.frame_mut().inst += 1; 50 | 51 | match instruction { 52 | Op::Constant(index) => { 53 | self.stack.push(self.read_constant(index).clone()); 54 | } 55 | Op::ConstantLong(index) => { 56 | self.stack.push(self.read_constant(index).clone()); 57 | } 58 | Op::Nil => self.stack.push(Value::Nil), 59 | Op::True => self.stack.push(Value::Boolean(true)), 60 | Op::False => self.stack.push(Value::Boolean(false)), 61 | Op::Pop => { 62 | self.stack.pop(); 63 | } 64 | Op::GetGlobal(index) => self.fetch_global(index)?, 65 | Op::GetGlobalLong(index) => self.fetch_global(index)?, 66 | Op::DefineGlobal(index) => self.define_global_from_stack(index)?, 67 | Op::DefineGlobalLong(index) => self.define_global_from_stack(index)?, 68 | Op::SetGlobal(index) => self.set_global(index)?, 69 | Op::SetGlobalLong(index) => self.set_global(index)?, 70 | Op::GetLocal(index) => self.fetch_local(index)?, 71 | Op::GetLocalLong(index) => self.fetch_local(index)?, 72 | Op::SetLocal(index) => self.set_local(index)?, 73 | Op::SetLocalLong(index) => self.set_local(index)?, 74 | Op::GetUpvalue(index) => self.fetch_upvalue(index)?, 75 | Op::GetUpvalueLong(index) => self.fetch_upvalue(index)?, 76 | Op::SetUpvalue(index) => self.set_upvalue(index)?, 77 | Op::SetUpvalueLong(index) => self.set_upvalue(index)?, 78 | Op::GetProperty(index) => self.get_property(index)?, 79 | Op::GetPropertyLong(index) => self.get_property(index)?, 80 | Op::SetProperty(index) => self.set_property(index)?, 81 | Op::SetPropertyLong(index) => self.set_property(index)?, 82 | Op::GetSuper(index) => self.get_super(index)?, 83 | Op::GetSuperLong(index) => self.get_super(index)?, 84 | Op::Equal => { 85 | let (a, b) = self.pop_pair()?; 86 | self.stack.push(Value::Boolean(b == a)); 87 | } 88 | Op::Greater => binary_op!(>, Boolean), 89 | Op::Less => binary_op!(<, Boolean), 90 | Op::Add => match (self.peek(1)?, self.peek(0)?) { 91 | (Value::Number(_), Value::Number(_)) => { 92 | binary_op_body!(+, Number); 93 | } 94 | (Value::r#String(_), Value::r#String(_)) => { 95 | if let (Value::r#String(a), Value::r#String(b)) = self.pop_pair()? { 96 | let mut a = a.into_string(); 97 | a.push_str(&b); 98 | self.stack.push(Value::r#String(a.into_boxed_str())); 99 | } 100 | } 101 | _ => return Err(RuntimeError::ArgumentTypes), 102 | }, 103 | Op::Subtract => binary_op!(-, Number), 104 | Op::Multiply => binary_op!(*, Number), 105 | Op::Divide => binary_op!(/, Number), 106 | Op::Not => { 107 | if let Some(val) = self.stack.pop() { 108 | self.stack.push(Value::Boolean(val.is_falsey())); 109 | } 110 | } 111 | Op::Negate => { 112 | if let Some(value) = self.stack.pop() { 113 | self.stack.push(value.negate()?); 114 | } 115 | } 116 | Op::Print => { 117 | if let Some(val) = self.stack.pop() { 118 | self.print(format_args!("{}\n", val)); 119 | } 120 | } 121 | Op::JumpIfFalse(distance) => { 122 | if self.peek(0)?.is_falsey() { 123 | self.frame_mut().inst += distance as usize; 124 | } 125 | } 126 | Op::Jump(distance) => self.frame_mut().inst += distance as usize, 127 | Op::Loop(distance) => self.frame_mut().inst -= distance as usize, 128 | Op::Call(arg_count) => { 129 | self.call_value_from_stack(arg_count)?; 130 | } 131 | Op::Invoke(index, arg_count) => { 132 | let name = self.read_string(index).to_string(); 133 | self.invoke(name, arg_count)?; 134 | } 135 | Op::InvokeLong(index, arg_count) => { 136 | let name = self.read_string(index).to_string(); 137 | self.invoke(name, arg_count)?; 138 | } 139 | Op::SuperInvoke(index, arg_count) => { 140 | let name = self.read_string(index).to_string(); 141 | if let Value::Class(super_class) = 142 | self.stack.pop().ok_or(RuntimeError::StackEmpty)? 143 | { 144 | self.invoke_from_class(super_class, &name, arg_count)?; 145 | } else { 146 | return Err(RuntimeError::ArgumentTypes); 147 | } 148 | } 149 | Op::SuperInvokeLong(index, arg_count) => { 150 | let name = self.read_string(index).to_string(); 151 | if let Value::Class(super_class) = 152 | self.stack.pop().ok_or(RuntimeError::StackEmpty)? 153 | { 154 | self.invoke_from_class(super_class, &name, arg_count)?; 155 | } else { 156 | return Err(RuntimeError::ArgumentTypes); 157 | } 158 | } 159 | Op::Closure(index, upvals) => self.create_closure(index, &upvals)?, 160 | Op::ClosureLong(index, upvals) => self.create_closure(index, &upvals)?, 161 | Op::CloseUpvalue => { 162 | let idx = self 163 | .stack 164 | .len() 165 | .checked_sub(1) 166 | .ok_or(RuntimeError::StackEmpty)?; 167 | self.close_upvalues(idx); 168 | self.stack.pop(); 169 | } 170 | Op::Return => { 171 | let base_ptr = self.frame().base; 172 | let result = self.stack.pop().ok_or(RuntimeError::StackEmpty)?; 173 | 174 | self.close_upvalues(base_ptr); 175 | 176 | self.frames.pop(); 177 | if self.frames.is_empty() { 178 | self.stack.pop(); 179 | break; 180 | } 181 | 182 | self.stack.truncate(base_ptr as usize); 183 | self.stack.push(result); 184 | } 185 | Op::Class(index) => { 186 | let name = Class::new(self.read_constant(index)); 187 | let val = Value::Class(self.alloc(name)); 188 | self.stack.push(val); 189 | } 190 | Op::ClassLong(index) => { 191 | let name = Class::new(self.read_constant(index)); 192 | let val = Value::Class(self.alloc(name)); 193 | self.stack.push(val); 194 | } 195 | Op::Inherit => { 196 | if let (Value::Class(mut sub_class), Value::Class(super_class)) = ( 197 | self.stack.pop().ok_or(RuntimeError::StackEmpty)?, 198 | self.peek(0)?.clone(), 199 | ) { 200 | for (name, value) in &super_class.methods { 201 | sub_class.methods.insert(name.clone(), value.clone()); 202 | } 203 | } else { 204 | return Err(RuntimeError::ArgumentTypes); 205 | } 206 | } 207 | Op::Method(index) => self.define_method(index)?, 208 | Op::MethodLong(index) => self.define_method(index)?, 209 | } 210 | } 211 | 212 | Ok(()) 213 | } 214 | 215 | fn frame(&self) -> &CallFrame { 216 | if let Some(frame) = self.frames.last() { 217 | return frame; 218 | } 219 | 220 | unreachable!("Current call frame not found.") 221 | } 222 | 223 | fn frame_mut(&mut self) -> &mut CallFrame { 224 | if let Some(frame) = self.frames.last_mut() { 225 | return frame; 226 | } 227 | 228 | unreachable!("Current call frame not found.") 229 | } 230 | 231 | fn step(&mut self) -> Option { 232 | let frame = self.frame(); 233 | frame.chunk().code.get(frame.inst).cloned() 234 | } 235 | 236 | fn read_constant>(&self, index: T) -> &Value { 237 | self.frame().chunk().read_constant(index.into()) 238 | } 239 | 240 | fn read_string + fmt::Display + Copy>(&self, index: T) -> &str { 241 | match self.read_constant(index) { 242 | Value::r#String(s) => s, 243 | c => 244 | unreachable!( 245 | "Invariant violation: tried to load a string value from the constant table at index {}; found non-string value {}", 246 | index, c 247 | ), 248 | } 249 | } 250 | 251 | fn define_global_from_stack + fmt::Display + Copy>( 252 | &mut self, 253 | index: T, 254 | ) -> RunResult { 255 | if let Some(value) = self.stack.pop() { 256 | let var_name = self.read_string(index).to_string().into_boxed_str(); 257 | self.globals.insert(var_name, value); 258 | Ok(()) 259 | } else { 260 | Err(RuntimeError::BadStackIndex(index.into(), self.stack.len())) 261 | } 262 | } 263 | 264 | fn fetch_global + fmt::Display + Copy>(&mut self, index: T) -> RunResult { 265 | let var_name = self.read_string(index); 266 | if let Some(value) = self.globals.get(var_name).cloned() { 267 | self.stack.push(value); 268 | Ok(()) 269 | } else { 270 | Err(RuntimeError::UndefinedGlobal(var_name.to_string())) 271 | } 272 | } 273 | 274 | fn set_global + fmt::Display + Copy>(&mut self, index: T) -> RunResult { 275 | let var_str = self.read_string(index).to_string(); 276 | if self.globals.contains_key(&*var_str) { 277 | let value = self.peek(0)?.clone(); 278 | self.globals.insert(var_str.into_boxed_str(), value); 279 | Ok(()) 280 | } else { 281 | Err(RuntimeError::UndefinedGlobal(var_str)) 282 | } 283 | } 284 | 285 | fn fetch_local>(&mut self, index: T) -> RunResult { 286 | let base_ptr = self.frame().base; 287 | 288 | let index = index.into(); 289 | if let Some(val) = self.stack.get(index + base_ptr).cloned() { 290 | self.stack.push(val); 291 | Ok(()) 292 | } else { 293 | Err(RuntimeError::BadStackIndex(index, self.stack.len())) 294 | } 295 | } 296 | 297 | fn set_local>(&mut self, index: T) -> RunResult { 298 | let base_ptr = self.frame().base; 299 | let new_val = self.peek(0)?.clone(); 300 | 301 | if let Some(el) = self.stack.get_mut(index.into() + base_ptr) { 302 | *el = new_val; 303 | } 304 | 305 | Ok(()) 306 | } 307 | 308 | fn pop_pair(&mut self) -> Result<(Value, Value), RuntimeError> { 309 | if let Some(b) = self.stack.pop() { 310 | if let Some(a) = self.stack.pop() { 311 | return Ok((a, b)); 312 | } 313 | } 314 | 315 | Err(RuntimeError::StackEmpty) 316 | } 317 | 318 | fn pop_many(&mut self, count: u16) -> RunResult { 319 | if let Some(new_len) = self.stack.len().checked_sub(count as usize) { 320 | self.stack.truncate(new_len); 321 | Ok(()) 322 | } else { 323 | Err(RuntimeError::StackEmpty) 324 | } 325 | } 326 | 327 | fn peek(&self, distance: usize) -> Result<&Value, RuntimeError> { 328 | if let Some(idx) = self.stack.len().checked_sub(distance + 1) { 329 | if let Some(val) = self.stack.get(idx) { 330 | Ok(val) 331 | } else { 332 | Err(RuntimeError::BadStackIndex(idx, self.stack.len())) 333 | } 334 | } else { 335 | Err(RuntimeError::StackEmpty) 336 | } 337 | } 338 | 339 | fn create_closure>(&mut self, index: T, upvals: &[Upvalue]) -> RunResult { 340 | if let Value::Fun(f) = self.read_constant(index).clone() { 341 | let mut upvalues = Vec::new(); 342 | for Upvalue { index, is_local } in upvals { 343 | upvalues.push(if *is_local { 344 | self.capture_upvalue(self.frame().base + index) 345 | } else { 346 | self.frame().upvalues[*index] 347 | }); 348 | } 349 | 350 | self.stack 351 | .push(Value::Closure(f, upvalues.into_boxed_slice())); 352 | Ok(()) 353 | } else { 354 | Err(RuntimeError::ArgumentTypes) 355 | } 356 | } 357 | 358 | // possible TODO: double iteration here. not sure if it's worth the custom code to do it in one 359 | // iteration, given that the list is almost always empty or very short 360 | fn capture_upvalue(&mut self, slot: usize) -> Gc { 361 | if let Some(r) = self 362 | .open_upvalues 363 | .find(|u_val| matches!(&**u_val, UpvalueRef::Live(s) if *s == slot)) 364 | { 365 | *r 366 | } else { 367 | let allocated = self.alloc(UpvalueRef::new(slot)); 368 | 369 | *self.open_upvalues.insert_before( 370 | allocated, 371 | |u_val| matches!(&**u_val, UpvalueRef::Live(s) if *s < slot), 372 | ) 373 | } 374 | } 375 | 376 | fn fetch_upvalue>(&mut self, index: T) -> RunResult { 377 | match self.frame().get_upvalue(index.into()) { 378 | Some(u) => match &*u { 379 | UpvalueRef::Live(slot) => { 380 | if let Some(val) = self.stack.get(*slot).cloned() { 381 | self.stack.push(val); 382 | Ok(()) 383 | } else { 384 | Err(RuntimeError::BadStackIndex(*slot, self.stack.len())) 385 | } 386 | } 387 | UpvalueRef::Captured(ptr) => { 388 | let val = (*ptr).clone(); 389 | self.stack.push(*val); 390 | Ok(()) 391 | } 392 | }, 393 | _ => unreachable!(), 394 | } 395 | } 396 | 397 | fn set_upvalue>(&mut self, index: T) -> RunResult { 398 | match self.frame().get_upvalue(index.into()) { 399 | Some(mut u) => match &mut *u { 400 | UpvalueRef::Live(slot) => { 401 | let slot = *slot; 402 | let val = self.peek(0)?.clone(); 403 | if let Some(el) = self.stack.get_mut(slot) { 404 | *el = val; 405 | Ok(()) 406 | } else { 407 | Err(RuntimeError::BadStackIndex(slot, self.stack.len())) 408 | } 409 | } 410 | UpvalueRef::Captured(ref mut val) => { 411 | **val = self.peek(0)?.clone(); 412 | Ok(()) 413 | } 414 | }, 415 | _ => unreachable!(), 416 | } 417 | } 418 | 419 | fn close_upvalues(&mut self, max_height: usize) { 420 | while let Some(u_val) = self.open_upvalues.peek() { 421 | let slot = if let UpvalueRef::Live(slot) = &**u_val { 422 | if *slot < max_height { 423 | break; 424 | } 425 | 426 | *slot 427 | } else { 428 | unreachable!("open upvalue list should not contain any closed upvalues") 429 | }; 430 | 431 | if let Some(mut ptr) = self.open_upvalues.pop() { 432 | ptr.close(self.stack[slot].clone()); 433 | } 434 | } 435 | } 436 | 437 | fn get_property + fmt::Display + Copy>(&mut self, index: T) -> RunResult { 438 | match self.stack.pop() { 439 | None => Err(RuntimeError::StackEmpty), 440 | Some(Value::Instance(i)) => { 441 | let var_str = self.read_string(index); 442 | 443 | if let Some(val) = i.fields.get(var_str) { 444 | self.stack.push(val.clone()); 445 | Ok(()) 446 | } else { 447 | let var_string = var_str.to_string(); 448 | self.bind_method(i.class, i, var_string) 449 | } 450 | } 451 | Some(_) => Err(RuntimeError::ArgumentTypes), 452 | } 453 | } 454 | 455 | fn bind_method>( 456 | &mut self, 457 | class: Gc, 458 | instance: Gc, 459 | name: T, 460 | ) -> RunResult { 461 | if let Some(m) = class.methods.get(name.as_ref()) { 462 | let (fun, upvalues) = match m { 463 | Value::Fun(f) => (f, Box::new([]) as Box<[_]>), 464 | Value::Closure(f, u) => (f, u.clone()), 465 | _ => return Err(RuntimeError::ArgumentTypes), 466 | }; 467 | 468 | self.stack.push(Value::BoundMethod { 469 | recv: instance, 470 | fun: *fun, 471 | upvalues, 472 | }); 473 | 474 | Ok(()) 475 | } else { 476 | Err(RuntimeError::UndefinedProperty(name.as_ref().to_string())) 477 | } 478 | } 479 | 480 | fn set_property + fmt::Display + Copy>(&mut self, index: T) -> RunResult { 481 | let value = self.stack.pop().ok_or(RuntimeError::StackEmpty)?; 482 | 483 | match self.stack.pop() { 484 | None => Err(RuntimeError::StackEmpty), 485 | Some(Value::Instance(mut i)) => { 486 | let var_str = self.read_string(index); 487 | i.fields 488 | .insert(var_str.to_string().into_boxed_str(), value.clone()); 489 | self.stack.push(value); 490 | Ok(()) 491 | } 492 | _ => Err(RuntimeError::ArgumentTypes), 493 | } 494 | } 495 | 496 | fn get_super + fmt::Display + Copy>(&mut self, index: T) -> RunResult { 497 | let (this_val, super_val) = self.pop_pair()?; 498 | 499 | if let (Value::Class(super_class), Value::Instance(this)) = (super_val, this_val) { 500 | let method_name = self.read_string(index).to_string(); 501 | self.bind_method(super_class, this, method_name) 502 | } else { 503 | Err(RuntimeError::ArgumentTypes) 504 | } 505 | } 506 | 507 | fn define_method + fmt::Display + Copy>(&mut self, index: T) -> RunResult { 508 | let value = self.stack.pop().ok_or(RuntimeError::StackEmpty)?; 509 | 510 | match self.peek(0)? { 511 | Value::Class(c) => { 512 | let mut c = *c; 513 | let var_str = self.read_string(index); 514 | c.methods 515 | .insert(var_str.to_string().into_boxed_str(), value); 516 | Ok(()) 517 | } 518 | _ => Err(RuntimeError::ArgumentTypes), 519 | } 520 | } 521 | 522 | fn invoke>(&mut self, method: T, arg_count: u8) -> RunResult { 523 | if let Value::Instance(recv) = self.peek(arg_count.into())? { 524 | // check for fields, they shadow class methods 525 | if let Some(field) = recv.fields.get(method.as_ref()).cloned() { 526 | let l = self.stack.len(); 527 | self.stack[l - usize::from(arg_count) - 1] = field; 528 | self.call_value_from_stack(arg_count) 529 | } else { 530 | let class = recv.class; 531 | self.invoke_from_class(class, method.as_ref(), arg_count) 532 | } 533 | } else { 534 | Err(RuntimeError::ArgumentTypes) 535 | } 536 | } 537 | 538 | fn invoke_from_class(&mut self, class: Gc, method: &str, arg_count: u8) -> RunResult { 539 | if let Some(m) = class.methods.get(method) { 540 | self.call_value(m.clone(), arg_count) 541 | } else { 542 | Err(RuntimeError::UndefinedProperty(method.to_string())) 543 | } 544 | } 545 | 546 | fn call_value(&mut self, value: Value, arg_count: u8) -> RunResult { 547 | match value { 548 | Value::Closure(f, u) => self.call(f, u, arg_count), 549 | Value::Fun(f) => self.call(f, Box::new([]), arg_count), 550 | Value::Class(c) => { 551 | let cls = c; 552 | 553 | let val = Value::Instance(self.alloc(Instance::new(cls))); 554 | let l = self.stack.len(); 555 | self.stack[l - usize::from(arg_count) - 1] = val; 556 | 557 | if let Some(init) = c.methods.get("init") { 558 | match init { 559 | Value::Fun(f) => { 560 | let f = *f; 561 | self.call(f, Box::new([]), arg_count) 562 | } 563 | Value::Closure(f, u) => { 564 | let (f, u) = (*f, u.clone()); 565 | self.call(f, u, arg_count) 566 | } 567 | _ => Err(RuntimeError::ArgumentTypes), 568 | } 569 | } else if arg_count == 0 { 570 | Ok(()) 571 | } else { 572 | Err(RuntimeError::ArityMismatch(0, arg_count)) 573 | } 574 | } 575 | Value::BoundMethod { 576 | recv, 577 | fun, 578 | upvalues, 579 | .. 580 | } => { 581 | let l = self.stack.len(); 582 | self.stack[l - usize::from(arg_count) - 1] = Value::Instance(recv); 583 | self.call(fun, upvalues, arg_count) 584 | } 585 | Value::NativeFun(mut f) => { 586 | let from = self 587 | .stack 588 | .len() 589 | .checked_sub(arg_count.into()) 590 | .ok_or(RuntimeError::StackEmpty)?; 591 | 592 | let result = (f.as_mut())(&self.stack[from..]); 593 | self.pop_many(u16::from(arg_count) + 1)?; 594 | self.stack 595 | .push(result.map_err(RuntimeError::NativeFunError)?); 596 | 597 | Ok(()) 598 | } 599 | _ => Err(RuntimeError::NotCallable), 600 | } 601 | } 602 | 603 | pub(super) fn call_value_from_stack(&mut self, arg_count: u8) -> RunResult { 604 | let val = self.peek(arg_count.into())?.clone(); 605 | self.call_value(val, arg_count) 606 | } 607 | 608 | fn call( 609 | &mut self, 610 | callee: Gc, 611 | upvalues: Box<[Gc]>, 612 | arg_count: u8, 613 | ) -> RunResult { 614 | if arg_count == callee.arity { 615 | self.frames.push(CallFrame { 616 | func: callee, 617 | inst: 0, 618 | base: self 619 | .stack 620 | .len() 621 | .checked_sub(1 + usize::from(arg_count)) 622 | .ok_or(RuntimeError::StackEmpty)?, 623 | upvalues, 624 | }); 625 | Ok(()) 626 | } else { 627 | Err(RuntimeError::ArityMismatch(callee.arity, arg_count)) 628 | } 629 | } 630 | } 631 | -------------------------------------------------------------------------------- /src/compiler/mod.rs: -------------------------------------------------------------------------------- 1 | use std::{convert::TryInto, mem}; 2 | 3 | mod errors; 4 | mod scanner; 5 | mod token; 6 | mod wrapper; 7 | 8 | pub(crate) use { 9 | errors::{CompileError, CompileErrorType}, 10 | wrapper::Upvalue, 11 | }; 12 | 13 | use { 14 | super::{Fun, Op, Value}, 15 | scanner::{ScanError, Scanner}, 16 | token::{Token, TokenType}, 17 | wrapper::{ClassWrapper, FunType, FunWrapper, Local}, 18 | }; 19 | 20 | type MaybeToken<'a> = Option, ScanError>>; 21 | 22 | struct Parser<'a> { 23 | current: MaybeToken<'a>, 24 | previous: MaybeToken<'a>, 25 | line: usize, 26 | panic_mode: bool, 27 | } 28 | 29 | impl<'a> Parser<'a> { 30 | fn previous_type(&self) -> Option { 31 | if let Some(Ok(token)) = &self.previous { 32 | Some(token.r#type) 33 | } else { 34 | None 35 | } 36 | } 37 | 38 | fn current_type(&self) -> Option { 39 | if let Some(Ok(token)) = &self.current { 40 | Some(token.r#type) 41 | } else { 42 | None 43 | } 44 | } 45 | } 46 | 47 | #[derive(Clone, Copy, PartialEq, PartialOrd)] 48 | enum Precedence { 49 | None = 0, 50 | Assignment, 51 | Or, 52 | And, 53 | Equality, 54 | Comparison, 55 | Term, 56 | Factor, 57 | Unary, 58 | Call, 59 | Primary, 60 | } 61 | 62 | impl Precedence { 63 | fn next(self) -> Self { 64 | match self { 65 | Self::None => Self::Assignment, 66 | Self::Assignment => Self::Or, 67 | Self::Or => Self::And, 68 | Self::Equality => Self::Comparison, 69 | Self::Comparison => Self::Term, 70 | Self::Term => Self::Factor, 71 | Self::Factor => Self::Unary, 72 | Self::Unary => Self::Call, 73 | _ => Self::Primary, 74 | } 75 | } 76 | } 77 | 78 | macro_rules! short_or_long { 79 | ( $value: expr; $short: ident <> $long: ident $(, $extra: expr)? ) => {{ 80 | match $value.try_into() { 81 | Ok(v) => Op::$short(v $(, $extra)?), 82 | Err(_) => Op::$long($value as u16 $(, $extra)?), 83 | } 84 | }}; 85 | } 86 | 87 | macro_rules! emit { 88 | (const $c: ident : $short: ident / $long: ident, $val: expr $(, $extra: expr)? ) => {{ 89 | let index = $c.fun.inner.chunk.add_constant($val); 90 | $c.fun.inner.chunk.write( 91 | short_or_long!(index; $short <> $long $(, $extra)?), 92 | $c.parser.line 93 | ); 94 | }}; 95 | 96 | (jump $c: ident : $variant: ident) => {{ 97 | $c.fun.inner.chunk.write(Op::$variant(std::u16::MAX), $c.parser.line) 98 | }}; 99 | 100 | (loop $c: ident : $loop_start: expr) => {{ 101 | let loop_end = $c.fun.inner.chunk.code.len(); 102 | let difference = loop_end - $loop_start + 1; 103 | 104 | let difference = match difference.try_into() { 105 | Err(_) => { 106 | $c.error(CompileErrorType::JumpTooLarge(difference)); 107 | std::u16::MAX 108 | } 109 | Ok(d) => d, 110 | }; 111 | 112 | $c.fun.inner.chunk.write(Op::Loop(difference), $c.parser.line); 113 | }}; 114 | 115 | ( $c:ident, $( $op:expr ),* ) => {{ 116 | $( $c.fun.inner.chunk.write($op, $c.parser.line); )* 117 | }}; 118 | } 119 | 120 | pub(crate) struct Compiler<'compile> { 121 | scanner: Scanner<'compile>, 122 | parser: Parser<'compile>, 123 | errors: Vec, 124 | fun: Box>, 125 | class: Option>, 126 | alloc: Alloc<'compile>, 127 | } 128 | 129 | type Alloc<'compile> = &'compile mut dyn FnMut(Fun) -> Value; 130 | type ParseFn<'compile> = fn(&mut Compiler<'compile>, bool); 131 | type Rule<'compile> = ( 132 | Option>, 133 | Option>, 134 | Precedence, 135 | ); 136 | 137 | impl<'compile> Compiler<'compile> { 138 | pub fn compile( 139 | source: &'compile str, 140 | alloc: Alloc<'compile>, 141 | ) -> Result> { 142 | let mut compiler = Self { 143 | scanner: Scanner::new(source), 144 | parser: Parser { 145 | current: None, 146 | previous: None, 147 | line: 1, 148 | panic_mode: false, 149 | }, 150 | errors: Vec::new(), 151 | fun: Box::new(FunWrapper::new(&"script", FunType::Script)), 152 | class: None, 153 | alloc, 154 | }; 155 | 156 | compiler.advance(); 157 | 158 | while let Some(Ok(_)) = &compiler.parser.current { 159 | compiler.declaration(); 160 | } 161 | 162 | if compiler.scanner.next().is_some() { 163 | compiler.error_at_current(CompileErrorType::ExpectedEOF); 164 | } 165 | 166 | compiler.end_function(); 167 | 168 | if compiler.errors.is_empty() { 169 | Ok(compiler.fun.inner) 170 | } else { 171 | Err(compiler.errors) 172 | } 173 | } 174 | 175 | fn end_function(&mut self) { 176 | self.emit_return(); 177 | 178 | #[cfg(feature = "trace-compilation")] 179 | log::debug!("\n{}", self.fun.inner.chunk); 180 | } 181 | 182 | fn advance(&mut self) { 183 | self.parser.previous = self.parser.current.take(); 184 | self.parser.current = loop { 185 | match self.scanner.next() { 186 | // continue churning through the errors until you find `None` (EOF) or 187 | // `Some(Ok(...))` (a non-error token) 188 | Some(Err((_, e))) => self.error_at_current(e), 189 | Some(Ok(token)) => { 190 | self.parser.line = token.line; 191 | break Some(Ok(token)); 192 | } 193 | None => break None, 194 | } 195 | }; 196 | } 197 | 198 | fn consume(&mut self, r#type: TokenType, if_not: CompileErrorType) { 199 | if let Some(Ok(Token { r#type: t, .. })) = self.parser.current { 200 | if t == r#type { 201 | self.advance(); 202 | return; 203 | } 204 | } 205 | 206 | self.error_at_current(if_not); 207 | } 208 | 209 | fn error(&mut self, err: CompileErrorType) { 210 | self.error_at(self.parser.previous.clone(), err); 211 | } 212 | 213 | fn error_at_current(&mut self, err: CompileErrorType) { 214 | self.error_at(self.parser.current.clone(), err); 215 | } 216 | 217 | fn error_at(&mut self, token: MaybeToken<'compile>, err: CompileErrorType) { 218 | if !self.parser.panic_mode { 219 | self.parser.panic_mode = true; 220 | 221 | let new_err = CompileError { 222 | err, 223 | line: self.parser.line, 224 | text: token 225 | .map(Result::unwrap) 226 | .map(|Token { text, .. }| text.to_string()), 227 | }; 228 | 229 | log::error!("{}", new_err); 230 | self.errors.push(new_err); 231 | } 232 | } 233 | 234 | fn synchronize(&mut self) { 235 | self.parser.panic_mode = false; 236 | 237 | while let Some(maybe_token) = &self.parser.current { 238 | if let Some(TokenType::Semi) = self.parser.previous_type() { 239 | return; 240 | } 241 | 242 | if let Ok(token) = maybe_token { 243 | match token.r#type { 244 | TokenType::Class 245 | | TokenType::Fun 246 | | TokenType::Var 247 | | TokenType::For 248 | | TokenType::If 249 | | TokenType::While 250 | | TokenType::Print 251 | | TokenType::Return => return, 252 | _ => (), 253 | } 254 | } 255 | 256 | self.advance(); 257 | } 258 | } 259 | 260 | fn declaration(&mut self) { 261 | match self.parser.current_type() { 262 | Some(TokenType::Class) => { 263 | self.advance(); 264 | self.class_declaration(); 265 | } 266 | Some(TokenType::Fun) => { 267 | self.advance(); 268 | self.fun_declaration(); 269 | } 270 | Some(TokenType::Var) => { 271 | self.advance(); 272 | self.var_declaration(); 273 | } 274 | _ => self.statement(), 275 | } 276 | 277 | if self.parser.panic_mode { 278 | self.synchronize(); 279 | } 280 | } 281 | 282 | fn class_declaration(&mut self) { 283 | let name_idx = self.parse_variable(CompileErrorType::MissingClassName); 284 | 285 | // get class name for later 286 | let class_name = if let Some(Ok(Token { 287 | r#type: TokenType::Identifier, 288 | text, 289 | .. 290 | })) = self.parser.previous 291 | { 292 | text 293 | } else { 294 | unreachable!() 295 | }; 296 | 297 | emit!(self, short_or_long!(name_idx; Class <> ClassLong)); 298 | self.define_variable(name_idx); 299 | 300 | self.class = Some(Box::new(ClassWrapper { 301 | enclosing: self.class.take(), 302 | has_superclass: false, 303 | })); 304 | 305 | if let Some(TokenType::Less) = self.parser.current_type() { 306 | self.advance(); 307 | self.consume(TokenType::Identifier, CompileErrorType::MissingSuperclass); 308 | self.variable(false); 309 | 310 | if let Some(Ok(Token { text, .. })) = self.parser.previous { 311 | if text == class_name { 312 | self.error(CompileErrorType::InheritFromSelf); 313 | } 314 | } 315 | 316 | self.begin_scope(); 317 | self.add_local("super"); 318 | self.define_variable(0); 319 | 320 | self.named_variable(class_name, false); 321 | emit!(self, Op::Inherit); 322 | 323 | if let Some(ref mut c) = &mut self.class { 324 | c.has_superclass = true; 325 | } 326 | } 327 | 328 | self.named_variable(class_name, false); // load class object back onto stack 329 | 330 | // { 331 | self.consume( 332 | TokenType::LeftBrace, 333 | CompileErrorType::MissingLeftBrace("before class body"), 334 | ); 335 | 336 | loop { 337 | if let None | Some(TokenType::RightBrace) = self.parser.current_type() { 338 | break; 339 | } 340 | 341 | self.method(); 342 | } 343 | 344 | // } 345 | self.consume( 346 | TokenType::RightBrace, 347 | CompileErrorType::MissingRightBrace("after class body"), 348 | ); 349 | 350 | emit!(self, Op::Pop); 351 | 352 | if let Some(cls) = self.class.take() { 353 | if cls.has_superclass { 354 | self.end_scope(); 355 | } 356 | 357 | self.class = cls.enclosing; 358 | } else { 359 | unreachable!("Must be compiling a class at this point.") 360 | } 361 | } 362 | 363 | fn method(&mut self) { 364 | self.consume(TokenType::Identifier, CompileErrorType::MissingMethodName); 365 | let name_idx = if let Some(Ok(Token { text, .. })) = self.parser.previous { 366 | self.fun 367 | .inner 368 | .chunk 369 | .add_constant(Value::r#String(text.to_string().into_boxed_str())) 370 | } else { 371 | unreachable!() 372 | }; 373 | 374 | self.function( 375 | if let Some(Ok(Token { text: "init", .. })) = self.parser.previous { 376 | FunType::Initializer 377 | } else { 378 | FunType::Method 379 | }, 380 | ); 381 | 382 | emit!(self, short_or_long!(name_idx; Method <> MethodLong)); 383 | } 384 | 385 | fn fun_declaration(&mut self) { 386 | let name_idx = self.parse_variable(CompileErrorType::MissingFunName); 387 | self.mark_last_local_initialized(); 388 | self.function(FunType::Function); 389 | self.define_variable(name_idx); 390 | } 391 | 392 | fn function(&mut self, r#type: FunType) { 393 | // insert at head of list 394 | let fun_name = match self.parser.previous { 395 | Some(Ok(Token { 396 | r#type: TokenType::Identifier, 397 | text, 398 | .. 399 | })) => text.to_string().into_boxed_str(), 400 | _ => unreachable!("cannot compile a function without a name"), 401 | }; 402 | let old_fun_wrapper = 403 | mem::replace(&mut self.fun, Box::new(FunWrapper::new(&fun_name, r#type))); 404 | self.fun.enclosing = Some(old_fun_wrapper); 405 | 406 | self.begin_scope(); 407 | 408 | // ( 409 | self.consume( 410 | TokenType::LeftParen, 411 | CompileErrorType::MissingLeftParen("after function name"), 412 | ); 413 | 414 | // 415 | match self.parser.current_type() { 416 | Some(TokenType::RightParen) => (), // empty list 417 | _ => loop { 418 | if self.fun.inner.arity == std::u8::MAX { 419 | self.error_at_current(CompileErrorType::TooManyParams); 420 | } else { 421 | self.fun.inner.arity += 1; 422 | } 423 | 424 | let param_constant = self.parse_variable(CompileErrorType::MissingParamName); 425 | self.define_variable(param_constant); 426 | 427 | if let Some(TokenType::Comma) = self.parser.current_type() { 428 | self.advance(); 429 | } else { 430 | break; 431 | } 432 | }, 433 | } 434 | 435 | // ) 436 | self.consume( 437 | TokenType::RightParen, 438 | CompileErrorType::MissingRightParen("after parameters"), 439 | ); 440 | // { 441 | self.consume( 442 | TokenType::LeftBrace, 443 | CompileErrorType::MissingLeftBrace("before function body"), 444 | ); 445 | 446 | // 447 | self.block(); 448 | 449 | self.end_function(); 450 | 451 | // pop from head of list and insert into constant table 452 | if let Some(enclosing_fun) = self.fun.enclosing.take() { 453 | let this_fun = mem::replace(&mut self.fun, enclosing_fun); 454 | 455 | let fun_value = (self.alloc)(this_fun.inner); 456 | 457 | if this_fun.upvalues.is_empty() { 458 | emit!(const self: Constant / ConstantLong, fun_value); 459 | } else { 460 | let upvalues = this_fun.upvalues.into_boxed_slice(); 461 | emit!(const self: Closure / ClosureLong, fun_value, upvalues); 462 | } 463 | } else { 464 | // should be infallible, enforce with runtime assertion 465 | unreachable!(); 466 | } 467 | } 468 | 469 | fn parse_variable(&mut self, err: CompileErrorType) -> usize { 470 | self.consume(TokenType::Identifier, err); 471 | if let Some(Ok(Token { text, .. })) = self.parser.previous { 472 | self.declare_variable(text); 473 | 474 | // globals only 475 | if self.fun.scope_depth == 0 { 476 | self.fun 477 | .inner 478 | .chunk 479 | .add_constant(Value::r#String(text.to_string().into_boxed_str())) 480 | } else { 481 | 0 482 | } 483 | } else { 484 | unreachable!(); 485 | } 486 | } 487 | 488 | fn declare_variable(&mut self, name: &'compile str) { 489 | if self.fun.scope_depth == 0 { 490 | return; 491 | } 492 | 493 | // check for shadowing in current scope only 494 | if self 495 | .fun 496 | .locals 497 | .iter() 498 | .rev() 499 | .take_while(|l| l.depth >= self.fun.scope_depth) 500 | .any(|l| l.name == name) 501 | { 502 | self.error(CompileErrorType::DuplicateLocal(name.to_string())); 503 | } 504 | 505 | self.add_local(name); 506 | } 507 | 508 | fn add_local(&mut self, name: &'compile str) { 509 | self.fun.locals.push(Local { 510 | name, 511 | depth: self.fun.scope_depth, 512 | is_defined: false, 513 | is_captured: false, 514 | }); 515 | } 516 | 517 | fn var_declaration(&mut self) { 518 | let var_const = self.parse_variable(CompileErrorType::MissingVarName); 519 | 520 | if let Some(TokenType::Equal) = self.parser.current_type() { 521 | self.advance(); 522 | self.expression(); 523 | } else { 524 | emit!(self, Op::Nil); 525 | } 526 | 527 | self.consume(TokenType::Semi, CompileErrorType::MissingSemi); 528 | 529 | self.define_variable(var_const); 530 | } 531 | 532 | fn define_variable(&mut self, var_const_index: usize) { 533 | // globals only 534 | if self.fun.scope_depth == 0 { 535 | emit!( 536 | self, 537 | short_or_long!(var_const_index; DefineGlobal <> DefineGlobalLong) 538 | ); 539 | } else { 540 | self.mark_last_local_initialized(); 541 | } 542 | } 543 | 544 | fn mark_last_local_initialized(&mut self) { 545 | if self.fun.scope_depth > 0 { 546 | if let Some(last) = self.fun.locals.last_mut() { 547 | last.is_defined = true; 548 | } 549 | } 550 | } 551 | 552 | fn statement(&mut self) { 553 | match self.parser.current_type() { 554 | Some(TokenType::Print) => self.print_statement(), 555 | Some(TokenType::For) => self.for_statement(), 556 | Some(TokenType::If) => self.if_statement(), 557 | Some(TokenType::Return) => self.return_statement(), 558 | Some(TokenType::While) => self.while_statement(), 559 | Some(TokenType::LeftBrace) => { 560 | self.advance(); 561 | self.begin_scope(); 562 | self.block(); 563 | self.end_scope(); 564 | } 565 | _ => self.expression_statement(), 566 | } 567 | } 568 | 569 | fn begin_scope(&mut self) { 570 | self.fun.scope_depth += 1; 571 | } 572 | 573 | fn end_scope(&mut self) { 574 | self.fun.scope_depth -= 1; 575 | 576 | while let Some(local) = self.fun.locals.pop() { 577 | if local.depth <= self.fun.scope_depth { 578 | self.fun.locals.push(local); 579 | break; 580 | } 581 | 582 | if local.is_captured { 583 | emit!(self, Op::CloseUpvalue); 584 | } else { 585 | emit!(self, Op::Pop); 586 | } 587 | } 588 | } 589 | 590 | fn block(&mut self) { 591 | while let Some(Ok(token)) = &self.parser.current { 592 | if let TokenType::RightBrace = token.r#type { 593 | break; 594 | } 595 | 596 | self.declaration(); 597 | } 598 | 599 | self.consume( 600 | TokenType::RightBrace, 601 | CompileErrorType::MissingRightBrace("after block"), 602 | ); 603 | } 604 | 605 | fn print_statement(&mut self) { 606 | self.advance(); 607 | self.expression(); 608 | self.consume(TokenType::Semi, CompileErrorType::MissingSemi); 609 | emit!(self, Op::Print); 610 | } 611 | 612 | fn if_statement(&mut self) { 613 | // IF 614 | self.advance(); 615 | // ( 616 | self.consume( 617 | TokenType::LeftParen, 618 | CompileErrorType::MissingLeftParen("after 'if'"), 619 | ); 620 | // 621 | self.expression(); 622 | // ) 623 | self.consume( 624 | TokenType::RightParen, 625 | CompileErrorType::MissingRightParen("after condition"), 626 | ); 627 | 628 | let then_jump = emit!(jump self: JumpIfFalse); 629 | emit!(self, Op::Pop); 630 | // 631 | self.statement(); 632 | 633 | let else_jump = emit!(jump self: Jump); 634 | if let Err(e) = self.fun.inner.chunk.patch_jump(then_jump) { 635 | self.error(e); 636 | } 637 | emit!(self, Op::Pop); 638 | 639 | // ELSE 640 | if let Some(TokenType::Else) = self.parser.current_type() { 641 | self.advance(); 642 | // 643 | self.statement(); 644 | } 645 | 646 | if let Err(e) = self.fun.inner.chunk.patch_jump(else_jump) { 647 | self.error(e); 648 | } 649 | } 650 | 651 | fn return_statement(&mut self) { 652 | if let FunType::Script = self.fun.r#type { 653 | self.error(CompileErrorType::TopLevelReturn); 654 | } 655 | 656 | // RETURN 657 | self.advance(); 658 | 659 | if let Some(TokenType::Semi) = self.parser.current_type() { 660 | // no return value 661 | self.advance(); 662 | self.emit_return(); 663 | } else { 664 | if let FunType::Initializer = self.fun.r#type { 665 | self.error(CompileErrorType::ReturnFromInit); 666 | } 667 | 668 | self.expression(); 669 | self.consume(TokenType::Semi, CompileErrorType::MissingSemi); 670 | emit!(self, Op::Return); 671 | } 672 | } 673 | 674 | fn emit_return(&mut self) { 675 | if let FunType::Initializer = self.fun.r#type { 676 | emit!(self, Op::GetLocal(0)); 677 | } else { 678 | emit!(self, Op::Nil); 679 | } 680 | 681 | emit!(self, Op::Return); 682 | } 683 | 684 | fn while_statement(&mut self) { 685 | let loop_start = self.fun.inner.chunk.code.len(); 686 | 687 | // WHILE 688 | self.advance(); 689 | // ( 690 | self.consume( 691 | TokenType::LeftParen, 692 | CompileErrorType::MissingLeftParen("after 'while'"), 693 | ); 694 | // 695 | self.expression(); 696 | // ) 697 | self.consume( 698 | TokenType::RightParen, 699 | CompileErrorType::MissingRightParen("after condition"), 700 | ); 701 | 702 | let exit_jmp = emit!(jump self: JumpIfFalse); 703 | 704 | emit!(self, Op::Pop); 705 | self.statement(); 706 | 707 | emit!(loop self: loop_start); 708 | 709 | if let Err(e) = self.fun.inner.chunk.patch_jump(exit_jmp) { 710 | self.error(e); 711 | } 712 | 713 | emit!(self, Op::Pop); 714 | } 715 | 716 | fn for_statement(&mut self) { 717 | self.begin_scope(); 718 | 719 | // FOR 720 | self.advance(); 721 | // ( 722 | self.consume( 723 | TokenType::LeftParen, 724 | CompileErrorType::MissingLeftParen("after 'for'"), 725 | ); 726 | // ; 727 | match self.parser.current_type() { 728 | Some(TokenType::Semi) => (), // no initializer 729 | Some(TokenType::Var) => { 730 | self.advance(); 731 | self.var_declaration(); 732 | } 733 | _ => self.expression_statement(), 734 | } 735 | 736 | let mut loop_start = self.fun.inner.chunk.code.len(); 737 | let exit_jmp; 738 | 739 | // ; 740 | if let Some(TokenType::Semi) = self.parser.current_type() { 741 | self.advance(); 742 | exit_jmp = None; 743 | } else { 744 | self.expression(); 745 | self.consume(TokenType::Semi, CompileErrorType::MissingSemi); 746 | 747 | exit_jmp = Some(emit!(jump self: JumpIfFalse)); 748 | emit!(self, Op::Pop); 749 | } 750 | 751 | // ) 752 | if let Some(TokenType::RightParen) = self.parser.current_type() { 753 | self.advance(); 754 | } else { 755 | let body_jmp = emit!(jump self: Jump); 756 | 757 | let increment_start = self.fun.inner.chunk.code.len(); 758 | self.expression(); 759 | emit!(self, Op::Pop); 760 | self.consume( 761 | TokenType::RightParen, 762 | CompileErrorType::MissingRightParen("after 'for' clauses"), 763 | ); 764 | 765 | emit!(loop self: loop_start); 766 | loop_start = increment_start; 767 | if let Err(e) = self.fun.inner.chunk.patch_jump(body_jmp) { 768 | self.error(e); 769 | } 770 | } 771 | 772 | // 773 | self.statement(); 774 | 775 | emit!(loop self: loop_start); 776 | 777 | if let Some(jmp) = exit_jmp { 778 | if let Err(e) = self.fun.inner.chunk.patch_jump(jmp) { 779 | self.error(e); 780 | } 781 | } 782 | 783 | self.end_scope(); 784 | } 785 | 786 | fn expression_statement(&mut self) { 787 | self.expression(); 788 | self.consume(TokenType::Semi, CompileErrorType::MissingSemi); 789 | emit!(self, Op::Pop); 790 | } 791 | 792 | fn parse_precedence(&mut self, precedence: Precedence) { 793 | let can_assign = (precedence as isize) <= (Precedence::Assignment as isize); 794 | self.advance(); 795 | if let Some(operator) = self.parser.previous_type() { 796 | if let (Some(prefix_func), _, _) = Self::get_rule(operator) { 797 | prefix_func(self, can_assign); 798 | } else { 799 | self.error(CompileErrorType::MissingPrefixExpr); 800 | return; 801 | } 802 | } 803 | 804 | while let Some(operator) = self.parser.current_type() { 805 | let (_, maybe_infix, rule_precedence) = Self::get_rule(operator); 806 | if precedence > rule_precedence { 807 | break; 808 | } 809 | 810 | self.advance(); 811 | 812 | if let Some(infix_func) = maybe_infix { 813 | infix_func(self, can_assign); 814 | } 815 | } 816 | } 817 | 818 | fn expression(&mut self) { 819 | self.parse_precedence(Precedence::Assignment); 820 | } 821 | 822 | fn grouping(&mut self, _: bool) { 823 | self.expression(); 824 | self.consume( 825 | TokenType::RightParen, 826 | CompileErrorType::MissingRightParen("after expression"), 827 | ); 828 | } 829 | 830 | fn number(&mut self, _: bool) { 831 | if let Some(Ok(token)) = &self.parser.previous { 832 | if let Ok(v) = token.text.parse() { 833 | emit!(const self: Constant / ConstantLong, Value::Number(v)); 834 | } 835 | } 836 | } 837 | 838 | fn unary(&mut self, _: bool) { 839 | if let Some(operator) = self.parser.previous_type() { 840 | self.parse_precedence(Precedence::Unary); 841 | 842 | match operator { 843 | TokenType::Bang => emit!(self, Op::Not), 844 | TokenType::Minus => emit!(self, Op::Negate), 845 | _ => unreachable!(), 846 | } 847 | } 848 | } 849 | 850 | fn binary(&mut self, _: bool) { 851 | if let Some(operator) = self.parser.previous_type() { 852 | // right operand 853 | let rule = Self::get_rule(operator); 854 | self.parse_precedence(rule.2.next()); 855 | match operator { 856 | TokenType::BangEqual => emit!(self, Op::Equal, Op::Not), 857 | TokenType::DoubleEqual => emit!(self, Op::Equal), 858 | TokenType::Greater => emit!(self, Op::Greater), 859 | TokenType::GreaterEqual => emit!(self, Op::Less, Op::Not), 860 | TokenType::Less => emit!(self, Op::Less), 861 | TokenType::LessEqual => emit!(self, Op::Greater, Op::Not), 862 | TokenType::Plus => emit!(self, Op::Add), 863 | TokenType::Minus => emit!(self, Op::Subtract), 864 | TokenType::Star => emit!(self, Op::Multiply), 865 | TokenType::Slash => emit!(self, Op::Divide), 866 | _ => unreachable!(), 867 | } 868 | } 869 | } 870 | 871 | fn literal(&mut self, _: bool) { 872 | match self.parser.previous_type() { 873 | Some(TokenType::False) => emit!(self, Op::False), 874 | Some(TokenType::Nil) => emit!(self, Op::Nil), 875 | Some(TokenType::True) => emit!(self, Op::True), 876 | _ => unreachable!(), 877 | } 878 | } 879 | 880 | fn string(&mut self, _: bool) { 881 | let text_without_quotes = if let Some(Ok(t)) = &self.parser.previous { 882 | t.text[1..t.text.len() - 1].to_string().into_boxed_str() 883 | } else { 884 | return; 885 | }; 886 | 887 | emit!(const self: Constant / ConstantLong, Value::r#String(text_without_quotes)); 888 | } 889 | 890 | fn variable(&mut self, can_assign: bool) { 891 | let token_text = if let Some(Ok(Token { text, .. })) = &self.parser.previous { 892 | <&str>::clone(text) 893 | } else { 894 | unreachable!(); 895 | }; 896 | 897 | self.named_variable(token_text, can_assign); 898 | } 899 | 900 | fn named_variable(&mut self, name: &str, can_assign: bool) { 901 | let resolved_index = self.resolve_local(name); 902 | let value_maker = || Value::r#String(name.to_string().into_boxed_str()); 903 | 904 | // "Set" expression 905 | if can_assign && self.parser.current_type() == Some(TokenType::Equal) { 906 | self.advance(); 907 | self.expression(); 908 | 909 | if let Some(idx) = resolved_index { 910 | emit!(self, short_or_long!(idx; SetLocal <> SetLocalLong)); 911 | } else if let Some(idx) = self.fun.resolve_upvalue(name) { 912 | emit!(self, short_or_long!(idx; SetUpvalue <> SetUpvalueLong)); 913 | } else { 914 | emit!(const self: SetGlobal / SetGlobalLong, value_maker()); 915 | } 916 | // "Get" expression 917 | } else if let Some(idx) = resolved_index { 918 | emit!(self, short_or_long!(idx; GetLocal <> GetLocalLong)); 919 | } else if let Some(idx) = self.fun.resolve_upvalue(name) { 920 | emit!(self, short_or_long!(idx; GetUpvalue <> GetUpvalueLong)); 921 | } else { 922 | emit!(const self: GetGlobal / GetGlobalLong, value_maker()); 923 | } 924 | } 925 | 926 | fn resolve_local(&mut self, query: &str) -> Option { 927 | if let Some((index, is_defined)) = self.fun.resolve_local(query) { 928 | if !is_defined { 929 | self.error(CompileErrorType::ReadBeforeDefined(query.to_string())); 930 | } 931 | 932 | Some(index) 933 | } else { 934 | None 935 | } 936 | } 937 | 938 | fn and(&mut self, _: bool) { 939 | let end_jmp = emit!(jump self: JumpIfFalse); 940 | 941 | emit!(self, Op::Pop); 942 | self.parse_precedence(Precedence::And); 943 | 944 | if let Err(e) = self.fun.inner.chunk.patch_jump(end_jmp) { 945 | self.error(e); 946 | } 947 | } 948 | 949 | fn or(&mut self, _: bool) { 950 | let else_jmp = emit!(jump self: JumpIfFalse); 951 | let end_jmp = emit!(jump self: Jump); 952 | 953 | if let Err(e) = self.fun.inner.chunk.patch_jump(else_jmp) { 954 | self.error(e); 955 | } 956 | emit!(self, Op::Pop); 957 | 958 | self.parse_precedence(Precedence::Or); 959 | if let Err(e) = self.fun.inner.chunk.patch_jump(end_jmp) { 960 | self.error(e); 961 | } 962 | } 963 | 964 | fn call(&mut self, _: bool) { 965 | let arg_count = self.argument_list(); 966 | emit!(self, Op::Call(arg_count)); 967 | } 968 | 969 | fn argument_list(&mut self) -> u8 { 970 | let mut arg_count = 0; 971 | 972 | match self.parser.current_type() { 973 | Some(TokenType::RightParen) => (), // empty list 974 | _ => loop { 975 | self.expression(); 976 | 977 | if arg_count == std::u8::MAX { 978 | self.error(CompileErrorType::TooManyParams); 979 | } else { 980 | arg_count += 1; 981 | } 982 | 983 | if let Some(TokenType::Comma) = self.parser.current_type() { 984 | self.advance(); 985 | } else { 986 | break; 987 | } 988 | }, 989 | } 990 | 991 | // ) 992 | self.consume( 993 | TokenType::RightParen, 994 | CompileErrorType::MissingRightParen("after arguments"), 995 | ); 996 | arg_count 997 | } 998 | 999 | fn dot(&mut self, can_assign: bool) { 1000 | self.consume(TokenType::Identifier, CompileErrorType::MissingPropertyName); 1001 | let property_idx = if let Some(Ok(Token { text, .. })) = self.parser.previous { 1002 | self.fun 1003 | .inner 1004 | .chunk 1005 | .add_constant(Value::r#String(text.to_string().into_boxed_str())) 1006 | } else { 1007 | unreachable!() 1008 | }; 1009 | 1010 | match self.parser.current_type() { 1011 | Some(TokenType::Equal) if can_assign => { 1012 | self.advance(); 1013 | self.expression(); 1014 | emit!( 1015 | self, 1016 | short_or_long!(property_idx; SetProperty <> SetPropertyLong) 1017 | ); 1018 | } 1019 | 1020 | Some(TokenType::LeftParen) => { 1021 | self.advance(); 1022 | let arg_count = self.argument_list(); 1023 | emit!( 1024 | self, 1025 | short_or_long!(property_idx; Invoke <> InvokeLong, arg_count) 1026 | ); 1027 | } 1028 | 1029 | _ => { 1030 | emit!( 1031 | self, 1032 | short_or_long!(property_idx; GetProperty <> GetPropertyLong) 1033 | ); 1034 | } 1035 | } 1036 | } 1037 | 1038 | fn this(&mut self, _: bool) { 1039 | if self.class.is_none() { 1040 | self.error(CompileErrorType::InvalidThis); 1041 | } else { 1042 | self.variable(false); 1043 | } 1044 | } 1045 | 1046 | fn super_call(&mut self, _: bool) { 1047 | if let Some(cls) = &self.class { 1048 | if !cls.has_superclass { 1049 | self.error(CompileErrorType::InvalidSuper( 1050 | "in a class with no superclass", 1051 | )); 1052 | } 1053 | } else { 1054 | self.error(CompileErrorType::InvalidSuper("outside of a class")); 1055 | } 1056 | 1057 | self.consume( 1058 | TokenType::Dot, 1059 | CompileErrorType::MissingDot("after 'super'"), 1060 | ); 1061 | 1062 | self.consume(TokenType::Identifier, CompileErrorType::MissingMethodName); 1063 | let property_idx = if let Some(Ok(Token { text, .. })) = self.parser.previous { 1064 | self.fun 1065 | .inner 1066 | .chunk 1067 | .add_constant(Value::r#String(text.to_string().into_boxed_str())) 1068 | } else { 1069 | unreachable!() 1070 | }; 1071 | 1072 | self.named_variable("this", false); 1073 | 1074 | if let Some(TokenType::LeftParen) = self.parser.current_type() { 1075 | self.advance(); 1076 | let arg_count = self.argument_list(); 1077 | self.named_variable("super", false); 1078 | emit!( 1079 | self, 1080 | short_or_long!(property_idx; SuperInvoke <> SuperInvokeLong, arg_count) 1081 | ); 1082 | } else { 1083 | self.named_variable("super", false); 1084 | emit!(self, short_or_long!(property_idx; GetSuper <> GetSuperLong)); 1085 | } 1086 | } 1087 | 1088 | fn get_rule(sigil: TokenType) -> Rule<'compile> { 1089 | match sigil { 1090 | TokenType::LeftParen => (Some(Self::grouping), Some(Self::call), Precedence::Call), 1091 | TokenType::Minus => (Some(Self::unary), Some(Self::binary), Precedence::Term), 1092 | TokenType::Plus => (None, Some(Self::binary), Precedence::Term), 1093 | TokenType::Slash | TokenType::Star => (None, Some(Self::binary), Precedence::Factor), 1094 | TokenType::Bang => (Some(Self::unary), None, Precedence::None), 1095 | TokenType::BangEqual | TokenType::DoubleEqual => { 1096 | (None, Some(Self::binary), Precedence::Equality) 1097 | } 1098 | TokenType::Greater 1099 | | TokenType::GreaterEqual 1100 | | TokenType::Less 1101 | | TokenType::LessEqual => (None, Some(Self::binary), Precedence::Comparison), 1102 | TokenType::Identifier => (Some(Self::variable), None, Precedence::None), 1103 | TokenType::r#String => (Some(Self::string), None, Precedence::None), 1104 | TokenType::Number => (Some(Self::number), None, Precedence::None), 1105 | TokenType::And => (None, Some(Self::and), Precedence::And), 1106 | TokenType::Or => (None, Some(Self::or), Precedence::Or), 1107 | TokenType::False | TokenType::Nil | TokenType::True => { 1108 | (Some(Self::literal), None, Precedence::None) 1109 | } 1110 | TokenType::Dot => (None, Some(Self::dot), Precedence::Call), 1111 | TokenType::This => (Some(Self::this), None, Precedence::None), 1112 | TokenType::Super => (Some(Self::super_call), None, Precedence::None), 1113 | _ => (None, None, Precedence::None), 1114 | } 1115 | } 1116 | } 1117 | --------------------------------------------------------------------------------