diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 4b8c354..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/mfussenegger/dapconfig-schema/master/dapconfig-schema.json", - "version": "0.2.0", - "configurations": [ - { - "type": "lldb", - "request": "launch", - "name": "Run Lox Interpreter", - "cargo": { - "args": [ - "build", - "--package", - "rox", - "--message-format", - "json" - ] - }, - "program": "${workspaceFolder}/target/debug/rox", - "args": [ - "run", - "-s", - "./lox/simple_for.lox" - ], - "env": { - "RUST_LOG": "debug" - }, - "cwd": "${workspaceFolder}" - } - ] -} diff --git a/bf/fibonacci.bf b/bf/fibonacci.bf deleted file mode 100644 index ac429e9..0000000 --- a/bf/fibonacci.bf +++ /dev/null @@ -1,6 +0,0 @@ ->++++++++++>+>+[ - [+++++[>++++++++<-]>.<++++++[>--------<-]+<<<]>.>>[ - [-]<[>+<-]>>[<<+>+>-]<[>+<-[>+<-[>+<-[>+<-[>+<-[>+<- - [>+<-[>+<-[>+<-[>[-]>+>+<<<-[>+<-]]]]]]]]]]]+>>> - ]<<< -] diff --git a/bf/helloworld.bf b/bf/helloworld.bf deleted file mode 100644 index 8fa0f72..0000000 --- a/bf/helloworld.bf +++ /dev/null @@ -1 +0,0 @@ -++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++. diff --git a/bf/simple_loop.bf b/bf/simple_loop.bf deleted file mode 100644 index ffa6054..0000000 --- a/bf/simple_loop.bf +++ /dev/null @@ -1 +0,0 @@ -++++++[>++++++++<-]>. // prints '0' diff --git a/lox/bf.lox b/lox/bf.lox index bb9281b..cb6deab 100644 --- a/lox/bf.lox +++ b/lox/bf.lox @@ -71,7 +71,7 @@ class Interpreter { } - if (instruction == "[" and tape.current.data = 0) { + if (instruction == "[" and tape.current.data < 0.0001) { instruction = read(this.bfpath); var bracketCount = 1; while (bracketCount > 0 and instruction != nil) { @@ -81,7 +81,7 @@ class Interpreter { } } - if (instruction == "]" and tape.current.data != 0) { + if (instruction == "]" and tape.current.data >= 0.0001) { instruction = read(this.bfpath, true); var bracketCount = 1; while (bracketCount > 0 and instruction != nil) { diff --git a/lox/fibonacci.lox b/lox/fibonacci.lox index 117d457..630e3b2 100644 --- a/lox/fibonacci.lox +++ b/lox/fibonacci.lox @@ -1,6 +1,7 @@ var a = 0; var temp; +var b = 1; for(var b = 1; a < 1000; b = temp + b) { print a; temp = a; diff --git a/lox/nested_while.lox b/lox/nested_while.lox deleted file mode 100644 index 31a7e2a..0000000 --- a/lox/nested_while.lox +++ /dev/null @@ -1,14 +0,0 @@ -{ - var i = 0; - var j = 0; - while (i < 10) { - print i; - - while (j < 100) { - print j; - j = j + 1; - } - - i = i + 1; - } -} diff --git a/lox/scope.lox b/lox/scope.lox deleted file mode 100644 index 99293e7..0000000 --- a/lox/scope.lox +++ /dev/null @@ -1,8 +0,0 @@ -var a = "outer"; - -{ - var a = "inner"; - print a; // expect: inner -} - -print a; diff --git a/lox/simple_for.lox b/lox/simple_for.lox deleted file mode 100644 index 1c15ec5..0000000 --- a/lox/simple_for.lox +++ /dev/null @@ -1,3 +0,0 @@ -for (var i = 0; i < 10; i = i + 1) { - print i; -} diff --git a/lox/simple_while.lox b/lox/simple_while.lox deleted file mode 100644 index 2acbf36..0000000 --- a/lox/simple_while.lox +++ /dev/null @@ -1,5 +0,0 @@ -var i = 0; -while (i < 10) { - print i; - i = i + 1; -} diff --git a/src/environment.rs b/src/environment.rs index 8966ee8..edfd980 100644 --- a/src/environment.rs +++ b/src/environment.rs @@ -55,24 +55,15 @@ impl Environment { } } - /// Assign a value to a variable in an environment at a specific lexical distance. + /// Assign a value to a variable at a specific lexical distance (number of scopes away). + /// Used by the resolver to handle closures correctly. pub fn assign_at( - env: Rc>, + &mut self, distance: usize, token: &Token, value: Value, ) -> Result<(), EnvironmentError> { - let mut current_env = env; - for _ in 0..distance { - let enclosing = current_env - .borrow() - .enclosing - .clone() - .ok_or(EnvironmentError::InvalidDistance)?; - current_env = enclosing; - } - current_env - .borrow_mut() + self.ancestor(distance)? .values .insert(token.lexeme.clone(), value); Ok(()) @@ -94,22 +85,9 @@ impl Environment { } /// Get a variable's value from an environment at a specific lexical distance. - pub fn get_at( - env: Rc>, - distance: usize, - token: &Token, - ) -> Result { - let mut current_env_rc = env; - for _ in 0..distance { - let enclosing = current_env_rc - .borrow() - .enclosing - .clone() - .ok_or(EnvironmentError::InvalidDistance)?; - current_env_rc = enclosing; - } - let borrowed_env = current_env_rc.borrow(); - if let Some(v) = borrowed_env.values.get(token.lexeme.as_str()) { + /// Used by the resolver to handle closures correctly. + pub fn get_at(&self, distance: usize, token: &Token) -> Result { + if let Some(v) = self.ancestor(distance)?.values.get(token.lexeme.as_str()) { Ok(v.clone()) } else { Err(EnvironmentError::UndefinedVariable( @@ -118,4 +96,19 @@ impl Environment { )) } } + + /// Find an environment at a specific lexical distance from the current one. + fn ancestor(&self, distance: usize) -> Result { + let mut environment = self.clone(); + + for _ in 0..distance { + let enclosing = match &environment.enclosing { + Some(enc) => enc.borrow().clone(), + None => return Err(EnvironmentError::InvalidDistance), + }; + environment = enclosing; + } + + Ok(environment) + } } diff --git a/src/interpreter.rs b/src/interpreter.rs index 7adfef3..0388687 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -52,9 +52,9 @@ pub struct Interpreter { /// Global environment containing built-in functions and top-level variables pub globals: Rc>, /// Current environment for variable lookups and assignments - pub environment: Rc>, + environment: Rc>, /// Map of expressions to their lexical distance for variable resolution - pub locals: HashMap, + locals: HashMap, } /// Default configuration for the interpreter, with builtin native functions. @@ -242,7 +242,7 @@ impl Interpreter { right, } => self.binary(left, operator, right), Expression::Variable { name } => self.var_expression(name, expression), - Expression::Assign { .. } => self.assign(expression), + Expression::Assign { name, value } => self.assign(name, value), Expression::Logical { left, operator, @@ -323,9 +323,8 @@ impl Interpreter { method: &Token, ) -> Result { if let Some(distance) = self.locals.get(expression) { - let superclass = Environment::get_at(self.environment.clone(), *distance, keyword)?; - let object = Environment::get_at( - self.environment.clone(), + let superclass = self.environment.borrow().get_at(*distance, keyword)?; + let object = self.environment.borrow().get_at( *distance - 1, &Token { token_type: TokenType::This, @@ -450,27 +449,27 @@ impl Interpreter { } /// Assign the value of an expression to a variable. - fn assign(&mut self, assign_expr: &Expression) -> Result { - if let Expression::Assign { name, value } = assign_expr { - let evaluated_value = self.evaluate(value)?; + fn assign(&mut self, name: &Token, expression: &Expression) -> Result { + let value = self.evaluate(expression)?; - if let Some(distance) = self.locals.get(assign_expr) { - Environment::assign_at( - self.environment.clone(), - *distance, - name, - evaluated_value.clone(), - )?; - } else { - self.globals - .borrow_mut() - .assign(name, evaluated_value.clone())?; - } - Ok(evaluated_value) + self.environment + .borrow_mut() + .assign(name, value.clone()) + .map_err(InterpreterError::UndefinedVariable)?; + + if let Some(distance) = self.locals.get(expression) { + self.environment + .borrow_mut() + .assign_at(*distance, name, value.clone()) + .map_err(InterpreterError::UndefinedVariable)?; } else { - // This should ideally not be reached if called correctly from `evaluate` - unreachable!("Interpreter::assign called with a non-Assign expression"); + self.globals + .borrow_mut() + .assign(name, value.clone()) + .map_err(InterpreterError::UndefinedVariable)?; } + + Ok(value) } /// Convert the literal value into a Value. @@ -543,13 +542,15 @@ impl Interpreter { expression: &Expression, ) -> Result { if let Some(distance) = self.locals.get(expression) { - Ok(Environment::get_at( - self.environment.clone(), - *distance, - name, - )?) + self.environment + .borrow() + .get_at(*distance, name) + .map_err(InterpreterError::UndefinedVariable) } else { - Ok(self.globals.borrow().get(name)?) + self.globals + .borrow() + .get(name) + .map_err(InterpreterError::UndefinedVariable) } } diff --git a/src/native_functions.rs b/src/native_functions.rs index 2e61e13..c02c6ca 100644 --- a/src/native_functions.rs +++ b/src/native_functions.rs @@ -3,7 +3,7 @@ use ordered_float::OrderedFloat; use std::{ collections::HashMap, fs::File, - io::{stdin, stdout, BufReader, ErrorKind, Read, Seek, SeekFrom, Write}, + io::{BufReader, ErrorKind, Read}, rc::Rc, sync::RwLock, time::{SystemTime, UNIX_EPOCH}, @@ -53,7 +53,7 @@ impl Callable for ReadFile { } fn arity(&self) -> usize { - 2 + 1 } fn call( @@ -61,24 +61,18 @@ impl Callable for ReadFile { _interpreter: &mut Interpreter, args: Vec, ) -> Result { - let file_path = args.first().ok_or(CallingError::CallFailed( - "first arg must be file path".into(), - ))?; + if args.len() != self.arity() { + return Err(CallingError::ArgumentMismatch(self.arity(), args.len())); + } + + let file_path = args + .first() + .ok_or(CallingError::CallFailed("arg not readable".into()))?; if let Value::String(file_path) = file_path { self.ensure_file_handle(file_path)?; let mut file_handles = FILE_HANDLES.write().unwrap(); let reader = file_handles.get_mut(file_path).unwrap(); let mut buffer = [0; 1]; - - let backwards = args.get(1).unwrap_or(&Value::Boolean(false)); - if let Value::Boolean(backwards) = backwards { - if *backwards { - reader - .seek(SeekFrom::Current(-2)) - .map_err(|e| CallingError::CallFailed(e.to_string()))?; - } - } - let character = match reader.read_exact(&mut buffer) { Ok(_) => { let character = buffer[0] as char; @@ -149,85 +143,9 @@ impl Callable for Out { } } -struct AsciiOut; -impl Callable for AsciiOut { - fn name(&self) -> String { - "asciiOut".into() - } - - fn arity(&self) -> usize { - 1 - } - - fn call( - &self, - _interpreter: &mut Interpreter, - args: Vec, - ) -> Result { - if args.len() != self.arity() { - return Err(CallingError::ArgumentMismatch(self.arity(), args.len())); - } - - let value = args - .first() - .ok_or(CallingError::CallFailed("arg not readable".into()))?; - if let Value::Number(value) = value { - let ascii = value.0 as u8 as char; - print!("{ascii}"); - - Ok(Value::Nil) - } else { - Err(CallingError::CallFailed( - "file_path arg must be a number".into(), - )) - } - } -} - -struct PromptAscii; -impl Callable for PromptAscii { - fn name(&self) -> String { - "prompt_ascii".into() - } - - fn arity(&self) -> usize { - 1 - } - - fn call( - &self, - _interpreter: &mut Interpreter, - args: Vec, - ) -> Result { - if args.len() != self.arity() { - return Err(CallingError::ArgumentMismatch(self.arity(), args.len())); - } - - let prompt = args - .first() - .ok_or(CallingError::CallFailed("arg not readable".into()))?; - if let Value::String(prompt) = prompt { - print!("{prompt} "); - stdout().flush(); - - let mut buffer = [0; 1]; - stdin().read_exact(&mut buffer); - - todo!() - // Ok(Value::Number(buffer[0] as char)) - } else { - Err(CallingError::CallFailed("prompt must be a string".into())) - } - } -} - /// Return all native functions available to the Lox interpreter pub fn all() -> Vec<(String, Value)> { vec![ - ( - "asciiOut".into(), - Value::Callable((Rc::new(AsciiOut {}), CallableType::Function)), - ), ( "clock".into(), Value::Callable((Rc::new(Clock {}), CallableType::Function)), @@ -240,9 +158,5 @@ pub fn all() -> Vec<(String, Value)> { "read".into(), Value::Callable((Rc::new(ReadFile {}), CallableType::Function)), ), - ( - "prompt".into(), - Value::Callable((Rc::new(PromptAscii {}), CallableType::Function)), - ), ] } diff --git a/src/resolver.rs b/src/resolver.rs index 5c39cc9..5e97ac6 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -71,7 +71,7 @@ impl<'a> Resolver<'a> { Statement::Block(statements) => self.resolve_block(statements), Statement::Print(expression) => self.resolve_expression(expression), Statement::Expression(expression) => self.resolve_expression(expression), - Statement::Var { name, initializer } => self.resolve_var(name, initializer.as_ref().as_ref()), + Statement::Var { name, initializer } => self.resolve_var(name, *initializer.clone()), Statement::If { condition, then_branch, @@ -262,12 +262,12 @@ impl<'a> Resolver<'a> { fn resolve_var( &mut self, name: &Token, - initializer: Option<&Expression>, + initializer: Option, ) -> Result<(), ResolverError> { self.declare(name)?; - if let Some(init_expr_ref) = initializer { - self.resolve_expression(init_expr_ref)?; + if let Some(initializer) = initializer { + self.resolve_expression(&initializer)?; } self.define(name); diff --git a/src/value.rs b/src/value.rs index cc1ff27..db858a5 100644 --- a/src/value.rs +++ b/src/value.rs @@ -4,7 +4,7 @@ use ordered_float::OrderedFloat; use crate::{callable::Callable, instance, token::Literal}; -/// Represent the type of a callable value in Lox. +/// Represents the type of a callable value in Lox. #[derive(Clone, Debug, PartialEq)] pub enum CallableType { /// A regular function or method