diff --git a/src/executor.rs b/src/executor.rs index c4a2c95..19254b1 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1,12 +1,14 @@ use std::{collections::HashMap, fmt}; +use crate::PreLispValue; + #[cfg(test)] mod tests { - use crate::{parse, LispState, LispValue, Tokenizer, TokenizerError}; + use crate::{parse, LispState, LispValue, Tokenizer}; use super::add_function; - fn get_exec_result(source: &str) -> Result, TokenizerError> { + fn get_exec_result(source: &str) -> Result, Box> { let mut state = LispState::new(); state.table.insert(String::from("output"), LispValue::List(vec![], false)); @@ -15,13 +17,13 @@ mod tests { v.push(LispValue::List(args, false)) } - vec![LispValue::Nil] + Ok(vec![LispValue::Nil]) }); let mut tokenizer = Tokenizer::new(String::from(source)); let instructions = parse(tokenizer.tokenize()?); - state.execute(instructions); + state.execute(instructions)?; let mut out = Vec::new(); if let LispValue::List(outerlist, _) = &state.table["output"] { @@ -38,7 +40,7 @@ mod tests { } #[test] - fn test_hello_world() -> Result<(), TokenizerError> { + fn test_hello_world() -> Result<(), Box> { assert_eq!( vec![ LispValue::String(String::from("Hello, World!")) @@ -51,7 +53,7 @@ mod tests { } #[test] - fn test_math() -> Result<(), TokenizerError> { + fn test_math() -> Result<(), Box> { assert_eq!( vec![ LispValue::Float(16.05) @@ -72,16 +74,26 @@ mod tests { } } -use crate::PreLispValue; - pub struct LispState { table: HashMap, } -fn add_function + Clone>(table: &mut HashMap, name: T, func: fn(&mut LispState, Vec) -> Vec) { - table.insert(name.clone().into(), LispValue::RustFunction(name.into(), func)); + +#[derive(Debug)] +pub struct RuntimeError { + message: String, } +impl fmt::Display for RuntimeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Runtime Error: {}", self.message) + } +} + +impl std::error::Error for RuntimeError {} + +type RustFunctionType = fn(&mut LispState, Vec) -> Result, RuntimeError>; + #[derive(Debug, Clone, PartialEq)] pub enum LispValue { Nil, @@ -89,8 +101,9 @@ pub enum LispValue { Integer(i32), Float(f32), List(Vec, bool), - LispFunction(String, Vec), - RustFunction(String, fn(&mut LispState, Vec) -> Vec), + LispFunction(String, Vec, Vec), + RustFunction(String, RustFunctionType), + Var(String) } impl fmt::Display for LispValue { @@ -99,7 +112,7 @@ impl fmt::Display for LispValue { match self { LispValue::Nil => write!(f, "nil"), LispValue::String(str) => write!(f, "\"{}\"", str), - LispValue::LispFunction(name, _) => write!(f, "", name), + LispValue::LispFunction(name, _, _) => write!(f, "", name), LispValue::RustFunction(name, _) => write!(f, "", name), LispValue::Integer(num) => write!(f, "{}", num), LispValue::Float(num) => write!(f, "{}", num), @@ -123,6 +136,19 @@ impl fmt::Display for LispValue { } } +fn get_type(v: &LispValue) -> String { + match v { + LispValue::Nil => String::from("nil"), + LispValue::Integer(_) => String::from("int"), + LispValue::Float(_) => String::from("float"), + LispValue::String(_) => String::from("string"), + LispValue::List(_, _) => String::from("list"), + LispValue::LispFunction(_, _, _) => String::from("function"), + LispValue::RustFunction(_, _) => String::from("function"), + LispValue::Var(_) => String::from("symbol") + } +} + impl From<&str> for LispValue { fn from(value: &str) -> Self { LispValue::String(value.to_string()) @@ -143,6 +169,15 @@ impl From for LispValue { LispValue::Float(value) } } +impl From> for LispValue { + fn from(value: Vec) -> Self { + LispValue::List(value, false) + } +} + +fn add_function + Clone>(table: &mut HashMap, name: T, func: RustFunctionType) { + table.insert(name.clone().into(), LispValue::RustFunction(name.into(), func)); +} impl LispState { pub fn new() -> LispState { @@ -157,7 +192,7 @@ impl LispState { let str = strings.join(" "); println!("{}", str); - vec![LispValue::Nil] + Ok(vec![LispValue::Nil]) }); add_function(&mut table, "concat", |_, args| { @@ -167,14 +202,14 @@ impl LispState { } let str = strings.concat(); - vec![LispValue::String(str)] + Ok(vec![LispValue::String(str)]) }); add_function(&mut table, "add", |_, args| { if args.len() < 1 { - return vec![LispValue::Nil] + return Ok(vec![LispValue::Nil]) } else if args.len() == 1 { - return vec![args[0].clone()]; + return Ok(vec![args[0].clone()]); } let mut t = "int"; @@ -182,7 +217,9 @@ impl LispState { match v { LispValue::Float(_) => t = "float", LispValue::Integer(_) => (), - _ => return vec![LispValue::Nil] + _ => return Err(RuntimeError { + message: format!("Invalid argument to 'add' (expected float or int, got {}", get_type(v)) + }) } } @@ -196,7 +233,7 @@ impl LispState { unreachable!() } } - return vec![LispValue::Integer(sum)]; + return Ok(vec![LispValue::Integer(sum)]); }, "float" => { let mut sum = 0.0 as f32; @@ -209,7 +246,7 @@ impl LispState { unreachable!() } } - return vec![LispValue::Float(sum)]; + return Ok(vec![LispValue::Float(sum)]); }, _ => unreachable!() } @@ -217,9 +254,9 @@ impl LispState { add_function(&mut table, "sub", |_, args| { if args.len() < 1 { - return vec![LispValue::Nil] + return Ok(vec![LispValue::Nil]); } else if args.len() == 1 { - return vec![args[0].clone()]; + return Ok(vec![args[0].clone()]); } let mut t = "int"; @@ -227,7 +264,9 @@ impl LispState { match v { LispValue::Float(_) => t = "float", LispValue::Integer(_) => (), - _ => return vec![LispValue::Nil] + _ => return Err(RuntimeError { + message: format!("Invalid argument to 'sub' (expected float or int, got {}", get_type(v)) + }) } } @@ -242,7 +281,7 @@ impl LispState { unreachable!() } } - return vec![LispValue::Integer(sum)]; + return Ok(vec![LispValue::Integer(sum)]); } else { unreachable!() } @@ -259,7 +298,7 @@ impl LispState { unreachable!() } } - return vec![LispValue::Float(sum)]; + return Ok(vec![LispValue::Float(sum)]); } else if let LispValue::Integer(n) = args[0] { let mut sum = n as f32; for i in 1..args.len() { @@ -271,7 +310,7 @@ impl LispState { unreachable!() } } - return vec![LispValue::Float(sum)]; + return Ok(vec![LispValue::Float(sum)]); } { unreachable!() } @@ -282,9 +321,9 @@ impl LispState { add_function(&mut table, "mul", |_, args| { if args.len() < 1 { - return vec![LispValue::Nil] + return Ok(vec![LispValue::Nil]); } else if args.len() == 1 { - return vec![args[0].clone()]; + return Ok(vec![args[0].clone()]); } let mut t = "int"; @@ -292,7 +331,9 @@ impl LispState { match v { LispValue::Float(_) => t = "float", LispValue::Integer(_) => (), - _ => return vec![LispValue::Nil] + _ => return Err(RuntimeError { + message: format!("Invalid argument to 'mul' (expected float or int, got {}", get_type(v)) + }) } } @@ -307,7 +348,7 @@ impl LispState { unreachable!() } } - return vec![LispValue::Integer(sum)]; + return Ok(vec![LispValue::Integer(sum)]); } else { unreachable!() } @@ -324,7 +365,7 @@ impl LispState { unreachable!() } } - return vec![LispValue::Float(product)]; + return Ok(vec![LispValue::Float(product)]); } else if let LispValue::Integer(n) = args[0] { let mut product = n as f32; for i in 1..args.len() { @@ -336,7 +377,7 @@ impl LispState { unreachable!() } } - return vec![LispValue::Float(product)]; + return Ok(vec![LispValue::Float(product)]); } { unreachable!() } @@ -347,9 +388,9 @@ impl LispState { add_function(&mut table, "div", |_, args| { if args.len() < 1 { - return vec![LispValue::Nil] + return Ok(vec![LispValue::Nil]); } else if args.len() == 1 { - return vec![args[0].clone()]; + return Ok(vec![args[0].clone()]); } let mut t = "int"; @@ -357,7 +398,9 @@ impl LispState { match v { LispValue::Float(_) => t = "float", LispValue::Integer(_) => (), - _ => return vec![LispValue::Nil] + _ => return Err(RuntimeError { + message: format!("Invalid argument to 'div' (expected float or int, got {}", get_type(v)) + }) } } @@ -372,7 +415,7 @@ impl LispState { unreachable!() } } - return vec![LispValue::Integer(sum)]; + return Ok(vec![LispValue::Integer(sum)]); } else { unreachable!() } @@ -389,7 +432,7 @@ impl LispState { unreachable!() } } - return vec![LispValue::Float(product)]; + return Ok(vec![LispValue::Float(product)]); } else if let LispValue::Integer(n) = args[0] { let mut product = n as f32; for i in 1..args.len() { @@ -401,7 +444,7 @@ impl LispState { unreachable!() } } - return vec![LispValue::Float(product)]; + return Ok(vec![LispValue::Float(product)]); } { unreachable!() } @@ -427,11 +470,11 @@ impl LispState { PreLispValue::List(list, quoted) => { LispValue::List(Self::handle_prevalues(self, list), quoted) }, - PreLispValue::LispFunction(name, instructions) => { - LispValue::LispFunction(name, Self::handle_prevalues(self, instructions)) + PreLispValue::LispFunction(name, arg_names, instructions) => { + LispValue::LispFunction(name, arg_names, Self::handle_prevalues(self, instructions)) }, PreLispValue::Var(name) => { - self.table.get(&name).unwrap_or(&LispValue::Nil).to_owned() + LispValue::Var(name) } }) } @@ -439,50 +482,98 @@ impl LispState { out } - fn eval_list(&mut self, list: &Vec, quoted: bool) -> (Vec, bool) { + fn eval_list(&mut self, env: &mut HashMap, list: &Vec, quoted: bool) -> Result<(Vec, bool), RuntimeError> { if list.len() < 1 { - return (vec![LispValue::List(Vec::new(), quoted)], false); + return Ok((vec![LispValue::List(Vec::new(), quoted)], false)); } let list = &mut list.clone(); for i in 0..list.len() { + if let LispValue::Var(name) = &list[i] { + list[i] = env.get(name).unwrap_or_else(|| self.table.get(name).unwrap_or(&LispValue::Nil)).to_owned(); + } if let LispValue::List(elements, quoted) = &list[i] { - let (elems, astuple) = self.eval_list(elements, *quoted); - if astuple { - list[i] = elems.get(0).unwrap_or(&LispValue::Nil).to_owned(); - for j in 1..elems.len() { - list.insert(i, elems[j].to_owned()); + if let Ok((elems, astuple)) = self.eval_list(env, elements, *quoted) { + if astuple { + list[i] = elems.get(0).unwrap_or(&LispValue::Nil).to_owned(); + for j in 1..elems.len() { + list.insert(i, elems[j].to_owned()); + } + } else { + list[i] = LispValue::List(elems, *quoted); } - } else { - list[i] = LispValue::List(elems, *quoted); } } } if quoted { - return (list.to_vec(), false); + return Ok((list.to_vec(), false)); } if let LispValue::RustFunction(_name, f) = &list[0] { - (f(self, list[1..].to_vec()), true) - } else if let LispValue::LispFunction(_name, _) = &list[0] { - (self.call_lisp_func(&list[0], list[1..].to_vec()), true) + Ok((f(self, list[1..].to_vec())?, true)) + } else if let LispValue::LispFunction(_name, _, _) = &list[0] { + Ok((self.call_lisp_func(env, &list[0], list[1..].to_vec())?, true)) } else { - (list.to_vec(), false) + Ok((list.to_vec(), false)) } } - fn call_lisp_func(&mut self, f: &LispValue, args: Vec) -> Vec { - todo!("Calling lisp function {} with args {:?}", f, args) - } + fn call_lisp_func(&mut self, env: &mut HashMap, f: &LispValue, args: Vec) -> Result, RuntimeError> { + let (arg_names, instructions) = match f { + LispValue::LispFunction(_, arg_names, i) => { + (arg_names, i) + }, + _ => return Err(RuntimeError { + message: format!("Invalid argument to call_lisp_func (expected function, got {})", get_type(f)) + }) + }; - pub fn execute(&mut self, instructions: Vec) { - let instructions = Self::handle_prevalues(self, instructions); - for val in instructions { - if let LispValue::List(elements, quoted) = val { - self.eval_list(&elements, quoted); + for i in 0..arg_names.len() { + env.insert(arg_names[i].to_owned(), args.get(i).unwrap_or(&LispValue::Nil).to_owned()); + } + + let mut out = Vec::new(); + for l in instructions { + if let LispValue::List(elements, quoted) = l { + let (values, astuple) = self.eval_list(env, &elements, *quoted)?; + if values.len() > 0 && values[0] != LispValue::Nil { + if astuple { + for v in values { + out.push(v); + } + } else { + out.push(values.into()); + } + } } } + Ok(out) + } + + pub fn execute(&mut self, instructions: Vec) -> Result, RuntimeError> { + let instructions = Self::handle_prevalues(self, instructions); + + let mut outs = Vec::new(); + + let mut env = HashMap::new(); + + for val in instructions { + if let LispValue::List(elements, quoted) = val { + let (vals, astuple) = self.eval_list(&mut env, &elements, quoted)?; + if astuple { + for val in vals { + outs.push(val); + } + } else { + outs.push(vals.into()) + } + } else { + outs.push(val); + } + } + + Ok(outs) } } diff --git a/src/parser.rs b/src/parser.rs index 6feea1a..c59f27b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -93,7 +93,7 @@ pub enum PreLispValue { Integer(i32), Float(f32), List(Vec, bool), // Values, Quoted - LispFunction(String, Vec), + LispFunction(String, Vec, Vec), } impl fmt::Display for PreLispValue { @@ -101,7 +101,7 @@ impl fmt::Display for PreLispValue { match self { PreLispValue::Nil => write!(f, "nil"), PreLispValue::String(str) => write!(f, "{}", str), - PreLispValue::LispFunction(name, _) => write!(f, "<'{}': Lisp Function>", name), + PreLispValue::LispFunction(name, _, _) => write!(f, "<'{}': Lisp Function>", name), PreLispValue::Integer(num) => write!(f, "{}", num), PreLispValue::Float(num) => write!(f, "{}", num), _ => todo!() diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 215be72..7a5ac25 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -118,6 +118,8 @@ impl fmt::Display for TokenizerError { } } +impl std::error::Error for TokenizerError {} + pub struct Tokenizer { code: String, line: u64,