From a1b76956dcc8196b7b5dd9ecc4a80dee5b87c223 Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Mon, 14 Oct 2024 21:55:34 -0400 Subject: [PATCH 01/48] use a box in executor --- src/executor.rs | 68 ++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 8b5b429..fbe8c17 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -75,11 +75,11 @@ impl>> Executor { fn exec( &mut self, - tree: ParseTree, + tree: Box, locals: &mut Cow>) -> Result { - match tree { - ParseTree::Add(x, y) => match (self.exec(*x, locals)?, self.exec(*y, locals)?) { + match *tree { + ParseTree::Add(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x + y)), (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x + y as f64)), (Value::Int(x), Value::Float(y)) => Ok(Value::Float(x as f64 + y)), @@ -87,14 +87,14 @@ impl>> Executor { (Value::String(x), Value::String(y)) => Ok(Value::String(format!("{x}{y}"))), (x, y) => Err(RuntimeError::NoOverloadForTypes("+".into(), vec![x, y])) }, - ParseTree::Sub(x, y) => match (self.exec(*x, locals)?, self.exec(*y, locals)?) { + ParseTree::Sub(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x - y)), (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x - y as f64)), (Value::Int(x), Value::Float(y)) => Ok(Value::Float(x as f64 - y)), (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x - y)), (x, y) => Err(RuntimeError::NoOverloadForTypes("-".into(), vec![x, y])) }, - ParseTree::Mul(x, y) => match (self.exec(*x, locals)?, self.exec(*y, locals)?) { + ParseTree::Mul(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x * y)), (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x * y as f64)), (Value::Int(x), Value::Float(y)) => Ok(Value::Float(x as f64 * y)), @@ -102,28 +102,28 @@ impl>> Executor { (Value::String(x), Value::Int(y)) => Ok(Value::String(x.repeat(y as usize))), (x, y) => Err(RuntimeError::NoOverloadForTypes("*".into(), vec![x, y])) }, - ParseTree::Div(x, y) => match (self.exec(*x, locals)?, self.exec(*y, locals)?) { + ParseTree::Div(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x / y)), (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x / y as f64)), (Value::Int(x), Value::Float(y)) => Ok(Value::Float(x as f64 / y)), (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x / y)), (x, y) => Err(RuntimeError::NoOverloadForTypes("*".into(), vec![x, y])) }, - ParseTree::Exp(x, y) => match (self.exec(*x, locals)?, self.exec(*y, locals)?) { + ParseTree::Exp(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x.pow(y as u32))), (Value::Int(x), Value::Float(y)) => Ok(Value::Float((x as f64).powf(y))), (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x.powf(y as f64))), (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x.powf(y))), (x, y) => Err(RuntimeError::NoOverloadForTypes("**".into(), vec![x, y])), }, - ParseTree::Mod(x, y) => match (self.exec(*x, locals)?, self.exec(*y, locals)?) { + ParseTree::Mod(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x % y)), (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x % y as f64)), (Value::Int(x), Value::Float(y)) => Ok(Value::Float(x as f64 % y)), (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x % y)), (x, y) => Err(RuntimeError::NoOverloadForTypes("%".into(), vec![x, y])), }, - ParseTree::EqualTo(x, y) => match (self.exec(*x, locals)?, self.exec(*y, locals)?) { + ParseTree::EqualTo(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Bool(x == y)), (Value::Int(x), Value::Float(y)) => Ok(Value::Bool(x as f64 == y)), (Value::Float(x), Value::Int(y)) => Ok(Value::Bool(x == y as f64)), @@ -132,35 +132,35 @@ impl>> Executor { (Value::String(x), Value::String(y)) => Ok(Value::Bool(x == y)), (x, y) => Err(RuntimeError::NoOverloadForTypes("==".into(), vec![x, y])), }, - ParseTree::GreaterThan(x, y) => match (self.exec(*x, locals)?, self.exec(*y, locals)?) { + ParseTree::GreaterThan(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Bool(x > y)), (Value::Int(x), Value::Float(y)) => Ok(Value::Bool(x as f64 > y)), (Value::Float(x), Value::Int(y)) => Ok(Value::Bool(x > y as f64)), (Value::Float(x), Value::Float(y)) => Ok(Value::Bool(x > y)), (x, y) => Err(RuntimeError::NoOverloadForTypes(">".into(), vec![x, y])), }, - ParseTree::GreaterThanOrEqualTo(x, y) => match (self.exec(*x, locals)?, self.exec(*y, locals)?) { + ParseTree::GreaterThanOrEqualTo(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Bool(x >= y)), (Value::Int(x), Value::Float(y)) => Ok(Value::Bool(x as f64 >= y)), (Value::Float(x), Value::Int(y)) => Ok(Value::Bool(x >= y as f64)), (Value::Float(x), Value::Float(y)) => Ok(Value::Bool(x >= y)), (x, y) => Err(RuntimeError::NoOverloadForTypes(">=".into(), vec![x, y])), }, - ParseTree::LessThan(x, y) => match (self.exec(*x, locals)?, self.exec(*y, locals)?) { + ParseTree::LessThan(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Bool(x < y)), (Value::Int(x), Value::Float(y)) => Ok(Value::Bool((x as f64) < y)), (Value::Float(x), Value::Int(y)) => Ok(Value::Bool(x < y as f64)), (Value::Float(x), Value::Float(y)) => Ok(Value::Bool(x < y)), (x, y) => Err(RuntimeError::NoOverloadForTypes("<".into(), vec![x, y])), }, - ParseTree::LessThanOrEqualTo(x, y) => match (self.exec(*x, locals)?, self.exec(*y, locals)?) { + ParseTree::LessThanOrEqualTo(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Bool(x <= y)), (Value::Int(x), Value::Float(y)) => Ok(Value::Bool(x as f64 <= y)), (Value::Float(x), Value::Int(y)) => Ok(Value::Bool(x <= y as f64)), (Value::Float(x), Value::Float(y)) => Ok(Value::Bool(x <= y)), (x, y) => Err(RuntimeError::NoOverloadForTypes("<=".into(), vec![x, y])), }, - ParseTree::Not(x) => match self.exec(*x, locals)? { + ParseTree::Not(x) => match self.exec(x, locals)? { Value::Bool(x) => Ok(Value::Bool(!x)), x => Err(RuntimeError::NoOverloadForTypes("not".into(), vec![x])) }, @@ -169,10 +169,10 @@ impl>> Executor { Err(RuntimeError::ImmutableError(ident.clone())) } else { let locals = locals.to_mut(); - let value = self.exec(*body, &mut Cow::Borrowed(&locals))?; + let value = self.exec(body, &mut Cow::Borrowed(&locals))?; locals.insert(ident.clone(), Object::Variable(Evaluation::Computed(value))); - self.exec(*scope, &mut Cow::Borrowed(&locals)) + self.exec(scope, &mut Cow::Borrowed(&locals)) } }, ParseTree::LazyEqu(ident, body, scope) => { @@ -182,7 +182,7 @@ impl>> Executor { let locals = locals.to_mut(); locals.insert(ident.clone(), Object::Variable(Evaluation::Uncomputed(body))); - self.exec(*scope, &mut Cow::Borrowed(&locals)) + self.exec(scope, &mut Cow::Borrowed(&locals)) } }, ParseTree::FunctionDefinition(ident, args, r, body, scope) => { @@ -198,36 +198,36 @@ impl>> Executor { body: Some(body) })); - self.exec(*scope, &mut Cow::Borrowed(&locals)) + self.exec(scope, &mut Cow::Borrowed(&locals)) } } }, ParseTree::Compose(x, y) => { - self.exec(*x, locals)?; - self.exec(*y, locals) + self.exec(x, locals)?; + self.exec(y, locals) }, - ParseTree::Id(x) => self.exec(*x, locals), - ParseTree::If(cond, body) => if match self.exec(*cond, locals)? { + ParseTree::Id(x) => self.exec(x, locals), + ParseTree::If(cond, body) => if match self.exec(cond, locals)? { Value::Float(f) => f != 0.0, Value::Int(i) => i != 0, Value::Bool(b) => b, Value::String(s) => !s.is_empty(), Value::Nil => false, } { - self.exec(*body, locals) + self.exec(body, locals) } else { Ok(Value::Nil) }, - ParseTree::IfElse(cond, istrue, isfalse) => if match self.exec(*cond, locals)? { + ParseTree::IfElse(cond, istrue, isfalse) => if match self.exec(cond, locals)? { Value::Float(f) => f != 0.0, Value::Int(i) => i != 0, Value::Bool(b) => b, Value::String(s) => !s.is_empty(), Value::Nil => false, } { - self.exec(*istrue, locals) + self.exec(istrue, locals) } else { - self.exec(*isfalse, locals) + self.exec(isfalse, locals) }, ParseTree::FunctionCall(ident, args) => { let obj = locals.get(&ident).or(self.globals.get(&ident)).cloned(); @@ -237,10 +237,10 @@ impl>> Executor { let body = f.body.ok_or(RuntimeError::FunctionUndefined(ident.clone()))?; for ((name, _), tree) in std::iter::zip(f.decl.args, args) { - locals.insert(name.clone(), Object::Variable(Evaluation::Computed(self.exec(tree, &mut Cow::Borrowed(locals))?))); + locals.insert(name.clone(), Object::Variable(Evaluation::Computed(self.exec(Box::new(tree), &mut Cow::Borrowed(locals))?))); } - self.exec(*body, &mut Cow::Borrowed(&locals)) + self.exec(body, &mut Cow::Borrowed(&locals)) } else { Err(RuntimeError::FunctionUndeclared(ident.clone())) } @@ -254,7 +254,7 @@ impl>> Executor { match eval { Evaluation::Computed(v) => Ok(v), Evaluation::Uncomputed(tree) => { - let v = self.exec(*tree, &mut Cow::Borrowed(&locals))?; + let v = self.exec(tree, &mut Cow::Borrowed(&locals))?; locals.insert(ident, Object::Variable(Evaluation::Computed(v.clone()))); Ok(v) @@ -265,7 +265,7 @@ impl>> Executor { } }, ParseTree::Constant(value) => Ok(value), - ParseTree::ToInt(x) => match self.exec(*x, locals)? { + ParseTree::ToInt(x) => match self.exec(x, locals)? { Value::Int(x) => Ok(Value::Int(x)), Value::Float(x) => Ok(Value::Int(x as i64)), Value::Bool(x) => Ok(Value::Int(if x { 1 } else { 0 })), @@ -275,7 +275,7 @@ impl>> Executor { } x => Err(RuntimeError::NoOverloadForTypes("int".into(), vec![x])), }, - ParseTree::ToFloat(x) => match self.exec(*x, locals)? { + ParseTree::ToFloat(x) => match self.exec(x, locals)? { Value::Int(x) => Ok(Value::Float(x as f64)), Value::Float(x) => Ok(Value::Float(x)), Value::Bool(x) => Ok(Value::Float(if x { 1.0 } else { 0.0 })), @@ -285,14 +285,14 @@ impl>> Executor { } x => Err(RuntimeError::NoOverloadForTypes("float".into(), vec![x])), }, - ParseTree::ToBool(x) => match self.exec(*x, locals)? { + ParseTree::ToBool(x) => match self.exec(x, locals)? { Value::Int(x) => Ok(Value::Bool(x != 0)), Value::Float(x) => Ok(Value::Bool(x != 0.0)), Value::Bool(x) => Ok(Value::Bool(x)), Value::String(x) => Ok(Value::Bool(!x.is_empty())), x => Err(RuntimeError::NoOverloadForTypes("bool".into(), vec![x])), }, - ParseTree::ToString(x) => Ok(Value::String(format!("{}", self.exec(*x, locals)?))), + ParseTree::ToString(x) => Ok(Value::String(format!("{}", self.exec(x, locals)?))), } } } @@ -304,7 +304,7 @@ impl>> Iterator for Executor let expr = self.exprs.next(); match expr { - Some(Ok(expr)) => Some(self.exec(expr, &mut Cow::Borrowed(&HashMap::new()))), + Some(Ok(expr)) => Some(self.exec(Box::new(expr), &mut Cow::Borrowed(&HashMap::new()))), Some(Err(e)) => Some(Err(RuntimeError::ParseError(e))), None => None, } From 7e8009617e260100402e817fe53823d3b08aa95b Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Mon, 14 Oct 2024 23:58:30 -0400 Subject: [PATCH 02/48] fix order so keywords get parsed correctly --- src/tokenizer.rs | 118 ++++++++++++++++++++--------------------------- 1 file changed, 50 insertions(+), 68 deletions(-) diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 1bd01f7..850d0a6 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -90,59 +90,57 @@ impl Token { let identifier = regex::Regex::new(r#"[A-Za-z_][A-Za-z0-9_']*"#).map_err(|e| TokenizeError::Regex(e))?; let number = regex::Regex::new(r#"([0-9]+\.?[0-9]*)|(\.[0-9])"#).map_err(|e| TokenizeError::Regex(e))?; - if string.is_match(s) { - Ok(Token::Constant(Value::String(s[1..s.len() - 1].to_string()))) - } else if identifier.is_match(s) { - Ok(Token::Identifier(s.to_string())) - } else if number.is_match(s) { - if let Ok(int) = s.parse::() { - Ok(Token::Constant(Value::Int(int))) - } else if let Ok(float) = s.parse::() { - Ok(Token::Constant(Value::Float(float))) - } else { - Err(TokenizeError::InvalidNumericConstant(s.to_string())) - } - } else { - match s { - // First check if s is an operator - "+" => Ok(Token::Operator(Op::Add)), - "-" => Ok(Token::Operator(Op::Sub)), - "*" => Ok(Token::Operator(Op::Mul)), - "/" => Ok(Token::Operator(Op::Div)), - "**" => Ok(Token::Operator(Op::Exp)), - "%" => Ok(Token::Operator(Op::Mod)), - "=" => Ok(Token::Operator(Op::Equ)), - "." => Ok(Token::Operator(Op::LazyEqu)), - "~" => Ok(Token::Operator(Op::Compose)), - "," => Ok(Token::Operator(Op::Id)), - "?" => Ok(Token::Operator(Op::If)), - "??" => Ok(Token::Operator(Op::IfElse)), - ">" => Ok(Token::Operator(Op::GreaterThan)), - "<" => Ok(Token::Operator(Op::LessThan)), - ">=" => Ok(Token::Operator(Op::GreaterThanOrEqualTo)), - "<=" => Ok(Token::Operator(Op::LessThanOrEqualTo)), - "==" => Ok(Token::Operator(Op::EqualTo)), - - // then some keywords - "true" => Ok(Token::Constant(Value::Bool(true))), - "false" => Ok(Token::Constant(Value::Bool(false))), - "not" => Ok(Token::Operator(Op::Not)), - - // Type casting - "int" => Ok(Token::Operator(Op::IntCast)), - "float" => Ok(Token::Operator(Op::FloatCast)), - "bool" => Ok(Token::Operator(Op::BoolCast)), - "string" => Ok(Token::Operator(Op::StringCast)), - - // then variable length keywords - _ => { - if s.starts_with(":") { - Ok(Token::Operator(Op::FunctionDeclare( - get_dot_count(s).map(|x| x - 1).ok_or(TokenizeError::InvalidDynamicOperator(s.to_string()))? - ))) + match s { + // First check if s is an operator + "+" => Ok(Token::Operator(Op::Add)), + "-" => Ok(Token::Operator(Op::Sub)), + "*" => Ok(Token::Operator(Op::Mul)), + "/" => Ok(Token::Operator(Op::Div)), + "**" => Ok(Token::Operator(Op::Exp)), + "%" => Ok(Token::Operator(Op::Mod)), + "=" => Ok(Token::Operator(Op::Equ)), + "." => Ok(Token::Operator(Op::LazyEqu)), + "~" => Ok(Token::Operator(Op::Compose)), + "," => Ok(Token::Operator(Op::Id)), + "?" => Ok(Token::Operator(Op::If)), + "??" => Ok(Token::Operator(Op::IfElse)), + ">" => Ok(Token::Operator(Op::GreaterThan)), + "<" => Ok(Token::Operator(Op::LessThan)), + ">=" => Ok(Token::Operator(Op::GreaterThanOrEqualTo)), + "<=" => Ok(Token::Operator(Op::LessThanOrEqualTo)), + "==" => Ok(Token::Operator(Op::EqualTo)), + + // then some keywords + "true" => Ok(Token::Constant(Value::Bool(true))), + "false" => Ok(Token::Constant(Value::Bool(false))), + "not" => Ok(Token::Operator(Op::Not)), + + // Type casting + "int" => Ok(Token::Operator(Op::IntCast)), + "float" => Ok(Token::Operator(Op::FloatCast)), + "bool" => Ok(Token::Operator(Op::BoolCast)), + "string" => Ok(Token::Operator(Op::StringCast)), + + // then variable length keywords + _ => { + if s.starts_with(":") { + Ok(Token::Operator(Op::FunctionDeclare( + get_dot_count(s).map(|x| x - 1).ok_or(TokenizeError::InvalidDynamicOperator(s.to_string()))? + ))) + } else if string.is_match(s) { + Ok(Token::Constant(Value::String(s[1..s.len() - 1].to_string()))) + } else if identifier.is_match(s) { + Ok(Token::Identifier(s.to_string())) + } else if number.is_match(s) { + if let Ok(int) = s.parse::() { + Ok(Token::Constant(Value::Int(int))) + } else if let Ok(float) = s.parse::() { + Ok(Token::Constant(Value::Float(float))) } else { - Err(TokenizeError::UnableToMatchToken(s.to_string())) + Err(TokenizeError::InvalidNumericConstant(s.to_string())) } + } else { + Err(TokenizeError::UnableToMatchToken(s.to_string())) } } } @@ -258,20 +256,4 @@ impl std::iter::Iterator for Tokenizer { Err(e) => Some(Err(TokenizeError::IO(e))), } } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::str::FromStr; - - #[test] - fn tokenizer() { - let program = ": function x ** x 2 function 1200"; - - let tok = Tokenizer::from_str(program).unwrap(); - let tokens: Vec = tok.collect::>().expect("tokenizer error"); - - println!("{tokens:?}"); - } -} +} \ No newline at end of file From 290393a62f896bffd5e103a1814093949f6780ac Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Mon, 14 Oct 2024 23:59:33 -0400 Subject: [PATCH 03/48] use a box for hashmap --- src/executor.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index fbe8c17..75930cd 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -76,7 +76,7 @@ impl>> Executor { fn exec( &mut self, tree: Box, - locals: &mut Cow>) -> Result + locals: &mut Cow>>) -> Result { match *tree { ParseTree::Add(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { @@ -304,7 +304,7 @@ impl>> Iterator for Executor let expr = self.exprs.next(); match expr { - Some(Ok(expr)) => Some(self.exec(Box::new(expr), &mut Cow::Borrowed(&HashMap::new()))), + Some(Ok(expr)) => Some(self.exec(Box::new(expr), &mut Cow::Borrowed(&Box::new(HashMap::new())))), Some(Err(e)) => Some(Err(RuntimeError::ParseError(e))), None => None, } From e0e33c868beefb625e1ff69af8ad84c3029bb89d Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Tue, 15 Oct 2024 00:28:39 -0400 Subject: [PATCH 04/48] add print builtin function --- src/executor.rs | 51 +++++++++++++++++++++++++++++++++++++++++------- src/parser.rs | 20 +++++++++++-------- src/tokenizer.rs | 4 ++++ 3 files changed, 60 insertions(+), 15 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 75930cd..49e3bd5 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -5,6 +5,7 @@ use std::collections::HashMap; use std::borrow::Cow; use std::fmt::Display; use std::error::Error; +use std::io::{self, Read, Write}; #[derive(Debug)] pub enum RuntimeError { @@ -16,6 +17,7 @@ pub enum RuntimeError { FunctionUndefined(String), NotAVariable(String), ParseFail(String, Type), + IO(io::Error), } impl Display for RuntimeError { @@ -31,6 +33,7 @@ impl Display for RuntimeError { Self::FunctionUndefined(ident) => write!(f, "function `{ident}` was not defined"), Self::NotAVariable(ident) => write!(f, "`{ident}` is a function but was attempted to be used like a variable"), Self::ParseFail(s, t) => write!(f, "`\"{s}\"` couldn't be parsed into {}", t), + Self::IO(e) => write!(f, "{e}"), } } } @@ -60,16 +63,44 @@ enum Object { } /// Executes an input of ParseTrees -pub struct Executor>> { +pub struct Executor<'a, I> +where + I: Iterator>, +{ exprs: I, globals: HashMap, + stdout: Box, + stdin: Box, } -impl>> Executor { +impl<'a, I> Executor<'a, I> +where + I: Iterator>, +{ pub fn new(exprs: I) -> Self { Self { exprs, globals: HashMap::new(), + stdout: Box::new(io::stdout()), + stdin: Box::new(io::stdin()), + } + } + + pub fn stdout(self, writer: impl Write + 'a) -> Self { + Self { + exprs: self.exprs, + globals: self.globals, + stdout: Box::new(writer), + stdin: self.stdin, + } + } + + pub fn stdin(self, reader: impl Read + 'a) -> Self { + Self { + exprs: self.exprs, + globals: self.globals, + stdout: self.stdout, + stdin: Box::new(reader), } } @@ -265,7 +296,7 @@ impl>> Executor { } }, ParseTree::Constant(value) => Ok(value), - ParseTree::ToInt(x) => match self.exec(x, locals)? { + ParseTree::IntCast(x) => match self.exec(x, locals)? { Value::Int(x) => Ok(Value::Int(x)), Value::Float(x) => Ok(Value::Int(x as i64)), Value::Bool(x) => Ok(Value::Int(if x { 1 } else { 0 })), @@ -275,7 +306,7 @@ impl>> Executor { } x => Err(RuntimeError::NoOverloadForTypes("int".into(), vec![x])), }, - ParseTree::ToFloat(x) => match self.exec(x, locals)? { + ParseTree::FloatCast(x) => match self.exec(x, locals)? { Value::Int(x) => Ok(Value::Float(x as f64)), Value::Float(x) => Ok(Value::Float(x)), Value::Bool(x) => Ok(Value::Float(if x { 1.0 } else { 0.0 })), @@ -285,19 +316,25 @@ impl>> Executor { } x => Err(RuntimeError::NoOverloadForTypes("float".into(), vec![x])), }, - ParseTree::ToBool(x) => match self.exec(x, locals)? { + ParseTree::BoolCast(x) => match self.exec(x, locals)? { Value::Int(x) => Ok(Value::Bool(x != 0)), Value::Float(x) => Ok(Value::Bool(x != 0.0)), Value::Bool(x) => Ok(Value::Bool(x)), Value::String(x) => Ok(Value::Bool(!x.is_empty())), x => Err(RuntimeError::NoOverloadForTypes("bool".into(), vec![x])), }, - ParseTree::ToString(x) => Ok(Value::String(format!("{}", self.exec(x, locals)?))), + ParseTree::StringCast(x) => Ok(Value::String(format!("{}", self.exec(x, locals)?))), + ParseTree::Print(x) => match self.exec(x, locals)? { + x => { + writeln!(self.stdout, "{x}").map_err(|e| RuntimeError::IO(e)); + Ok(Value::Nil) + } + } } } } -impl>> Iterator for Executor { +impl<'a, I: Iterator>> Iterator for Executor<'a, I> { type Item = Result; fn next(&mut self) -> Option { diff --git a/src/parser.rs b/src/parser.rs index 64285f9..45e6468 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -70,10 +70,13 @@ pub(crate) enum ParseTree { Constant(Value), // Type Casts - ToInt(Box), - ToFloat(Box), - ToBool(Box), - ToString(Box), + IntCast(Box), + FloatCast(Box), + BoolCast(Box), + StringCast(Box), + + // Misc + Print(Box), } impl ParseTree { @@ -217,10 +220,11 @@ impl ParseTree { Box::new(ParseTree::parse(tokens, globals, locals)?) )), Op::Not => Ok(ParseTree::Not(Box::new(ParseTree::parse(tokens, globals, locals)?))), - Op::IntCast => Ok(ParseTree::ToInt(Box::new(ParseTree::parse(tokens, globals, locals)?))), - Op::FloatCast => Ok(ParseTree::ToFloat(Box::new(ParseTree::parse(tokens, globals, locals)?))), - Op::BoolCast => Ok(ParseTree::ToBool(Box::new(ParseTree::parse(tokens, globals, locals)?))), - Op::StringCast => Ok(ParseTree::ToString(Box::new(ParseTree::parse(tokens, globals, locals)?))), + Op::IntCast => Ok(ParseTree::IntCast(Box::new(ParseTree::parse(tokens, globals, locals)?))), + Op::FloatCast => Ok(ParseTree::FloatCast(Box::new(ParseTree::parse(tokens, globals, locals)?))), + Op::BoolCast => Ok(ParseTree::BoolCast(Box::new(ParseTree::parse(tokens, globals, locals)?))), + Op::StringCast => Ok(ParseTree::StringCast(Box::new(ParseTree::parse(tokens, globals, locals)?))), + Op::Print => Ok(ParseTree::Print(Box::new(ParseTree::parse(tokens, globals, locals)?))) } } } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 850d0a6..6fc705a 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -64,6 +64,7 @@ pub(crate) enum Op { FloatCast, BoolCast, StringCast, + Print, } #[derive(Debug, Clone)] @@ -121,6 +122,9 @@ impl Token { "bool" => Ok(Token::Operator(Op::BoolCast)), "string" => Ok(Token::Operator(Op::StringCast)), + // misc + "print" => Ok(Token::Operator(Op::Print)), + // then variable length keywords _ => { if s.starts_with(":") { From f9f5cb40e91815fe1e1d04f4ddee685d99c2be6d Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Tue, 15 Oct 2024 00:47:20 -0400 Subject: [PATCH 05/48] new Runtime object --- examples/repl.rs | 4 +++- src/executor.rs | 2 +- src/lib.rs | 34 +++++++++++++++++++++++++++++++--- 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/examples/repl.rs b/examples/repl.rs index e978730..88460f5 100644 --- a/examples/repl.rs +++ b/examples/repl.rs @@ -1,7 +1,9 @@ use std::io::{self, BufReader}; fn main() { - for value in lamm::evaluate(BufReader::new(io::stdin())) { + let runtime = lamm::Runtime::new(BufReader::new(io::stdin())); + + for value in runtime.values() { match value { Ok(v) => println!("{v}"), Err(e) => eprintln!("{e}"), diff --git a/src/executor.rs b/src/executor.rs index 49e3bd5..4ab92fb 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -326,7 +326,7 @@ where ParseTree::StringCast(x) => Ok(Value::String(format!("{}", self.exec(x, locals)?))), ParseTree::Print(x) => match self.exec(x, locals)? { x => { - writeln!(self.stdout, "{x}").map_err(|e| RuntimeError::IO(e)); + writeln!(self.stdout, "{x}").map_err(|e| RuntimeError::IO(e))?; Ok(Value::Nil) } } diff --git a/src/lib.rs b/src/lib.rs index 2173d49..db1e335 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,8 +3,12 @@ mod tokenizer; mod parser; mod executor; +use executor::{Executor, RuntimeError}; +use parser::Parser; +use tokenizer::Tokenizer; + use std::fmt::Display; -use std::io::BufRead; +use std::io::{Write, Read, BufRead}; #[derive(Clone, Debug)] pub enum Type { @@ -72,6 +76,30 @@ pub(crate) struct FunctionDeclaration { args: Vec<(String, Type)>, } -pub fn evaluate(r: R) -> impl Iterator> { - executor::Executor::new(parser::Parser::new(tokenizer::Tokenizer::new(r))) +pub struct Runtime<'a, R: BufRead> { + inner: executor::Executor<'a, parser::Parser>> +} + +impl<'a, R: BufRead> Runtime<'a, R> { + pub fn new(reader: R) -> Self { + Self { + inner: Executor::new(Parser::new(Tokenizer::new(reader))) + } + } + + pub fn stdout(self, stdout: impl Write + 'a) -> Self { + Self { + inner: self.inner.stdout(stdout) + } + } + + pub fn stdin(self, stdin: impl Read + 'a) -> Self { + Self { + inner: self.inner.stdin(stdin) + } + } + + pub fn values(self) -> impl Iterator> + use<'a, R> { + self.inner + } } From bc612f74ae73bc2d598e003bb7d797a128fc646d Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Tue, 15 Oct 2024 01:23:12 -0400 Subject: [PATCH 06/48] do not use a nightly feature --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index db1e335..d5e1b4c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -80,7 +80,7 @@ pub struct Runtime<'a, R: BufRead> { inner: executor::Executor<'a, parser::Parser>> } -impl<'a, R: BufRead> Runtime<'a, R> { +impl<'a, R: BufRead + 'a> Runtime<'a, R> { pub fn new(reader: R) -> Self { Self { inner: Executor::new(Parser::new(Tokenizer::new(reader))) @@ -99,7 +99,7 @@ impl<'a, R: BufRead> Runtime<'a, R> { } } - pub fn values(self) -> impl Iterator> + use<'a, R> { + pub fn values(self) -> impl Iterator> + 'a { self.inner } } From f571fac6f1cbe85e9fe0ce6caed85094488f40c2 Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Tue, 15 Oct 2024 15:05:12 -0400 Subject: [PATCH 07/48] add lists these will eventually become arrays when the type-system gets overhauled and makes it easier to implement new types and the functions and operators they support --- src/executor.rs | 11 ++++++++--- src/lib.rs | 15 +++++++++------ src/parser.rs | 40 ++++++++++++++++++++++++++++++++++------ src/tokenizer.rs | 6 ++++++ 4 files changed, 57 insertions(+), 15 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 4ab92fb..f34795c 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -116,6 +116,8 @@ where (Value::Int(x), Value::Float(y)) => Ok(Value::Float(x as f64 + y)), (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x + y)), (Value::String(x), Value::String(y)) => Ok(Value::String(format!("{x}{y}"))), + (Value::Array(x), y) => Ok(Value::Array([x, vec![y]].concat())), + (x, Value::Array(y)) => Ok(Value::Array([vec![x], y].concat())), (x, y) => Err(RuntimeError::NoOverloadForTypes("+".into(), vec![x, y])) }, ParseTree::Sub(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { @@ -216,7 +218,7 @@ where self.exec(scope, &mut Cow::Borrowed(&locals)) } }, - ParseTree::FunctionDefinition(ident, args, r, body, scope) => { + ParseTree::FunctionDefinition(ident, args, body, scope) => { let existing = locals.get(&ident).or(self.globals.get(&ident)).cloned(); match existing { @@ -225,7 +227,7 @@ where let locals = locals.to_mut(); locals.insert(ident.clone(), Object::Function(Function { - decl: FunctionDeclaration { _name: ident.clone(), _r: r, args }, + decl: FunctionDeclaration { _name: ident.clone(), args }, body: Some(body) })); @@ -243,6 +245,7 @@ where Value::Int(i) => i != 0, Value::Bool(b) => b, Value::String(s) => !s.is_empty(), + Value::Array(vec) => !vec.is_empty(), Value::Nil => false, } { self.exec(body, locals) @@ -254,6 +257,7 @@ where Value::Int(i) => i != 0, Value::Bool(b) => b, Value::String(s) => !s.is_empty(), + Value::Array(vec) => !vec.is_empty(), Value::Nil => false, } { self.exec(istrue, locals) @@ -267,7 +271,7 @@ where let locals = locals.to_mut(); let body = f.body.ok_or(RuntimeError::FunctionUndefined(ident.clone()))?; - for ((name, _), tree) in std::iter::zip(f.decl.args, args) { + for (name, tree) in std::iter::zip(f.decl.args, args) { locals.insert(name.clone(), Object::Variable(Evaluation::Computed(self.exec(Box::new(tree), &mut Cow::Borrowed(locals))?))); } @@ -321,6 +325,7 @@ where Value::Float(x) => Ok(Value::Bool(x != 0.0)), Value::Bool(x) => Ok(Value::Bool(x)), Value::String(x) => Ok(Value::Bool(!x.is_empty())), + Value::Array(vec) => Ok(Value::Bool(!vec.is_empty())), x => Err(RuntimeError::NoOverloadForTypes("bool".into(), vec![x])), }, ParseTree::StringCast(x) => Ok(Value::String(format!("{}", self.exec(x, locals)?))), diff --git a/src/lib.rs b/src/lib.rs index d5e1b4c..1527b32 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,3 @@ - mod tokenizer; mod parser; mod executor; @@ -10,15 +9,16 @@ use tokenizer::Tokenizer; use std::fmt::Display; use std::io::{Write, Read, BufRead}; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub enum Type { Float, Int, Bool, String, + Array, + _Function(Box, Vec), Nil, Any, - _Function(Box, Vec), } impl Display for Type { @@ -28,9 +28,10 @@ impl Display for Type { Self::Int => "Int".into(), Self::Bool => "Bool".into(), Self::String => "String".into(), + Self::Array => format!("Array"), + Self::_Function(r, _) => format!("Function -> {}", *r), Self::Nil => "Nil".into(), Self::Any => "Any".into(), - Self::_Function(r, _) => format!("Function -> {}", *r) }) } } @@ -42,6 +43,7 @@ pub enum Value { Int(i64), Bool(bool), String(String), + Array(Vec), Nil, } @@ -52,6 +54,7 @@ impl Value { Self::Int(_) => Type::Int, Self::Bool(_) => Type::Bool, Self::String(_) => Type::String, + Self::Array(_) => Type::Array, Self::Nil => Type::Nil, } } @@ -64,6 +67,7 @@ impl Display for Value { Self::Int(x) => write!(f, "{x}"), Self::Bool(x) => write!(f, "{}", if *x { "true" } else { "false" }), Self::String(x) => write!(f, "{x}"), + Self::Array(v) => write!(f, "[{}]", v.iter().map(|x| format!("{x}")).collect::>().join(" ")), Self::Nil => write!(f, "nil"), } } @@ -72,8 +76,7 @@ impl Display for Value { #[derive(Clone, Debug)] pub(crate) struct FunctionDeclaration { _name: String, - _r: Type, - args: Vec<(String, Type)>, + args: Vec, } pub struct Runtime<'a, R: BufRead> { diff --git a/src/parser.rs b/src/parser.rs index 45e6468..840f8ee 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -14,6 +14,7 @@ pub enum ParseError { InvalidIdentifier, FunctionUndefined(String), VariableUndefined(String), + UnmatchedArrayClose, TokenizeError(TokenizeError), } @@ -26,6 +27,7 @@ impl Display for ParseError { ParseError::FunctionUndefined(name) => write!(f, "Undefined function `{name}`"), ParseError::VariableUndefined(name) => write!(f, "Undefined variable `{name}`"), ParseError::NoInput => write!(f, "No input given"), + ParseError::UnmatchedArrayClose => write!(f, "there was an unmatched array closing operator `]`"), ParseError::TokenizeError(e) => write!(f, "{e}"), } } @@ -54,7 +56,7 @@ pub(crate) enum ParseTree { // Defining Objects Equ(String, Box, Box), LazyEqu(String, Box, Box), - FunctionDefinition(String, Vec<(String, Type)>, Type, Box, Box), + FunctionDefinition(String, Vec, Box, Box), // Functional Operations Compose(Box, Box), @@ -157,9 +159,9 @@ impl ParseTree { .map_err(|e| ParseError::TokenizeError(e))?; if let Token::Identifier(ident) = token { - let args: Vec<(String, Type)> = tokens.take(nargs) + let args: Vec = tokens.take(nargs) .map(|token| match token { - Ok(Token::Identifier(ident)) => Ok((ident, Type::Any)), + Ok(Token::Identifier(ident)) => Ok(ident), Ok(_) => Err(ParseError::InvalidIdentifier), Err(e) => Err(ParseError::TokenizeError(e)), }) @@ -169,14 +171,12 @@ impl ParseTree { locals.insert(ident.clone(), FunctionDeclaration { _name: ident.clone(), - _r: Type::Any, args: args.clone(), }); Ok(ParseTree::FunctionDefinition( ident, args, - Type::Any, Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&*locals))?), Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&*locals))?))) } else { @@ -224,7 +224,35 @@ impl ParseTree { Op::FloatCast => Ok(ParseTree::FloatCast(Box::new(ParseTree::parse(tokens, globals, locals)?))), Op::BoolCast => Ok(ParseTree::BoolCast(Box::new(ParseTree::parse(tokens, globals, locals)?))), Op::StringCast => Ok(ParseTree::StringCast(Box::new(ParseTree::parse(tokens, globals, locals)?))), - Op::Print => Ok(ParseTree::Print(Box::new(ParseTree::parse(tokens, globals, locals)?))) + Op::Print => Ok(ParseTree::Print(Box::new(ParseTree::parse(tokens, globals, locals)?))), + Op::OpenArray => { + let mut depth = 1; + + // take tokens until we reach the end of this array + // if we don't collect them here it causes rust to overflow computing the types + let mut array_tokens = tokens.by_ref().take_while(|t| match t { + Ok(Token::Operator(Op::OpenArray)) => { + depth += 1; + true + }, + Ok(Token::Operator(Op::CloseArray)) => { + depth -= 1; + depth > 0 + } + _ => true, + }).collect::>>().into_iter(); + + let parser: Vec = Parser::new(&mut array_tokens).collect::>()?; + + let tree = parser.iter().fold( + ParseTree::Constant(Value::Array(vec![])), + |acc, x| ParseTree::Add(Box::new(acc), Box::new(x.clone())), + ); + + Ok(tree) + } + Op::Empty => Ok(ParseTree::Constant(Value::Array(vec![]))), + Op::CloseArray => Err(ParseError::UnmatchedArrayClose), } } } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 6fc705a..7c544ea 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -65,6 +65,9 @@ pub(crate) enum Op { BoolCast, StringCast, Print, + OpenArray, + CloseArray, + Empty, } #[derive(Debug, Clone)] @@ -110,6 +113,8 @@ impl Token { ">=" => Ok(Token::Operator(Op::GreaterThanOrEqualTo)), "<=" => Ok(Token::Operator(Op::LessThanOrEqualTo)), "==" => Ok(Token::Operator(Op::EqualTo)), + "[" => Ok(Token::Operator(Op::OpenArray)), + "]" => Ok(Token::Operator(Op::CloseArray)), // then some keywords "true" => Ok(Token::Constant(Value::Bool(true))), @@ -124,6 +129,7 @@ impl Token { // misc "print" => Ok(Token::Operator(Op::Print)), + "empty" => Ok(Token::Operator(Op::Empty)), // then variable length keywords _ => { From 376055b73f810920523f5aedcf8b7bbe49d4fe81 Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Tue, 15 Oct 2024 15:14:24 -0400 Subject: [PATCH 08/48] fix: undefined functions in arrays --- src/parser.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 840f8ee..5229231 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -242,9 +242,13 @@ impl ParseTree { _ => true, }).collect::>>().into_iter(); - let parser: Vec = Parser::new(&mut array_tokens).collect::>()?; + let mut trees: Vec = vec![]; + + while let Ok(tree) = ParseTree::parse(&mut array_tokens, globals, locals) { + trees.push(tree); + } - let tree = parser.iter().fold( + let tree = trees.iter().fold( ParseTree::Constant(Value::Array(vec![])), |acc, x| ParseTree::Add(Box::new(acc), Box::new(x.clone())), ); From ea61007301d42e8904c5018cd72b8f7c49066f2b Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Tue, 15 Oct 2024 16:29:38 -0400 Subject: [PATCH 09/48] random whitespace removal --- src/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.rs b/src/parser.rs index 5229231..b48c222 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -243,7 +243,7 @@ impl ParseTree { }).collect::>>().into_iter(); let mut trees: Vec = vec![]; - + while let Ok(tree) = ParseTree::parse(&mut array_tokens, globals, locals) { trees.push(tree); } From 92f6d43fa0fb6c952545df827ad449d9738de633 Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Tue, 15 Oct 2024 16:29:51 -0400 Subject: [PATCH 10/48] major tokenizer fixes --- src/tokenizer.rs | 146 +++++++++++++++++++++++++++++------------------ 1 file changed, 91 insertions(+), 55 deletions(-) diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 7c544ea..aba7b6e 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -1,6 +1,6 @@ use std::iter::Peekable; use std::{error, io}; -use std::collections::VecDeque; +use std::collections::{VecDeque, HashMap}; use super::Value; use std::fmt::{Display, Formatter}; @@ -77,69 +77,41 @@ pub(crate) enum Token { Constant(Value), } -fn get_dot_count(s: &str) -> Option { - s.chars().fold(Some(0), |acc, c| - match c { - ':' => acc.map(|acc| acc + 2), - '.' => acc.map(|acc| acc + 1), - _ => None, - } - ) +fn get_dot_count>(s: &mut Peekable) -> Option { + let mut total = 0; + + while let Some(n) = s.next_if(|&c| c == ':' || c == '.').map(|c| match c { + ':' => 2, + '.' => 1, + _ => 0, + }) { + total += n; + } + + Some(total) } impl Token { /// Parse a single token fn parse(s: &str) -> Result { - let string = regex::Regex::new(r#"".+""#).map_err(|e| TokenizeError::Regex(e))?; let identifier = regex::Regex::new(r#"[A-Za-z_][A-Za-z0-9_']*"#).map_err(|e| TokenizeError::Regex(e))?; let number = regex::Regex::new(r#"([0-9]+\.?[0-9]*)|(\.[0-9])"#).map_err(|e| TokenizeError::Regex(e))?; match s { - // First check if s is an operator - "+" => Ok(Token::Operator(Op::Add)), - "-" => Ok(Token::Operator(Op::Sub)), - "*" => Ok(Token::Operator(Op::Mul)), - "/" => Ok(Token::Operator(Op::Div)), - "**" => Ok(Token::Operator(Op::Exp)), - "%" => Ok(Token::Operator(Op::Mod)), - "=" => Ok(Token::Operator(Op::Equ)), - "." => Ok(Token::Operator(Op::LazyEqu)), - "~" => Ok(Token::Operator(Op::Compose)), - "," => Ok(Token::Operator(Op::Id)), - "?" => Ok(Token::Operator(Op::If)), - "??" => Ok(Token::Operator(Op::IfElse)), - ">" => Ok(Token::Operator(Op::GreaterThan)), - "<" => Ok(Token::Operator(Op::LessThan)), - ">=" => Ok(Token::Operator(Op::GreaterThanOrEqualTo)), - "<=" => Ok(Token::Operator(Op::LessThanOrEqualTo)), - "==" => Ok(Token::Operator(Op::EqualTo)), - "[" => Ok(Token::Operator(Op::OpenArray)), - "]" => Ok(Token::Operator(Op::CloseArray)), - - // then some keywords + // Match keywords first "true" => Ok(Token::Constant(Value::Bool(true))), "false" => Ok(Token::Constant(Value::Bool(false))), "not" => Ok(Token::Operator(Op::Not)), - - // Type casting "int" => Ok(Token::Operator(Op::IntCast)), "float" => Ok(Token::Operator(Op::FloatCast)), "bool" => Ok(Token::Operator(Op::BoolCast)), "string" => Ok(Token::Operator(Op::StringCast)), - - // misc "print" => Ok(Token::Operator(Op::Print)), "empty" => Ok(Token::Operator(Op::Empty)), - // then variable length keywords + // then identifiers and numbers _ => { - if s.starts_with(":") { - Ok(Token::Operator(Op::FunctionDeclare( - get_dot_count(s).map(|x| x - 1).ok_or(TokenizeError::InvalidDynamicOperator(s.to_string()))? - ))) - } else if string.is_match(s) { - Ok(Token::Constant(Value::String(s[1..s.len() - 1].to_string()))) - } else if identifier.is_match(s) { + if identifier.is_match(s) { Ok(Token::Identifier(s.to_string())) } else if number.is_match(s) { if let Ok(int) = s.parse::() { @@ -173,7 +145,28 @@ impl Tokenizer { /// Tokenizes more input and adds them to the internal queue fn tokenize>(&mut self, mut iter: Peekable) { - const OPERATOR_CHARS: &'static str = "!@$%^&*()-=+[]{}|;:,<.>/?"; + let operators: HashMap<&'static str, Op> = HashMap::from([ + ("+", Op::Add), + ("-", Op::Sub), + ("*", Op::Mul), + ("/", Op::Div), + ("**", Op::Exp), + ("%", Op::Mod), + ("=", Op::Equ), + (".", Op::LazyEqu), + (":", Op::FunctionDeclare(1)), + ("~", Op::Compose), + (",", Op::Id), + ("?", Op::If), + ("??", Op::IfElse), + (">", Op::GreaterThan), + ("<", Op::LessThan), + (">=", Op::GreaterThanOrEqualTo), + ("<=", Op::LessThanOrEqualTo), + ("==", Op::EqualTo), + ("[", Op::OpenArray), + ("]", Op::CloseArray), + ]); let c = if let Some(c) = iter.next() { c @@ -188,19 +181,9 @@ impl Tokenizer { token.push(c); } - self.tokens.push_back(Token::parse(&token)); - self.tokenize(iter) - } else if OPERATOR_CHARS.contains(c) { - let mut token = String::from(c); - - while let Some(c) = iter.next_if(|&c| OPERATOR_CHARS.contains(c)) { - token.push(c); - } - self.tokens.push_back(Token::parse(&token)); self.tokenize(iter) } else if c == '#' { - // consume comments let _: String = iter.by_ref().take_while(|&c| c != '\n').collect(); } else if c == '\"' { let mut token = String::new(); @@ -229,11 +212,64 @@ impl Tokenizer { } self.tokens.push_back(Ok(Token::Constant(Value::String(token)))); + self.tokenize(iter) + } else if operators.keys().any(|x| x.starts_with(c)) { + let mut token = String::from(c); + + loop { + let possible: HashMap<&'static str, Op> = operators + .clone().into_iter() + .filter(|(key, _)| key.starts_with(&token)) + .collect(); + + let is_expected = |c: &char| + possible.iter().any(|(op, _)| match op.chars().nth(token.len()) { + Some(i) => *c == i, + None => false, + }); + + match possible.len() { + 1 => { + self.tokens.push_back(Ok(Token::Operator(match possible.get(token.as_str()).unwrap().clone() { + Op::FunctionDeclare(n) => { + let count = match get_dot_count(&mut iter) { + Some(count) => count, + None => { + self.tokens.push_back(Err(TokenizeError::InvalidDynamicOperator(token))); + return; + } + }; + Op::FunctionDeclare(n + count) + } + op => op, + }))); + break; + } + 0 => { + self.tokens.push_back(Err(TokenizeError::InvalidCharacter(c))); + return; + } + _ => { + let next = match iter.next_if(is_expected) { + Some(c) => c, + None => { + // at this point, token must be in the hashmap possible, otherwise it wouldnt have any matches + self.tokens.push_back(Ok(Token::Operator(possible.get(token.as_str()).unwrap().clone()))); + break; + } + }; + + token.push(next); + } + } + } + self.tokenize(iter) } else if c.is_whitespace() { self.tokenize(iter) } else { self.tokens.push_back(Err(TokenizeError::InvalidCharacter(c))); + return; } } } From 3fe7ba9a7780d7c5e76fd000d8ed24e3f63753ab Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Tue, 15 Oct 2024 16:46:24 -0400 Subject: [PATCH 11/48] this branch is unreachable --- src/tokenizer.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/tokenizer.rs b/src/tokenizer.rs index aba7b6e..ffd8d1a 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -245,10 +245,7 @@ impl Tokenizer { }))); break; } - 0 => { - self.tokens.push_back(Err(TokenizeError::InvalidCharacter(c))); - return; - } + 0 => unreachable!(), _ => { let next = match iter.next_if(is_expected) { Some(c) => c, From 52c22c8f35215efc6da59200f506fa3cdb543189 Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Tue, 15 Oct 2024 16:58:45 -0400 Subject: [PATCH 12/48] fix: . operator broken (temporary fix --- src/tokenizer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tokenizer.rs b/src/tokenizer.rs index ffd8d1a..0b0825c 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -174,7 +174,7 @@ impl Tokenizer { return; }; - if c.is_alphanumeric() || c == '.' { + if c.is_alphanumeric() { let mut token = String::from(c); while let Some(c) = iter.next_if(|&c| c.is_alphanumeric() || c == '.' || c == '\'') { From 7a9a122765a90d8f4a40775200d5915546676887 Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Tue, 15 Oct 2024 17:23:12 -0400 Subject: [PATCH 13/48] parser fix, errors inside of an array actually return the error --- src/parser.rs | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index b48c222..eeeb1a8 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -230,7 +230,7 @@ impl ParseTree { // take tokens until we reach the end of this array // if we don't collect them here it causes rust to overflow computing the types - let mut array_tokens = tokens.by_ref().take_while(|t| match t { + let array_tokens = tokens.by_ref().take_while(|t| match t { Ok(Token::Operator(Op::OpenArray)) => { depth += 1; true @@ -240,15 +240,18 @@ impl ParseTree { depth > 0 } _ => true, - }).collect::>>().into_iter(); + }).collect::, TokenizeError>>().map_err(|e| ParseError::TokenizeError(e))?; - let mut trees: Vec = vec![]; + let array_tokens: Vec> = array_tokens.into_iter().map(|t| Ok(t)).collect(); - while let Ok(tree) = ParseTree::parse(&mut array_tokens, globals, locals) { - trees.push(tree); - } + let trees: Vec = Parser::new(array_tokens.into_iter()) + .globals(globals.clone()) + .locals(locals.to_mut().to_owned()) + .collect::>()?; - let tree = trees.iter().fold( + trees.iter().for_each(|t| println!("{t:?}")); + + let tree = trees.into_iter().fold( ParseTree::Constant(Value::Array(vec![])), |acc, x| ParseTree::Add(Box::new(acc), Box::new(x.clone())), ); @@ -286,6 +289,22 @@ impl>> Parser { locals: HashMap::new() } } + + pub fn globals(self, globals: HashMap) -> Self { + Self { + tokens: self.tokens, + globals, + locals: self.locals, + } + } + + pub fn locals(self, locals: HashMap) -> Self { + Self { + tokens: self.tokens, + globals: self.globals, + locals, + } + } } impl>> Iterator for Parser { From 0de9652360594a842348c93bebfc9751090e2de1 Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Tue, 15 Oct 2024 17:26:58 -0400 Subject: [PATCH 14/48] remove println debugging --- src/parser.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index eeeb1a8..611067b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -249,8 +249,6 @@ impl ParseTree { .locals(locals.to_mut().to_owned()) .collect::>()?; - trees.iter().for_each(|t| println!("{t:?}")); - let tree = trees.into_iter().fold( ParseTree::Constant(Value::Array(vec![])), |acc, x| ParseTree::Add(Box::new(acc), Box::new(x.clone())), From 4784cc295e21fc9c4a0974a3f5b498517c0d53df Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Tue, 15 Oct 2024 17:28:10 -0400 Subject: [PATCH 15/48] display strings with quotation marks --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 1527b32..5ef7c0d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,7 +66,7 @@ impl Display for Value { Self::Float(x) => write!(f, "{x}"), Self::Int(x) => write!(f, "{x}"), Self::Bool(x) => write!(f, "{}", if *x { "true" } else { "false" }), - Self::String(x) => write!(f, "{x}"), + Self::String(x) => write!(f, "\"{x}\""), Self::Array(v) => write!(f, "[{}]", v.iter().map(|x| format!("{x}")).collect::>().join(" ")), Self::Nil => write!(f, "nil"), } From 5701bf82688ef90ec0ab92a54e98dd2ac09a4156 Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Tue, 15 Oct 2024 18:22:48 -0400 Subject: [PATCH 16/48] add more bool operators --- src/executor.rs | 17 +++++++ src/parser.rs | 117 +++++++++++++++++++++-------------------------- src/tokenizer.rs | 57 +++++++++++++++++------ 3 files changed, 110 insertions(+), 81 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index f34795c..2133139 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -165,6 +165,15 @@ where (Value::String(x), Value::String(y)) => Ok(Value::Bool(x == y)), (x, y) => Err(RuntimeError::NoOverloadForTypes("==".into(), vec![x, y])), }, + ParseTree::NotEqualTo(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { + (Value::Int(x), Value::Int(y)) => Ok(Value::Bool(x != y)), + (Value::Int(x), Value::Float(y)) => Ok(Value::Bool(x as f64 != y)), + (Value::Float(x), Value::Int(y)) => Ok(Value::Bool(x != y as f64)), + (Value::Float(x), Value::Float(y)) => Ok(Value::Bool(x != y)), + (Value::Bool(x), Value::Bool(y)) => Ok(Value::Bool(x != y)), + (Value::String(x), Value::String(y)) => Ok(Value::Bool(x != y)), + (x, y) => Err(RuntimeError::NoOverloadForTypes("!=".into(), vec![x, y])), + }, ParseTree::GreaterThan(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Bool(x > y)), (Value::Int(x), Value::Float(y)) => Ok(Value::Bool(x as f64 > y)), @@ -197,6 +206,14 @@ where Value::Bool(x) => Ok(Value::Bool(!x)), x => Err(RuntimeError::NoOverloadForTypes("not".into(), vec![x])) }, + ParseTree::And(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { + (Value::Bool(x), Value::Bool(y)) => Ok(Value::Bool(x && y)), + (x, y) => Err(RuntimeError::NoOverloadForTypes("&&".into(), vec![x, y])) + }, + ParseTree::Or(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { + (Value::Bool(x), Value::Bool(y)) => Ok(Value::Bool(x || y)), + (x, y) => Err(RuntimeError::NoOverloadForTypes("||".into(), vec![x, y])) + }, ParseTree::Equ(ident, body, scope) => { if self.globals.contains_key(&ident) || locals.contains_key(&ident) { Err(RuntimeError::ImmutableError(ident.clone())) diff --git a/src/parser.rs b/src/parser.rs index 611067b..3f7cfc4 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -47,11 +47,14 @@ pub(crate) enum ParseTree { // Boolean Operations EqualTo(Box, Box), + NotEqualTo(Box, Box), GreaterThan(Box, Box), GreaterThanOrEqualTo(Box, Box), LessThan(Box, Box), LessThanOrEqualTo(Box, Box), Not(Box), + And(Box, Box), + Or(Box, Box), // Defining Objects Equ(String, Box, Box), @@ -81,6 +84,30 @@ pub(crate) enum ParseTree { Print(Box), } +macro_rules! one_arg { + ($op:ident, $tokens:ident, $globals:ident, $locals:ident) => { + Ok(ParseTree::$op( + Box::new(ParseTree::parse($tokens, $globals, $locals)?) + ))} +} + +macro_rules! two_arg { + ($op:ident, $tokens:ident, $globals:ident, $locals:ident) => { + Ok(ParseTree::$op( + Box::new(ParseTree::parse($tokens, $globals, $locals)?), + Box::new(ParseTree::parse($tokens, $globals, $locals)?) + ))} +} + +macro_rules! three_arg { + ($op:ident, $tokens:ident, $globals:ident, $locals:ident) => { + Ok(ParseTree::$op( + Box::new(ParseTree::parse($tokens, $globals, $locals)?), + Box::new(ParseTree::parse($tokens, $globals, $locals)?), + Box::new(ParseTree::parse($tokens, $globals, $locals)?) + ))} +} + impl ParseTree { fn parse( tokens: &mut I, @@ -108,30 +135,12 @@ impl ParseTree { } Token::Operator(op) => { match op { - Op::Add => Ok(ParseTree::Add( - Box::new(ParseTree::parse(tokens, globals, locals)?), - Box::new(ParseTree::parse(tokens, globals, locals)?) - )), - Op::Sub => Ok(ParseTree::Sub( - Box::new(ParseTree::parse(tokens, globals, locals)?), - Box::new(ParseTree::parse(tokens, globals, locals)?) - )), - Op::Mul => Ok(ParseTree::Mul( - Box::new(ParseTree::parse(tokens, globals, locals)?), - Box::new(ParseTree::parse(tokens, globals, locals)?) - )), - Op::Div => Ok(ParseTree::Div( - Box::new(ParseTree::parse(tokens, globals, locals)?), - Box::new(ParseTree::parse(tokens, globals, locals)?) - )), - Op::Exp => Ok(ParseTree::Exp( - Box::new(ParseTree::parse(tokens, globals, locals)?), - Box::new(ParseTree::parse(tokens, globals, locals)?) - )), - Op::Mod => Ok(ParseTree::Mod( - Box::new(ParseTree::parse(tokens, globals, locals)?), - Box::new(ParseTree::parse(tokens, globals, locals)?) - )), + Op::Add => two_arg!(Add, tokens, globals, locals), + Op::Sub => two_arg!(Sub, tokens, globals, locals), + Op::Mul => two_arg!(Mul, tokens, globals, locals), + Op::Div => two_arg!(Div, tokens, globals, locals), + Op::Exp => two_arg!(Exp, tokens, globals, locals), + Op::Mod => two_arg!(Mod, tokens, globals, locals), Op::Equ | Op::LazyEqu => { let token = tokens.next() .ok_or(ParseError::UnexpectedEndInput)? @@ -183,48 +192,21 @@ impl ParseTree { Err(ParseError::InvalidIdentifier) } } - Op::Compose => Ok(ParseTree::Compose( - Box::new(ParseTree::parse(tokens, globals, locals)?), - Box::new(ParseTree::parse(tokens, globals, locals)?) - )), - Op::Id => Ok(ParseTree::Id( - Box::new(ParseTree::parse(tokens, globals, locals)?) - )), - Op::If => Ok(ParseTree::If( - Box::new(ParseTree::parse(tokens, globals, locals)?), - Box::new(ParseTree::parse(tokens, globals, locals)?) - )), - Op::IfElse => Ok(ParseTree::IfElse( - Box::new(ParseTree::parse(tokens, globals, locals)?), - Box::new(ParseTree::parse(tokens, globals, locals)?), - Box::new(ParseTree::parse(tokens, globals, locals)?) - )), - Op::EqualTo => Ok(ParseTree::EqualTo( - Box::new(ParseTree::parse(tokens, globals, locals)?), - Box::new(ParseTree::parse(tokens, globals, locals)?) - )), - Op::GreaterThan => Ok(ParseTree::GreaterThan( - Box::new(ParseTree::parse(tokens, globals, locals)?), - Box::new(ParseTree::parse(tokens, globals, locals)?) - )), - Op::LessThan => Ok(ParseTree::LessThan( - Box::new(ParseTree::parse(tokens, globals, locals)?), - Box::new(ParseTree::parse(tokens, globals, locals)?) - )), - Op::GreaterThanOrEqualTo => Ok(ParseTree::GreaterThanOrEqualTo( - Box::new(ParseTree::parse(tokens, globals, locals)?), - Box::new(ParseTree::parse(tokens, globals, locals)?) - )), - Op::LessThanOrEqualTo => Ok(ParseTree::LessThanOrEqualTo( - Box::new(ParseTree::parse(tokens, globals, locals)?), - Box::new(ParseTree::parse(tokens, globals, locals)?) - )), - Op::Not => Ok(ParseTree::Not(Box::new(ParseTree::parse(tokens, globals, locals)?))), - Op::IntCast => Ok(ParseTree::IntCast(Box::new(ParseTree::parse(tokens, globals, locals)?))), - Op::FloatCast => Ok(ParseTree::FloatCast(Box::new(ParseTree::parse(tokens, globals, locals)?))), - Op::BoolCast => Ok(ParseTree::BoolCast(Box::new(ParseTree::parse(tokens, globals, locals)?))), - Op::StringCast => Ok(ParseTree::StringCast(Box::new(ParseTree::parse(tokens, globals, locals)?))), - Op::Print => Ok(ParseTree::Print(Box::new(ParseTree::parse(tokens, globals, locals)?))), + Op::Compose => two_arg!(Compose, tokens, globals, locals), + Op::Id => one_arg!(Id, tokens, globals, locals), + Op::If => two_arg!(If, tokens, globals, locals), + Op::IfElse => three_arg!(IfElse, tokens, globals, locals), + Op::EqualTo => two_arg!(EqualTo, tokens, globals, locals), + Op::GreaterThan => two_arg!(GreaterThan, tokens, globals, locals), + Op::LessThan => two_arg!(LessThan, tokens, globals, locals), + Op::GreaterThanOrEqualTo => two_arg!(GreaterThanOrEqualTo, tokens, globals, locals), + Op::LessThanOrEqualTo => two_arg!(LessThanOrEqualTo, tokens, globals, locals), + Op::Not => one_arg!(Not, tokens, globals, locals), + Op::IntCast => one_arg!(IntCast, tokens, globals, locals), + Op::FloatCast => one_arg!(FloatCast, tokens, globals, locals), + Op::BoolCast => one_arg!(BoolCast, tokens, globals, locals), + Op::StringCast => one_arg!(StringCast, tokens, globals, locals), + Op::Print => one_arg!(Print, tokens, globals, locals), Op::OpenArray => { let mut depth = 1; @@ -258,6 +240,9 @@ impl ParseTree { } Op::Empty => Ok(ParseTree::Constant(Value::Array(vec![]))), Op::CloseArray => Err(ParseError::UnmatchedArrayClose), + Op::NotEqualTo => two_arg!(NotEqualTo, tokens, globals, locals), + Op::And => two_arg!(And, tokens, globals, locals), + Op::Or => two_arg!(Or, tokens, globals, locals), } } } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 0b0825c..6e4ce48 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -57,6 +57,7 @@ pub(crate) enum Op { GreaterThan, LessThan, EqualTo, + NotEqualTo, GreaterThanOrEqualTo, LessThanOrEqualTo, Not, @@ -68,6 +69,8 @@ pub(crate) enum Op { OpenArray, CloseArray, Empty, + And, + Or, } #[derive(Debug, Clone)] @@ -101,7 +104,6 @@ impl Token { // Match keywords first "true" => Ok(Token::Constant(Value::Bool(true))), "false" => Ok(Token::Constant(Value::Bool(false))), - "not" => Ok(Token::Operator(Op::Not)), "int" => Ok(Token::Operator(Op::IntCast)), "float" => Ok(Token::Operator(Op::FloatCast)), "bool" => Ok(Token::Operator(Op::BoolCast)), @@ -164,8 +166,12 @@ impl Tokenizer { (">=", Op::GreaterThanOrEqualTo), ("<=", Op::LessThanOrEqualTo), ("==", Op::EqualTo), + ("!=", Op::NotEqualTo), ("[", Op::OpenArray), ("]", Op::CloseArray), + ("!", Op::Not), + ("&&", Op::And), + ("||", Op::Or), ]); let c = if let Some(c) = iter.next() { @@ -217,11 +223,15 @@ impl Tokenizer { let mut token = String::from(c); loop { + // get a list of all tokens this current token could possibly be let possible: HashMap<&'static str, Op> = operators .clone().into_iter() .filter(|(key, _)| key.starts_with(&token)) .collect(); + // checks if a character is "expected", aka based on how many chars + // we have eaten so far, which characters out of the current nominees + // are expected in the next position let is_expected = |c: &char| possible.iter().any(|(op, _)| match op.chars().nth(token.len()) { Some(i) => *c == i, @@ -230,20 +240,37 @@ impl Tokenizer { match possible.len() { 1 => { - self.tokens.push_back(Ok(Token::Operator(match possible.get(token.as_str()).unwrap().clone() { - Op::FunctionDeclare(n) => { - let count = match get_dot_count(&mut iter) { - Some(count) => count, - None => { - self.tokens.push_back(Err(TokenizeError::InvalidDynamicOperator(token))); - return; - } - }; - Op::FunctionDeclare(n + count) - } - op => op, - }))); - break; + // if the current operator exists in possible, we push it + // if not, we need to make sure that the next characters + // we grab *actually* match the last operator + if let Some(op) = possible.get(token.as_str()) { + self.tokens.push_back(Ok(Token::Operator(match op { + // special handling for "dynamic" operators + Op::FunctionDeclare(n) => { + let count = match get_dot_count(&mut iter) { + Some(count) => count, + None => { + self.tokens.push_back(Err(TokenizeError::InvalidDynamicOperator(token))); + return; + } + }; + Op::FunctionDeclare(n + count) + } + op => op.clone(), + }))); + + break; + } else { + let next = match iter.next_if(is_expected) { + Some(c) => c, + None => { + self.tokens.push_back(Err(TokenizeError::UnableToMatchToken(format!("{token}")))); + return; + } + }; + + token.push(next); + } } 0 => unreachable!(), _ => { From 34077344cac175269d64e713867e5c8205af68fc Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Wed, 16 Oct 2024 01:14:26 -0400 Subject: [PATCH 17/48] add function parameters and type declarations --- src/executor.rs | 93 +++++++++++++++-------- src/lib.rs | 67 ++++++++++++---- src/parser.rs | 194 ++++++++++++++++++++++++++++++++++++++--------- src/tokenizer.rs | 60 ++++++++++++++- 4 files changed, 333 insertions(+), 81 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 2133139..2ff5b1b 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1,4 +1,4 @@ -use super::{Value, Type, FunctionDeclaration}; +use super::{Value, Type, Function, FunctionType}; use super::parser::{ParseTree, ParseError}; use std::collections::HashMap; @@ -17,13 +17,14 @@ pub enum RuntimeError { FunctionUndefined(String), NotAVariable(String), ParseFail(String, Type), + TypeError(Type, Type), IO(io::Error), } impl Display for RuntimeError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::ParseError(e) => write!(f, "{e}"), + Self::ParseError(e) => write!(f, "Parser Error: {e}"), Self::NoOverloadForTypes(op, values) => write!(f, "No overload of `{op}` exists for the operands `[{}]`", values.iter().map(|x| format!("{}({x})", x.get_type())).collect::>().join(", ")), @@ -34,6 +35,7 @@ impl Display for RuntimeError { Self::NotAVariable(ident) => write!(f, "`{ident}` is a function but was attempted to be used like a variable"), Self::ParseFail(s, t) => write!(f, "`\"{s}\"` couldn't be parsed into {}", t), Self::IO(e) => write!(f, "{e}"), + Self::TypeError(left, right) => write!(f, "expected type `{left}` but got type `{right}`"), } } } @@ -50,12 +52,6 @@ enum Evaluation { Uncomputed(Box), } -#[derive(Clone, Debug)] -struct Function { - decl: FunctionDeclaration, - body: Option>, -} - #[derive(Clone, Debug)] enum Object { Variable(Evaluation), @@ -116,8 +112,8 @@ where (Value::Int(x), Value::Float(y)) => Ok(Value::Float(x as f64 + y)), (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x + y)), (Value::String(x), Value::String(y)) => Ok(Value::String(format!("{x}{y}"))), - (Value::Array(x), y) => Ok(Value::Array([x, vec![y]].concat())), - (x, Value::Array(y)) => Ok(Value::Array([vec![x], y].concat())), + (Value::Array(_, x), y) => Ok(Value::Array(Type::Any, [x, vec![y]].concat())), + (x, Value::Array(_, y)) => Ok(Value::Array(Type::Any, [vec![x], y].concat())), (x, y) => Err(RuntimeError::NoOverloadForTypes("+".into(), vec![x, y])) }, ParseTree::Sub(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { @@ -235,19 +231,29 @@ where self.exec(scope, &mut Cow::Borrowed(&locals)) } }, - ParseTree::FunctionDefinition(ident, args, body, scope) => { - let existing = locals.get(&ident).or(self.globals.get(&ident)).cloned(); - + ParseTree::FunctionDefinition(func, scope) => { + let ident = func.name.clone().unwrap(); + + let existing = locals.get(&ident) + .or(self.globals.get(&ident)); + match existing { - Some(_) => Err(RuntimeError::ImmutableError(ident.clone())), + Some(Object::Function(f)) => { + if f.body.is_some() || f.arg_names.is_some() { + return Err(RuntimeError::ImmutableError(ident.clone())); + } + + let new_func = Function::named(func.name.unwrap().as_str(), func.t.clone(), func.arg_names.clone(), func.body.clone()); + + let locals = locals.to_mut(); + locals.insert(ident.clone(), Object::Function(new_func)); + + self.exec(scope, &mut Cow::Borrowed(&locals)) + } + Some(Object::Variable(_)) => Err(RuntimeError::ImmutableError(ident.clone())), None => { let locals = locals.to_mut(); - - locals.insert(ident.clone(), Object::Function(Function { - decl: FunctionDeclaration { _name: ident.clone(), args }, - body: Some(body) - })); - + locals.insert(ident.clone(), Object::Function(func)); self.exec(scope, &mut Cow::Borrowed(&locals)) } } @@ -262,8 +268,9 @@ where Value::Int(i) => i != 0, Value::Bool(b) => b, Value::String(s) => !s.is_empty(), - Value::Array(vec) => !vec.is_empty(), + Value::Array(_, vec) => !vec.is_empty(), Value::Nil => false, + x => return Err(RuntimeError::NoOverloadForTypes("?".into(), vec![x])), } { self.exec(body, locals) } else { @@ -274,8 +281,9 @@ where Value::Int(i) => i != 0, Value::Bool(b) => b, Value::String(s) => !s.is_empty(), - Value::Array(vec) => !vec.is_empty(), + Value::Array(_, vec) => !vec.is_empty(), Value::Nil => false, + x => return Err(RuntimeError::NoOverloadForTypes("?".into(), vec![x])), } { self.exec(istrue, locals) } else { @@ -284,17 +292,26 @@ where ParseTree::FunctionCall(ident, args) => { let obj = locals.get(&ident).or(self.globals.get(&ident)).cloned(); - if let Some(Object::Function(f)) = obj { - let locals = locals.to_mut(); - let body = f.body.ok_or(RuntimeError::FunctionUndefined(ident.clone()))?; + match obj { + Some(Object::Function(f)) => { + let locals = locals.to_mut(); - for (name, tree) in std::iter::zip(f.decl.args, args) { - locals.insert(name.clone(), Object::Variable(Evaluation::Computed(self.exec(Box::new(tree), &mut Cow::Borrowed(locals))?))); + for ((t, name), tree) in std::iter::zip(std::iter::zip(f.t.1, f.arg_names.unwrap()), args) { + let v = self.exec(Box::new(tree), &mut Cow::Borrowed(locals))?; + + if v.get_type() != t && t != Type::Any { + return Err(RuntimeError::TypeError(t, v.get_type())); + } + + locals.insert(name.clone(), match v { + Value::Function(func) => Object::Function(func), + _ => Object::Variable(Evaluation::Computed(v)) + }); + } + + self.exec(f.body.unwrap(), &mut Cow::Borrowed(&locals)) } - - self.exec(body, &mut Cow::Borrowed(&locals)) - } else { - Err(RuntimeError::FunctionUndeclared(ident.clone())) + _ => Err(RuntimeError::FunctionUndefined(ident.clone())) } }, ParseTree::Variable(ident) => { @@ -342,7 +359,7 @@ where Value::Float(x) => Ok(Value::Bool(x != 0.0)), Value::Bool(x) => Ok(Value::Bool(x)), Value::String(x) => Ok(Value::Bool(!x.is_empty())), - Value::Array(vec) => Ok(Value::Bool(!vec.is_empty())), + Value::Array(_, vec) => Ok(Value::Bool(!vec.is_empty())), x => Err(RuntimeError::NoOverloadForTypes("bool".into(), vec![x])), }, ParseTree::StringCast(x) => Ok(Value::String(format!("{}", self.exec(x, locals)?))), @@ -352,6 +369,18 @@ where Ok(Value::Nil) } } + ParseTree::FunctionDeclaration(func, scope) => { + let locals = locals.to_mut(); + let name = func.name.clone().unwrap(); + + if locals.contains_key(&name) { + Err(RuntimeError::ImmutableError(name.clone())) + } else { + locals.insert(name, Object::Function(func)); + self.exec(scope, &mut Cow::Borrowed(&locals)) + } + } + ParseTree::LambdaDefinition(func) => Ok(Value::Function(func)), } } } diff --git a/src/lib.rs b/src/lib.rs index 5ef7c0d..3020752 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,11 +3,12 @@ mod parser; mod executor; use executor::{Executor, RuntimeError}; -use parser::Parser; +use parser::{ParseTree, Parser}; use tokenizer::Tokenizer; use std::fmt::Display; use std::io::{Write, Read, BufRead}; +use std::fmt; #[derive(Clone, Debug, PartialEq)] pub enum Type { @@ -15,21 +16,21 @@ pub enum Type { Int, Bool, String, - Array, - _Function(Box, Vec), + Array(Box), + Function(FunctionType), Nil, Any, } impl Display for Type { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", match self { Self::Float => "Float".into(), Self::Int => "Int".into(), Self::Bool => "Bool".into(), Self::String => "String".into(), - Self::Array => format!("Array"), - Self::_Function(r, _) => format!("Function -> {}", *r), + Self::Array(t) => format!("[{t}]"), + Self::Function(r) => format!("{r}"), Self::Nil => "Nil".into(), Self::Any => "Any".into(), }) @@ -43,7 +44,8 @@ pub enum Value { Int(i64), Bool(bool), String(String), - Array(Vec), + Array(Type, Vec), + Function(Function), Nil, } @@ -54,8 +56,9 @@ impl Value { Self::Int(_) => Type::Int, Self::Bool(_) => Type::Bool, Self::String(_) => Type::String, - Self::Array(_) => Type::Array, + Self::Array(t, _) => Type::Array(Box::new(t.clone())), Self::Nil => Type::Nil, + Self::Function(f) => Type::Function(f.t.clone()), } } } @@ -67,16 +70,54 @@ impl Display for Value { Self::Int(x) => write!(f, "{x}"), Self::Bool(x) => write!(f, "{}", if *x { "true" } else { "false" }), Self::String(x) => write!(f, "\"{x}\""), - Self::Array(v) => write!(f, "[{}]", v.iter().map(|x| format!("{x}")).collect::>().join(" ")), + Self::Array(_t, v) => write!(f, "[{}]", v.iter().map(|x| format!("{x}")).collect::>().join(" ")), + Self::Function(func) => { + if let Some(name) = &func.name { + write!(f, "Function({}, {}, {})", name, func.t.0, func.t.1.iter().map(|x| format!("{x}")).collect::>().join(", ")) + } else { + write!(f, "{}", func.t) + } + } Self::Nil => write!(f, "nil"), } } } -#[derive(Clone, Debug)] -pub(crate) struct FunctionDeclaration { - _name: String, - args: Vec, +#[derive(Clone, Debug, PartialEq)] +pub struct FunctionType(Box, Vec); + +impl Display for FunctionType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Function({}, {})", self.0, self.1.iter().map(|x| format!("{x}")).collect::>().join(", ")) + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct Function { + name: Option, + t: FunctionType, + arg_names: Option>, + body: Option>, +} + +impl Function { + pub fn lambda(t: FunctionType, arg_names: Vec, body: Option>) -> Self { + Self { + name: None, + t, + arg_names: Some(arg_names), + body + } + } + + pub fn named(name: &str, t: FunctionType, arg_names: Option>, body: Option>) -> Self { + Self { + name: Some(name.to_string()), + t, + arg_names, + body + } + } } pub struct Runtime<'a, R: BufRead> { diff --git a/src/parser.rs b/src/parser.rs index 3f7cfc4..f61d288 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,4 +1,4 @@ -use super::{Type, Value, FunctionDeclaration}; +use super::{Value, Type, Function, FunctionType}; use super::tokenizer::{Token, TokenizeError, Op}; use std::error; @@ -15,7 +15,9 @@ pub enum ParseError { FunctionUndefined(String), VariableUndefined(String), UnmatchedArrayClose, + UnwantedToken(Token), TokenizeError(TokenizeError), + ImmutableError(String), } impl Display for ParseError { @@ -28,14 +30,16 @@ impl Display for ParseError { ParseError::VariableUndefined(name) => write!(f, "Undefined variable `{name}`"), ParseError::NoInput => write!(f, "No input given"), ParseError::UnmatchedArrayClose => write!(f, "there was an unmatched array closing operator `]`"), - ParseError::TokenizeError(e) => write!(f, "{e}"), + ParseError::TokenizeError(e) => write!(f, "Tokenizer Error: {e}"), + ParseError::ImmutableError(i) => write!(f, "attempt to redeclare {i} met with force"), + ParseError::UnwantedToken(_t) => write!(f, "unexpected token"), } } } impl error::Error for ParseError {} -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub(crate) enum ParseTree { // Mathematical Operators Add(Box, Box), @@ -59,7 +63,9 @@ pub(crate) enum ParseTree { // Defining Objects Equ(String, Box, Box), LazyEqu(String, Box, Box), - FunctionDefinition(String, Vec, Box, Box), + FunctionDefinition(Function, Box), + FunctionDeclaration(Function, Box), + LambdaDefinition(Function), // Functional Operations Compose(Box, Box), @@ -111,8 +117,8 @@ macro_rules! three_arg { impl ParseTree { fn parse( tokens: &mut I, - globals: &HashMap, - locals: &mut Cow>) -> Result + globals: &HashMap, + locals: &mut Cow>) -> Result where I: Iterator>, { @@ -124,8 +130,8 @@ impl ParseTree { // If it is found to be a function, get its argument count. // During parsing, we only keep track of function definitions // so that we know how many arguments it takes - if let Some(decl) = locals.clone().get(&ident).or(globals.clone().get(&ident)) { - let args = decl.args.iter() + if let Some(f) = locals.clone().get(&ident).or(globals.clone().get(&ident)) { + let args = f.t.1.iter() .map(|_| ParseTree::parse(tokens, globals, locals)).collect::, ParseError>>()?; Ok(ParseTree::FunctionCall(ident.clone(), args)) @@ -162,35 +168,59 @@ impl ParseTree { Err(ParseError::InvalidIdentifier) } } - Op::FunctionDeclare(nargs) => { + Op::FunctionDefine(nargs) => { let token = tokens.next() .ok_or(ParseError::UnexpectedEndInput)? .map_err(|e| ParseError::TokenizeError(e))?; - if let Token::Identifier(ident) = token { - let args: Vec = tokens.take(nargs) - .map(|token| match token { - Ok(Token::Identifier(ident)) => Ok(ident), - Ok(_) => Err(ParseError::InvalidIdentifier), - Err(e) => Err(ParseError::TokenizeError(e)), - }) - .collect::, ParseError>>()?; + if let Token::Identifier(ident) = token { + let args: Vec = tokens.take(nargs) + .map(|token| match token { + Ok(Token::Identifier(ident)) => Ok(ident), + Ok(_) => Err(ParseError::InvalidIdentifier), + Err(e) => Err(ParseError::TokenizeError(e)), + }) + .collect::, ParseError>>()?; + + let f = if locals.contains_key(&ident) { + let locals = locals.to_mut(); + let f = locals.get(&ident).unwrap(); + let f = f.clone(); + + // iterate over f's types and push them + for (t, name) in std::iter::zip(f.t.1.clone(), args.clone()) { + match t { + Type::Function(finner) => { + locals.insert(name.clone(), Function::named(&name, finner, None, None)); + } + _ => (), + } + } + + Function::named( + &ident, + f.t.clone(), + Some(args), + Some(Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&locals))?))) + } else { + let f = Function::named( + &ident, + FunctionType(Box::new(Type::Any), args.iter().map(|_| Type::Any).collect()), + Some(args), + Some(Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&locals))?))); let locals = locals.to_mut(); - locals.insert(ident.clone(), FunctionDeclaration { - _name: ident.clone(), - args: args.clone(), - }); + locals.insert(ident.clone(), f.clone()); - Ok(ParseTree::FunctionDefinition( - ident, - args, - Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&*locals))?), - Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&*locals))?))) - } else { - Err(ParseError::InvalidIdentifier) - } + f + }; + + Ok(ParseTree::FunctionDefinition(f, + Box::new(ParseTree::parse(tokens, globals, locals)?))) + } else { + Err(ParseError::InvalidIdentifier) + } } Op::Compose => two_arg!(Compose, tokens, globals, locals), Op::Id => one_arg!(Id, tokens, globals, locals), @@ -232,25 +262,119 @@ impl ParseTree { .collect::>()?; let tree = trees.into_iter().fold( - ParseTree::Constant(Value::Array(vec![])), + ParseTree::Constant(Value::Array(Type::Any, vec![])), |acc, x| ParseTree::Add(Box::new(acc), Box::new(x.clone())), ); Ok(tree) } - Op::Empty => Ok(ParseTree::Constant(Value::Array(vec![]))), + Op::Empty => Ok(ParseTree::Constant(Value::Array(Type::Any, vec![]))), Op::CloseArray => Err(ParseError::UnmatchedArrayClose), Op::NotEqualTo => two_arg!(NotEqualTo, tokens, globals, locals), Op::And => two_arg!(And, tokens, globals, locals), Op::Or => two_arg!(Or, tokens, globals, locals), + Op::FunctionDeclare(arg_count) => { + let name = match tokens.next() + .ok_or(ParseError::UnexpectedEndInput)? + .map_err(|e| ParseError::TokenizeError(e))? + { + Token::Identifier(x) => x, + _ => return Err(ParseError::InvalidIdentifier), + }; + + let args: Vec = (0..arg_count) + .map(|_| Self::parse_type(tokens)) + .collect::>()?; + + let rett = Self::parse_type(tokens)?; + + if locals.contains_key(&name) { + println!("{name} already found: {locals:?}"); + return Err(ParseError::ImmutableError(name.clone())); + } + + let f = Function::named( + &name, + FunctionType(Box::new(rett), args), + None, + None); + + let locals = locals.to_mut(); + + locals.insert(name, f.clone()); + + Ok(ParseTree::FunctionDeclaration( + f, + Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&*locals))?))) + } + Op::LambdaDefine(arg_count) => { + let args: Vec = tokens.take(arg_count) + .map(|token| match token { + Ok(Token::Identifier(ident)) => Ok(ident), + Ok(_) => Err(ParseError::InvalidIdentifier), + Err(e) => Err(ParseError::TokenizeError(e)), + }) + .collect::, ParseError>>()?; + + Ok(ParseTree::LambdaDefinition( + Function::lambda( + FunctionType(Box::new(Type::Any), args.clone().into_iter().map(|_| Type::Any).collect()), + args, + Some(Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&*locals))?))))) + } + Op::NonCall => { + let ident = match tokens.next().ok_or(ParseError::UnexpectedEndInput)? + .map_err(|e| ParseError::TokenizeError(e))? + { + Token::Identifier(x) => x, + _ => return Err(ParseError::InvalidIdentifier), + }; + + if let Some(f) = locals.clone().get(&ident).or(globals.clone().get(&ident)).cloned() { + Ok(ParseTree::Constant(Value::Function(f))) + } else { + Err(ParseError::FunctionUndefined(ident.clone())) + } + } } } + t => Err(ParseError::UnwantedToken(t)), } }, Some(Err(e)) => Err(ParseError::TokenizeError(e)), None => Err(ParseError::NoInput), } } + + fn parse_type(tokens: &mut I) -> Result + where + I: Iterator>, + { + match tokens.next() { + Some(Ok(Token::Type(t))) => Ok(t), + Some(Ok(Token::Operator(Op::FunctionDefine(n)))) => { + let args: Vec = (0..n) + .map(|_| Self::parse_type(tokens)) + .collect::>()?; + + let rett = Self::parse_type(tokens)?; + + Ok(Type::Function(FunctionType(Box::new(rett), args.clone()))) + }, + Some(Ok(Token::Operator(Op::OpenArray))) => { + let t = Self::parse_type(tokens)?; + let _ = match tokens.next() { + Some(Ok(Token::Operator(Op::CloseArray))) => (), + _ => return Err(ParseError::UnmatchedArrayClose), + }; + + Ok(Type::Array(Box::new(t))) + } + Some(Ok(t)) => Err(ParseError::UnwantedToken(t.clone())), + Some(Err(e)) => Err(ParseError::TokenizeError(e)), + None => Err(ParseError::UnexpectedEndInput), + } + } } /// Parses input tokens and produces ParseTrees for an Executor @@ -260,8 +384,8 @@ pub(crate) struct Parser>> { // These are used to keep track of functions in the current context // by the parser. otherwise the parser would have no way to tell // if the program `* a b 12` is supposed to be ((* a b) (12)) or (* (a b) 12) - globals: HashMap, - locals: HashMap, + globals: HashMap, + locals: HashMap, } impl>> Parser { @@ -273,7 +397,7 @@ impl>> Parser { } } - pub fn globals(self, globals: HashMap) -> Self { + pub fn globals(self, globals: HashMap) -> Self { Self { tokens: self.tokens, globals, @@ -281,7 +405,7 @@ impl>> Parser { } } - pub fn locals(self, locals: HashMap) -> Self { + pub fn locals(self, locals: HashMap) -> Self { Self { tokens: self.tokens, globals: self.globals, diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 6e4ce48..69e1adb 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -2,6 +2,8 @@ use std::iter::Peekable; use std::{error, io}; use std::collections::{VecDeque, HashMap}; +use crate::Type; + use super::Value; use std::fmt::{Display, Formatter}; use std::io::{BufRead, Cursor}; @@ -49,7 +51,9 @@ pub(crate) enum Op { Equ, Mod, LazyEqu, + FunctionDefine(usize), FunctionDeclare(usize), + LambdaDefine(usize), Compose, Id, If, @@ -71,6 +75,7 @@ pub(crate) enum Op { Empty, And, Or, + NonCall, } #[derive(Debug, Clone)] @@ -78,6 +83,7 @@ pub(crate) enum Token { Identifier(String), Operator(Op), Constant(Value), + Type(Type), } fn get_dot_count>(s: &mut Peekable) -> Option { @@ -111,6 +117,13 @@ impl Token { "print" => Ok(Token::Operator(Op::Print)), "empty" => Ok(Token::Operator(Op::Empty)), + // Types + "Any" => Ok(Token::Type(Type::Any)), + "Int" => Ok(Token::Type(Type::Int)), + "Float" => Ok(Token::Type(Type::Float)), + "Bool" => Ok(Token::Type(Type::Bool)), + "String" => Ok(Token::Type(Type::String)), + // then identifiers and numbers _ => { if identifier.is_match(s) { @@ -156,7 +169,9 @@ impl Tokenizer { ("%", Op::Mod), ("=", Op::Equ), (".", Op::LazyEqu), - (":", Op::FunctionDeclare(1)), + (":", Op::FunctionDefine(1)), + ("?:", Op::FunctionDeclare(1)), + (";", Op::LambdaDefine(1)), ("~", Op::Compose), (",", Op::Id), ("?", Op::If), @@ -172,6 +187,7 @@ impl Tokenizer { ("!", Op::Not), ("&&", Op::And), ("||", Op::Or), + ("'", Op::NonCall), ]); let c = if let Some(c) = iter.next() { @@ -256,6 +272,26 @@ impl Tokenizer { }; Op::FunctionDeclare(n + count) } + Op::FunctionDefine(n) => { + let count = match get_dot_count(&mut iter) { + Some(count) => count, + None => { + self.tokens.push_back(Err(TokenizeError::InvalidDynamicOperator(token))); + return; + } + }; + Op::FunctionDefine(n + count) + } + Op::LambdaDefine(n) => { + let count = match get_dot_count(&mut iter) { + Some(count) => count, + None => { + self.tokens.push_back(Err(TokenizeError::InvalidDynamicOperator(token))); + return; + } + }; + Op::LambdaDefine(n + count) + } op => op.clone(), }))); @@ -326,4 +362,26 @@ impl std::iter::Iterator for Tokenizer { Err(e) => Some(Err(TokenizeError::IO(e))), } } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use crate::parser::{Parser, ParseTree, ParseError}; + + use super::*; + + #[test] + fn uwu() { + let program = "?:. apply : Any Any Any Any :. apply f x f x : id x x apply ; x id x 12"; + + let tokens: Vec = Tokenizer::from_str(program).unwrap().collect::>().unwrap(); + + println!("{tokens:?}"); + + let trees: Result, ParseError> = Parser::new(tokens.into_iter().map(|x| Ok(x))).collect(); + + println!("{trees:?}"); + } } \ No newline at end of file From 7dc88b8fb064fa4c3faa6068fa6593ab40dd56f5 Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Wed, 16 Oct 2024 15:30:38 -0400 Subject: [PATCH 18/48] new function parsing --- src/executor.rs | 42 ++------ src/parser.rs | 245 ++++++++++++++++++++++++----------------------- src/tokenizer.rs | 65 ++++++++++--- 3 files changed, 184 insertions(+), 168 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 2ff5b1b..05aadd0 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -232,31 +232,11 @@ where } }, ParseTree::FunctionDefinition(func, scope) => { - let ident = func.name.clone().unwrap(); - - let existing = locals.get(&ident) - .or(self.globals.get(&ident)); - - match existing { - Some(Object::Function(f)) => { - if f.body.is_some() || f.arg_names.is_some() { - return Err(RuntimeError::ImmutableError(ident.clone())); - } - - let new_func = Function::named(func.name.unwrap().as_str(), func.t.clone(), func.arg_names.clone(), func.body.clone()); + let locals = locals.to_mut(); - let locals = locals.to_mut(); - locals.insert(ident.clone(), Object::Function(new_func)); + locals.insert(func.name.clone().unwrap(), Object::Function(func)); - self.exec(scope, &mut Cow::Borrowed(&locals)) - } - Some(Object::Variable(_)) => Err(RuntimeError::ImmutableError(ident.clone())), - None => { - let locals = locals.to_mut(); - locals.insert(ident.clone(), Object::Function(func)); - self.exec(scope, &mut Cow::Borrowed(&locals)) - } - } + self.exec(scope, &mut Cow::Borrowed(&locals)) }, ParseTree::Compose(x, y) => { self.exec(x, locals)?; @@ -296,6 +276,9 @@ where Some(Object::Function(f)) => { let locals = locals.to_mut(); + assert!(f.arg_names.is_some()); + assert!(f.body.is_some()); + for ((t, name), tree) in std::iter::zip(std::iter::zip(f.t.1, f.arg_names.unwrap()), args) { let v = self.exec(Box::new(tree), &mut Cow::Borrowed(locals))?; @@ -369,18 +352,7 @@ where Ok(Value::Nil) } } - ParseTree::FunctionDeclaration(func, scope) => { - let locals = locals.to_mut(); - let name = func.name.clone().unwrap(); - - if locals.contains_key(&name) { - Err(RuntimeError::ImmutableError(name.clone())) - } else { - locals.insert(name, Object::Function(func)); - self.exec(scope, &mut Cow::Borrowed(&locals)) - } - } - ParseTree::LambdaDefinition(func) => Ok(Value::Function(func)), + ParseTree::LambdaDefinition(func) => todo!(), } } } diff --git a/src/parser.rs b/src/parser.rs index f61d288..2376996 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -5,13 +5,14 @@ use std::error; use std::collections::HashMap; use std::fmt::Display; use std::borrow::Cow; +use std::iter::Peekable; #[derive(Debug)] pub enum ParseError { NoInput, UnexpectedEndInput, IdentifierUndefined(String), - InvalidIdentifier, + InvalidIdentifier(Token), FunctionUndefined(String), VariableUndefined(String), UnmatchedArrayClose, @@ -25,7 +26,7 @@ impl Display for ParseError { match self { ParseError::UnexpectedEndInput => write!(f, "Input ended unexpectedly"), ParseError::IdentifierUndefined(name) => write!(f, "Undefined variable `{name}`"), - ParseError::InvalidIdentifier => write!(f, "Invalid identifier"), + ParseError::InvalidIdentifier(t) => write!(f, "Invalid identifier `{t:?}`"), ParseError::FunctionUndefined(name) => write!(f, "Undefined function `{name}`"), ParseError::VariableUndefined(name) => write!(f, "Undefined variable `{name}`"), ParseError::NoInput => write!(f, "No input given"), @@ -64,7 +65,6 @@ pub(crate) enum ParseTree { Equ(String, Box, Box), LazyEqu(String, Box, Box), FunctionDefinition(Function, Box), - FunctionDeclaration(Function, Box), LambdaDefinition(Function), // Functional Operations @@ -116,7 +116,7 @@ macro_rules! three_arg { impl ParseTree { fn parse( - tokens: &mut I, + tokens: &mut Peekable, globals: &HashMap, locals: &mut Cow>) -> Result where @@ -165,63 +165,32 @@ impl ParseTree { _ => panic!("Operator literally changed under your nose"), } } else { - Err(ParseError::InvalidIdentifier) + Err(ParseError::InvalidIdentifier(token)) } } - Op::FunctionDefine(nargs) => { - let token = tokens.next() - .ok_or(ParseError::UnexpectedEndInput)? - .map_err(|e| ParseError::TokenizeError(e))?; + Op::FunctionDefine(arg_count) => { + let mut f = ParseTree::parse_function(tokens, arg_count)?; - if let Token::Identifier(ident) = token { - let args: Vec = tokens.take(nargs) - .map(|token| match token { - Ok(Token::Identifier(ident)) => Ok(ident), - Ok(_) => Err(ParseError::InvalidIdentifier), - Err(e) => Err(ParseError::TokenizeError(e)), - }) - .collect::, ParseError>>()?; + assert!(f.arg_names.is_some()); + assert!(f.name.is_some()); + assert!(f.body.is_none()); - let f = if locals.contains_key(&ident) { - let locals = locals.to_mut(); - let f = locals.get(&ident).unwrap(); - let f = f.clone(); - - // iterate over f's types and push them - for (t, name) in std::iter::zip(f.t.1.clone(), args.clone()) { - match t { - Type::Function(finner) => { - locals.insert(name.clone(), Function::named(&name, finner, None, None)); - } - _ => (), - } - } - - Function::named( - &ident, - f.t.clone(), - Some(args), - Some(Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&locals))?))) - } else { - let f = Function::named( - &ident, - FunctionType(Box::new(Type::Any), args.iter().map(|_| Type::Any).collect()), - Some(args), - Some(Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&locals))?))); - - let locals = locals.to_mut(); - - locals.insert(ident.clone(), f.clone()); - - f - }; - - Ok(ParseTree::FunctionDefinition(f, - Box::new(ParseTree::parse(tokens, globals, locals)?))) - } else { - Err(ParseError::InvalidIdentifier) + if locals.contains_key(&f.name.clone().unwrap()) { + return Err(ParseError::ImmutableError(f.name.unwrap())); } - } + + let locals = locals.to_mut(); + + // recursion requires that f's prototype is present in locals + locals.insert(f.name.clone().unwrap(), f.clone()); + + f.body = Some(Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&locals))?)); + assert!(f.body.is_some()); + + println!("{:?} = {:?}", f.name, f); + + Ok(ParseTree::FunctionDefinition(f, Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&locals))?))) + }, Op::Compose => two_arg!(Compose, tokens, globals, locals), Op::Id => one_arg!(Id, tokens, globals, locals), Op::If => two_arg!(If, tokens, globals, locals), @@ -273,69 +242,9 @@ impl ParseTree { Op::NotEqualTo => two_arg!(NotEqualTo, tokens, globals, locals), Op::And => two_arg!(And, tokens, globals, locals), Op::Or => two_arg!(Or, tokens, globals, locals), - Op::FunctionDeclare(arg_count) => { - let name = match tokens.next() - .ok_or(ParseError::UnexpectedEndInput)? - .map_err(|e| ParseError::TokenizeError(e))? - { - Token::Identifier(x) => x, - _ => return Err(ParseError::InvalidIdentifier), - }; - - let args: Vec = (0..arg_count) - .map(|_| Self::parse_type(tokens)) - .collect::>()?; - - let rett = Self::parse_type(tokens)?; - - if locals.contains_key(&name) { - println!("{name} already found: {locals:?}"); - return Err(ParseError::ImmutableError(name.clone())); - } - - let f = Function::named( - &name, - FunctionType(Box::new(rett), args), - None, - None); - - let locals = locals.to_mut(); - - locals.insert(name, f.clone()); - - Ok(ParseTree::FunctionDeclaration( - f, - Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&*locals))?))) - } - Op::LambdaDefine(arg_count) => { - let args: Vec = tokens.take(arg_count) - .map(|token| match token { - Ok(Token::Identifier(ident)) => Ok(ident), - Ok(_) => Err(ParseError::InvalidIdentifier), - Err(e) => Err(ParseError::TokenizeError(e)), - }) - .collect::, ParseError>>()?; - - Ok(ParseTree::LambdaDefinition( - Function::lambda( - FunctionType(Box::new(Type::Any), args.clone().into_iter().map(|_| Type::Any).collect()), - args, - Some(Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&*locals))?))))) - } - Op::NonCall => { - let ident = match tokens.next().ok_or(ParseError::UnexpectedEndInput)? - .map_err(|e| ParseError::TokenizeError(e))? - { - Token::Identifier(x) => x, - _ => return Err(ParseError::InvalidIdentifier), - }; - - if let Some(f) = locals.clone().get(&ident).or(globals.clone().get(&ident)).cloned() { - Ok(ParseTree::Constant(Value::Function(f))) - } else { - Err(ParseError::FunctionUndefined(ident.clone())) - } - } + Op::LambdaDefine(_arg_count) => todo!(), + Op::NonCall => todo!(), + op => Err(ParseError::UnwantedToken(Token::Operator(op))), } } t => Err(ParseError::UnwantedToken(t)), @@ -346,6 +255,92 @@ impl ParseTree { } } + fn parse_function(tokens: &mut Peekable, arg_count: usize) -> Result + where + I: Iterator>, + { + let name = Self::get_identifier(tokens.next())?; + let (t, args) = Self::parse_function_declaration(tokens, arg_count)?; + + Ok(Function::named(&name, t, Some(args), None)) + } + + fn parse_function_declaration(tokens: &mut Peekable, arg_count: usize) -> Result<(FunctionType, Vec), ParseError> + where + I: Iterator> + { + let args: Vec<(Type, String)> = (0..arg_count) + .map(|_| Self::parse_function_declaration_parameter(tokens)) + .collect::>()?; + + + let (types, names): (Vec<_>, Vec<_>) = args.into_iter().unzip(); + let mut ret = Type::Any; + + if let Some(t) = tokens.next_if(|x| matches!(x, Ok(Token::Operator(Op::Arrow)))) + { + if let Err(e) = t { + return Err(ParseError::TokenizeError(e)); + } + + ret = Self::parse_type(tokens)?; + } + + Ok((FunctionType(Box::new(ret), types), names)) + } + + fn parse_function_declaration_parameter(mut tokens: &mut Peekable) -> Result<(Type, String), ParseError> + where + I: Iterator> + { + match tokens.next() { + // untyped variable + Some(Ok(Token::Identifier(x))) => Ok((Type::Any, x)), + + // typed variable + Some(Ok(Token::Operator(Op::TypeDeclaration))) => { + let name = Self::get_identifier(tokens.next())?; + let t = Self::parse_type(&mut tokens)?; + + Ok((t, name)) + } + + // untyped function (all args Any, return type Any) + Some(Ok(Token::Operator(Op::FunctionDefine(n)))) => { + let name = Self::get_identifier(tokens.next())?; + let args = (0..n).map(|_| Type::Any).collect(); + + Ok((Type::Function(FunctionType(Box::new(Type::Any), args)), name)) + } + + // typed function + Some(Ok(Token::Operator(Op::FunctionDeclare(n)))) => { + let name = Self::get_identifier(tokens.next())?; + let args = (0..n).map(|_| Self::parse_type(&mut tokens)).collect::>()?; + let mut ret = Type::Any; + + // this is annoying + // inside of the next_if closure, we already can know that its an error + // and return it, but we cannot return out of a closure + if let Some(t) = tokens.next_if(|x| matches!(x, Ok(Token::Operator(Op::Arrow)))) + { + // so we just check for an error here. this is the only reason t exists. + if let Err(e) = t { + return Err(ParseError::TokenizeError(e)); + } + + ret = Self::parse_type(&mut tokens)?; + } + + Ok((Type::Function(FunctionType(Box::new(ret), args)), name)) + } + + Some(Ok(t)) => Err(ParseError::UnwantedToken(t)), + Some(Err(e)) => Err(ParseError::TokenizeError(e)), + None => Err(ParseError::UnexpectedEndInput), + } + } + fn parse_type(tokens: &mut I) -> Result where I: Iterator>, @@ -375,6 +370,16 @@ impl ParseTree { None => Err(ParseError::UnexpectedEndInput), } } + + fn get_identifier(t: Option>) -> Result { + match t.ok_or(ParseError::UnexpectedEndInput)? + .map_err(|e| ParseError::TokenizeError(e)) + { + Ok(Token::Identifier(ident)) => Ok(ident), + Ok(t) => Err(ParseError::InvalidIdentifier(t)), + Err(e) => Err(e), + } + } } /// Parses input tokens and produces ParseTrees for an Executor @@ -418,7 +423,7 @@ impl>> Iterator for Parser { type Item = Result; fn next(&mut self) -> Option { - let tree = ParseTree::parse(&mut self.tokens, &self.globals, &mut Cow::Borrowed(&self.locals)); + let tree = ParseTree::parse(&mut self.tokens.by_ref().peekable(), &self.globals, &mut Cow::Borrowed(&self.locals)); match tree { Ok(tree) => Some(Ok(tree)), diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 69e1adb..57b97d8 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -41,7 +41,7 @@ impl Display for TokenizeError { impl error::Error for TokenizeError {} -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub(crate) enum Op { Add, Sub, @@ -51,9 +51,11 @@ pub(crate) enum Op { Equ, Mod, LazyEqu, + TypeDeclaration, FunctionDefine(usize), FunctionDeclare(usize), LambdaDefine(usize), + Arrow, Compose, Id, If, @@ -169,9 +171,11 @@ impl Tokenizer { ("%", Op::Mod), ("=", Op::Equ), (".", Op::LazyEqu), + ("?.", Op::TypeDeclaration), (":", Op::FunctionDefine(1)), ("?:", Op::FunctionDeclare(1)), (";", Op::LambdaDefine(1)), + ("->", Op::Arrow), ("~", Op::Compose), (",", Op::Id), ("?", Op::If), @@ -262,16 +266,6 @@ impl Tokenizer { if let Some(op) = possible.get(token.as_str()) { self.tokens.push_back(Ok(Token::Operator(match op { // special handling for "dynamic" operators - Op::FunctionDeclare(n) => { - let count = match get_dot_count(&mut iter) { - Some(count) => count, - None => { - self.tokens.push_back(Err(TokenizeError::InvalidDynamicOperator(token))); - return; - } - }; - Op::FunctionDeclare(n + count) - } Op::FunctionDefine(n) => { let count = match get_dot_count(&mut iter) { Some(count) => count, @@ -282,6 +276,16 @@ impl Tokenizer { }; Op::FunctionDefine(n + count) } + Op::FunctionDeclare(n) => { + let count = match get_dot_count(&mut iter) { + Some(count) => count, + None => { + self.tokens.push_back(Err(TokenizeError::InvalidDynamicOperator(token))); + return; + } + }; + Op::FunctionDeclare(n + count) + } Op::LambdaDefine(n) => { let count = match get_dot_count(&mut iter) { Some(count) => count, @@ -314,7 +318,42 @@ impl Tokenizer { Some(c) => c, None => { // at this point, token must be in the hashmap possible, otherwise it wouldnt have any matches - self.tokens.push_back(Ok(Token::Operator(possible.get(token.as_str()).unwrap().clone()))); + self.tokens.push_back(Ok(Token::Operator(match possible.get(token.as_str()).unwrap() { + // special handling for "dynamic" operators + Op::FunctionDefine(n) => { + let count = match get_dot_count(&mut iter) { + Some(count) => count, + None => { + self.tokens.push_back(Err(TokenizeError::InvalidDynamicOperator(token))); + return; + } + }; + println!("{n} + {count}"); + + Op::FunctionDefine(n + count) + } + Op::FunctionDeclare(n) => { + let count = match get_dot_count(&mut iter) { + Some(count) => count, + None => { + self.tokens.push_back(Err(TokenizeError::InvalidDynamicOperator(token))); + return; + } + }; + Op::FunctionDeclare(n + count) + } + Op::LambdaDefine(n) => { + let count = match get_dot_count(&mut iter) { + Some(count) => count, + None => { + self.tokens.push_back(Err(TokenizeError::InvalidDynamicOperator(token))); + return; + } + }; + Op::LambdaDefine(n + count) + } + op => op.clone(), + }))); break; } }; @@ -374,7 +413,7 @@ mod tests { #[test] fn uwu() { - let program = "?:. apply : Any Any Any Any :. apply f x f x : id x x apply ; x id x 12"; + let program = ":. add x y + x y"; let tokens: Vec = Tokenizer::from_str(program).unwrap().collect::>().unwrap(); From 283f5cdb41b4a988ea361d7981c9bbe588ff45cf Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Wed, 16 Oct 2024 16:34:36 -0400 Subject: [PATCH 19/48] re-implement lambdas --- src/executor.rs | 2 +- src/tokenizer.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 05aadd0..7a7815a 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -352,7 +352,7 @@ where Ok(Value::Nil) } } - ParseTree::LambdaDefinition(func) => todo!(), + ParseTree::LambdaDefinition(func) => Ok(Value::Function(func)), } } } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 57b97d8..3dbb045 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -413,7 +413,7 @@ mod tests { #[test] fn uwu() { - let program = ":. add x y + x y"; + let program = ": id ?. x Any -> Any x id 5"; let tokens: Vec = Tokenizer::from_str(program).unwrap().collect::>().unwrap(); From 7b01ac45fc682f5b247e7ef37a01c4905b3eb8e4 Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Wed, 16 Oct 2024 16:38:28 -0400 Subject: [PATCH 20/48] re-implement lambdas --- src/parser.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/parser.rs b/src/parser.rs index 2376996..caa68b7 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -242,7 +242,13 @@ impl ParseTree { Op::NotEqualTo => two_arg!(NotEqualTo, tokens, globals, locals), Op::And => two_arg!(And, tokens, globals, locals), Op::Or => two_arg!(Or, tokens, globals, locals), - Op::LambdaDefine(_arg_count) => todo!(), + Op::LambdaDefine(arg_count) => { + let mut f = ParseTree::parse_function(tokens, arg_count)?; + + f.body = Some(Box::new(ParseTree::parse(tokens, globals, locals)?)); + + Ok(ParseTree::LambdaDefinition(f)) + } Op::NonCall => todo!(), op => Err(ParseError::UnwantedToken(Token::Operator(op))), } From 5eb647d448f09a228e45f57349a110a6d7cafb42 Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Wed, 16 Oct 2024 16:44:12 -0400 Subject: [PATCH 21/48] insert arguments into local space --- src/parser.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/parser.rs b/src/parser.rs index caa68b7..7f37b00 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -184,6 +184,16 @@ impl ParseTree { // recursion requires that f's prototype is present in locals locals.insert(f.name.clone().unwrap(), f.clone()); + // we also need any function aprameters in local scope + for (name, t) in std::iter::zip(f.arg_names.clone().unwrap(), f.t.1.clone()) { + match t { + Type::Function(t) => { + locals.insert(name.clone(), Function::named(&name, t, None, None)); + } + _ => (), + } + } + f.body = Some(Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&locals))?)); assert!(f.body.is_some()); From d0661512c907a10899f13229caf8c5760b1b7036 Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Wed, 16 Oct 2024 16:50:57 -0400 Subject: [PATCH 22/48] correctly parse lambdas --- src/parser.rs | 14 ++++++++++---- src/tokenizer.rs | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 7f37b00..cd9899d 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -33,7 +33,7 @@ impl Display for ParseError { ParseError::UnmatchedArrayClose => write!(f, "there was an unmatched array closing operator `]`"), ParseError::TokenizeError(e) => write!(f, "Tokenizer Error: {e}"), ParseError::ImmutableError(i) => write!(f, "attempt to redeclare {i} met with force"), - ParseError::UnwantedToken(_t) => write!(f, "unexpected token"), + ParseError::UnwantedToken(t) => write!(f, "unexpected token {t:?}"), } } } @@ -197,8 +197,6 @@ impl ParseTree { f.body = Some(Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&locals))?)); assert!(f.body.is_some()); - println!("{:?} = {:?}", f.name, f); - Ok(ParseTree::FunctionDefinition(f, Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&locals))?))) }, Op::Compose => two_arg!(Compose, tokens, globals, locals), @@ -253,7 +251,7 @@ impl ParseTree { Op::And => two_arg!(And, tokens, globals, locals), Op::Or => two_arg!(Or, tokens, globals, locals), Op::LambdaDefine(arg_count) => { - let mut f = ParseTree::parse_function(tokens, arg_count)?; + let mut f = ParseTree::parse_lambda(tokens, arg_count)?; f.body = Some(Box::new(ParseTree::parse(tokens, globals, locals)?)); @@ -271,6 +269,14 @@ impl ParseTree { } } + fn parse_lambda(tokens: &mut Peekable, arg_count: usize) -> Result + where + I: Iterator>, + { + let (t, args) = Self::parse_function_declaration(tokens, arg_count)?; + Ok(Function::lambda(t, args, None)) + } + fn parse_function(tokens: &mut Peekable, arg_count: usize) -> Result where I: Iterator>, diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 3dbb045..e24b6bf 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -413,7 +413,7 @@ mod tests { #[test] fn uwu() { - let program = ": id ?. x Any -> Any x id 5"; + let program = ":. apply : f x f x apply ; x ** x 2 10"; let tokens: Vec = Tokenizer::from_str(program).unwrap().collect::>().unwrap(); From 329af77ff40fb4ea702ffa37862cc293285a70fc Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Wed, 16 Oct 2024 17:04:25 -0400 Subject: [PATCH 23/48] add noexec operator to pass named functions to functions --- src/executor.rs | 18 ++++++++++++++++++ src/parser.rs | 6 +++++- src/tokenizer.rs | 1 - 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 7a7815a..415ea5c 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -353,6 +353,24 @@ where } } ParseTree::LambdaDefinition(func) => Ok(Value::Function(func)), + ParseTree::NonCall(name) => { + let locals = locals.to_mut(); + + let func = locals.get(&name).ok_or(RuntimeError::FunctionUndefined(name.clone())).cloned()?; + + match func { + Object::Function(func) => Ok(Value::Function(func.clone())), + Object::Variable(var) => match var { + Evaluation::Computed(value) => Ok(value.clone()), + Evaluation::Uncomputed(tree) => { + let v = self.exec(tree, &mut Cow::Borrowed(&locals))?; + locals.insert(name, Object::Variable(Evaluation::Computed(v.clone()))); + + Ok(v) + } + } + } + } } } } diff --git a/src/parser.rs b/src/parser.rs index cd9899d..6189376 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -79,6 +79,7 @@ pub(crate) enum ParseTree { FunctionCall(String, Vec), Variable(String), Constant(Value), + NonCall(String), // Type Casts IntCast(Box), @@ -257,7 +258,10 @@ impl ParseTree { Ok(ParseTree::LambdaDefinition(f)) } - Op::NonCall => todo!(), + Op::NonCall => { + let name = Self::get_identifier(tokens.next())?; + Ok(ParseTree::NonCall(name)) + }, op => Err(ParseError::UnwantedToken(Token::Operator(op))), } } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index e24b6bf..db5f24c 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -328,7 +328,6 @@ impl Tokenizer { return; } }; - println!("{n} + {count}"); Op::FunctionDefine(n + count) } From 8c7993bacc9259dfd628844774c3b2ee780b083f Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Wed, 16 Oct 2024 17:10:27 -0400 Subject: [PATCH 24/48] make any match any type --- src/lib.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 3020752..628ec7f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,7 +10,7 @@ use std::fmt::Display; use std::io::{Write, Read, BufRead}; use std::fmt; -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub enum Type { Float, Int, @@ -22,6 +22,18 @@ pub enum Type { Any, } +impl PartialEq for Type { + fn eq(&self, other: &Type) -> bool { + match (self, other) { + (Self::Any, _) => true, + (_, Self::Any) => true, + (Self::Array(l0), Self::Array(r0)) => l0 == r0, + (Self::Function(l0), Self::Function(r0)) => l0 == r0, + _ => core::mem::discriminant(self) == core::mem::discriminant(other), + } + } +} + impl Display for Type { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", match self { From 9f6444668708a72b247b89e3b741840b0691d1ca Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Thu, 17 Oct 2024 00:32:17 -0400 Subject: [PATCH 25/48] basic array functions --- src/executor.rs | 44 +++++++++++++++++++++++++++++++++++++++++--- src/lib.rs | 4 ++-- src/parser.rs | 8 ++++++++ src/tokenizer.rs | 10 +++++++++- 4 files changed, 60 insertions(+), 6 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 415ea5c..a904c05 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -18,6 +18,7 @@ pub enum RuntimeError { NotAVariable(String), ParseFail(String, Type), TypeError(Type, Type), + EmptyArray, IO(io::Error), } @@ -26,7 +27,7 @@ impl Display for RuntimeError { match self { Self::ParseError(e) => write!(f, "Parser Error: {e}"), Self::NoOverloadForTypes(op, values) - => write!(f, "No overload of `{op}` exists for the operands `[{}]`", + => write!(f, "No overload of `{op}` exists for the operands `{}`", values.iter().map(|x| format!("{}({x})", x.get_type())).collect::>().join(", ")), Self::ImmutableError(ident) => write!(f, "`{ident}` already exists and cannot be redefined"), Self::VariableUndefined(ident) => write!(f, "variable `{ident}` was not defined"), @@ -36,6 +37,7 @@ impl Display for RuntimeError { Self::ParseFail(s, t) => write!(f, "`\"{s}\"` couldn't be parsed into {}", t), Self::IO(e) => write!(f, "{e}"), Self::TypeError(left, right) => write!(f, "expected type `{left}` but got type `{right}`"), + Self::EmptyArray => write!(f, "attempt to access element from an empty array"), } } } @@ -112,8 +114,28 @@ where (Value::Int(x), Value::Float(y)) => Ok(Value::Float(x as f64 + y)), (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x + y)), (Value::String(x), Value::String(y)) => Ok(Value::String(format!("{x}{y}"))), - (Value::Array(_, x), y) => Ok(Value::Array(Type::Any, [x, vec![y]].concat())), - (x, Value::Array(_, y)) => Ok(Value::Array(Type::Any, [vec![x], y].concat())), + (Value::Array(t, x), y) => { + let ytype = y.get_type(); + + if t != ytype { + return Err(RuntimeError::TypeError(t, ytype)); + } + + // NOTE: use y's type instead of the arrays type. + // an `empty` array has Any type, but any value will have a fixed type. + // this converts the empty array into a typed array. + Ok(Value::Array(ytype, [x, vec![y]].concat())) + }, + (x, Value::Array(t, y)) => { + let xtype = x.get_type(); + + if t != xtype { + return Err(RuntimeError::TypeError(t, xtype)); + } + + // NOTE: read above + Ok(Value::Array(xtype, [vec![x], y].concat())) + }, (x, y) => Err(RuntimeError::NoOverloadForTypes("+".into(), vec![x, y])) }, ParseTree::Sub(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { @@ -371,6 +393,22 @@ where } } } + ParseTree::Head(x) => match self.exec(x, locals)? { + Value::Array(_, x) => Ok(x.first().ok_or(RuntimeError::EmptyArray)?.clone()), + t => Err(RuntimeError::NoOverloadForTypes("head".into(), vec![t])) + }, + ParseTree::Tail(x) => match self.exec(x, locals)? { + Value::Array(t, x) => Ok(Value::Array(t, if x.len() > 0 { x[1..].to_vec() } else { vec![] })), + t => Err(RuntimeError::NoOverloadForTypes("tail".into(), vec![t])) + }, + ParseTree::Init(x) => match self.exec(x, locals)? { + Value::Array(t, x) => Ok(Value::Array(t, if x.len() > 0 { x[..x.len() - 1].to_vec() } else { vec![] })), + t => Err(RuntimeError::NoOverloadForTypes("init".into(), vec![t])) + }, + ParseTree::Fini(x) => match self.exec(x, locals)? { + Value::Array(_, x) => Ok(x.last().ok_or(RuntimeError::EmptyArray)?.clone()), + t => Err(RuntimeError::NoOverloadForTypes("fini".into(), vec![t])) + }, } } } diff --git a/src/lib.rs b/src/lib.rs index 628ec7f..15de2c8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -113,7 +113,7 @@ pub struct Function { } impl Function { - pub fn lambda(t: FunctionType, arg_names: Vec, body: Option>) -> Self { + fn lambda(t: FunctionType, arg_names: Vec, body: Option>) -> Self { Self { name: None, t, @@ -122,7 +122,7 @@ impl Function { } } - pub fn named(name: &str, t: FunctionType, arg_names: Option>, body: Option>) -> Self { + fn named(name: &str, t: FunctionType, arg_names: Option>, body: Option>) -> Self { Self { name: Some(name.to_string()), t, diff --git a/src/parser.rs b/src/parser.rs index 6189376..338928b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -70,6 +70,10 @@ pub(crate) enum ParseTree { // Functional Operations Compose(Box, Box), Id(Box), + Head(Box), + Tail(Box), + Init(Box), + Fini(Box), // Branching If(Box, Box), @@ -262,6 +266,10 @@ impl ParseTree { let name = Self::get_identifier(tokens.next())?; Ok(ParseTree::NonCall(name)) }, + Op::Head => one_arg!(Head, tokens, globals, locals), + Op::Tail => one_arg!(Tail, tokens, globals, locals), + Op::Init => one_arg!(Init, tokens, globals, locals), + Op::Fini => one_arg!(Fini, tokens, globals, locals), op => Err(ParseError::UnwantedToken(Token::Operator(op))), } } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index db5f24c..00a0353 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -78,6 +78,10 @@ pub(crate) enum Op { And, Or, NonCall, + Head, + Tail, + Init, + Fini, } #[derive(Debug, Clone)] @@ -118,6 +122,10 @@ impl Token { "string" => Ok(Token::Operator(Op::StringCast)), "print" => Ok(Token::Operator(Op::Print)), "empty" => Ok(Token::Operator(Op::Empty)), + "head" => Ok(Token::Operator(Op::Head)), + "tail" => Ok(Token::Operator(Op::Tail)), + "init" => Ok(Token::Operator(Op::Init)), + "fini" => Ok(Token::Operator(Op::Fini)), // Types "Any" => Ok(Token::Type(Type::Any)), @@ -412,7 +420,7 @@ mod tests { #[test] fn uwu() { - let program = ":. apply : f x f x apply ; x ** x 2 10"; + let program = ":. map ?: f Any -> Any ?. x [Any] -> [Any] ?? bool x + f head x map 'f tail x empty map ;x ** x 2 [1 2 3 4 5]"; let tokens: Vec = Tokenizer::from_str(program).unwrap().collect::>().unwrap(); From c1c5d0201143ec5eed256dbd3c2e9749048aa23f Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Thu, 17 Oct 2024 00:48:54 -0400 Subject: [PATCH 26/48] fix lambda parsing --- src/parser.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/parser.rs b/src/parser.rs index 338928b..c9c1572 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -258,7 +258,18 @@ impl ParseTree { Op::LambdaDefine(arg_count) => { let mut f = ParseTree::parse_lambda(tokens, arg_count)?; - f.body = Some(Box::new(ParseTree::parse(tokens, globals, locals)?)); + let locals = locals.to_mut(); + + for (name, t) in std::iter::zip(f.arg_names.clone().unwrap(), f.t.1.clone()) { + match t { + Type::Function(t) => { + locals.insert(name.clone(), Function::named(&name, t, None, None)); + } + _ => (), + } + } + + f.body = Some(Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&locals))?)); Ok(ParseTree::LambdaDefinition(f)) } From 3c163150a2c80df61095d0cb5acbe76060807097 Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Thu, 17 Oct 2024 01:59:42 -0400 Subject: [PATCH 27/48] move repl to main.rs --- examples/repl.rs => src/main.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/repl.rs => src/main.rs (100%) diff --git a/examples/repl.rs b/src/main.rs similarity index 100% rename from examples/repl.rs rename to src/main.rs From d1c7a5dbbd288182132b848af384a1c2214f50da Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Thu, 17 Oct 2024 01:59:55 -0400 Subject: [PATCH 28/48] add README.md --- README.md | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..82531ff --- /dev/null +++ b/README.md @@ -0,0 +1,206 @@ +# Lamm + +A small, functional programming language. + +# Syntax + +Lamm uses [Polish Notation](https://en.wikipedia.org/wiki/Polish_notation). +That means that instead of writing `5 + 6`, you would instead write `+ 5 6`. + +Since we're here, we might as well cover some operators. + +## Math Operators + +``` ++ 5 6 # => 11 +- 5 6 # => -1 +* 5 6 # => 30 +/ 5 6 # => 0 (integer division) +** 5 6 # => 15625 +% 6 5 # => 1 +``` + +There is no order of operations to worry about, you essentially write your code in the order it should be evaluated in. + +## Variables + +Variables are **constant** in Lamm, there is no mutation. Here are some examples of defining variables. + +``` += pi 3.1415926 # immediately evaluated +. sqrt2 ** 2 0.5 # lazy evaluated +``` + +Variables are **scoped** in Lamm, meaning they only exist in the single expression that they are defined for. That means that the following code is an **error**. + +``` += pi 3.1415926 += r 16 +* pi ** r 2 # OK += deg 60 +* deg / pi 360.0 # ERROR: `pi` was undefined +``` + +Scope in Lamm consists of a single expression, such as `sqrt + ** a 2 ** b 2`. So then, what do I do when I need a variable for more than a single expression? There are multiple solutions depending on your needs. + +### Multi-Statement Expression (half implemented) + +You can create a multi-statement expression using either `()` syntax or the `~` operator, which `()` is simple syntactic sugar for. In these, only the value of the last expression is returned, the rest get ignored. This is the perfect place to put stateful function calls. + +``` +( + print "Hello, world!" + print "Auf Wiedersehen!" +) +``` + +### Global Scope (unimplemented) + +You can introduce a variable to global scope using the `export` builtin function. + +``` +# A very useful constant += pi 3.1415926 +export ["pi"] + +# Some more useful constants += e 2.71828 += phi 1.6180339887 +export ["e" "phi"] +``` + +## Functions + +All functions in Lamm are **scoped** similarly to variables. Functions are declared using the `:` operator, which can be extended with more `:` and `.` characters to let Lamm know how many arguments the function takes. + +``` +: inc x + x 1 +inc 24 # => 25 + +:. pythag a b sqrt + ** a 2.0 ** b 2.0 +pythag 3 4 # => 5 + +:::::. ten'args a b c d e f g h i j + [a b c d e f g h i j] +``` + +The parameter types and return type of functions can be declared using a special syntax unique to function and lambda definitions. + +``` +# Takes an x of `Any` type +: inc x + x 1 + inc 12 # => 13 + +# Takes an x of `Int` and returns an `Int` +: inc ?. x Int -> Int + x 1 + inc 9 # => 10 +``` + +The `?.` operator is unique to function declarations and is used to specify the type of an argument. There are also first class functions, here is the syntax for it. + +``` +# Applies a function to any value +:. apply : f x f x + apply 'sqrt 9 # => 3 + +# Applies a function f which maps an Int to an Int to x +:. apply'int ?: f Int -> Int ?. x Int -> Int f x + apply'int 'sqrt 36 # => 6 +``` + +The `:` operator inside of a function prototype tells Lamm that this argument must be a function where every argument and it's return type are all `Any`. This means that `: f` is essentially syntactic sugar for `?: f Any -> Any`. Also, in order to pass a function to a function, you must use the `'` operator, which tells Lamm not to call the function. + +And off course, `:` and `?:` in function prototypes can also be extended depending on the number of arguments the function must take. + +### Branching + +Lamm has the following boolean expressions + +``` +== 1 2 # => false +!= 1 2 # => true +> 1 2 # => false +< 1 2 # => true +>= 1 2 # => false +<= 1 2 # => true +!true # => false +true && false # => false +true || false # => true +``` + +These can be used inside of `?` (if) and `??` (if-else) statements. + +``` +. n 12 + ?? < 12 10 + print "n is less than 10" + print "n is greater than 10" +``` + +An `?` if statement where it's condition is false simply returns `nil`, as do `print` and other functions without a return value. `?` is mostly useful inside of blocks. + +``` +: times'twelve ?. n Int -> Int ( + ? == n 0 + print "n is 0" + + * n 12 +) +``` + +### Arrays + +Lamm offers a few fundamental array operations. + +``` ++ 1 [2 3 4] # => [1 2 3 4] ++ [1 2 3] 4 # => [1 2 3 4] ++ [1 2] [3 4] # => [1 2 3 4] +head [1 2 3 4] # => 1 +tail [1 2 3 4] # => [2 3 4] +init [1 2 3 4] # => [1 2 3] +fini [1 2 3 4] # => 4 +bool [1 2 3] # => true +bool empty # => false +``` + +Using these, you can build a lot of fundamental functional paradigm functions. + +``` +:. map ?: f Any -> Any ?. x [Any] -> [Any] + ?? bool x + + f head x map 'f tail x + empty +map ;y ** y 2 [1 2 3 4 5 6 7 8 9 10] # => [1 4 9 16 25 36 49 64 81 100] + +:: iterate : f i count -> [Any] + ?? > count 0 + + i iterate 'f f i - count 1 + empty +iterate ;x + 1 x 0 10 # => [0 1 2 3 4 5 6 7 8 9] + +:. take ?. n Int ?. x [Any] -> [Any] + ?? > n 0 + + head x take - n 1 tail x + empty +take 3 [1 2 3 4 5] # => [1 2 3] + +:. take'while : pred Any -> Bool ?. x [Any] -> [Any] + ?? bool x + ?? pred head x + + head x take'while 'pred tail x + empty + empty +take'while ;y < y 10 [1 3 5 7 9 11 13 15 16] # => [1 3 5 7 9] +``` + +# Lambdas + +Lambdas are created using the `;` operator, and they are always passed as a value, so no `'` is necessary. + + +``` +map ;x * x 12 [1 2 3] # => [12 24 36] +``` + +They follow the same prototype syntax as regular functions, with the notable lack of an identifier. \ No newline at end of file From 9bd21e01f720b1876e1f28865f8cba172f840efc Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Thu, 17 Oct 2024 02:01:13 -0400 Subject: [PATCH 29/48] update README.md --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 82531ff..6dd4cfc 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,8 @@ Variables are **scoped** in Lamm, meaning they only exist in the single expressi * deg / pi 360.0 # ERROR: `pi` was undefined ``` +## Scope + Scope in Lamm consists of a single expression, such as `sqrt + ** a 2 ** b 2`. So then, what do I do when I need a variable for more than a single expression? There are multiple solutions depending on your needs. ### Multi-Statement Expression (half implemented) @@ -112,7 +114,7 @@ The `:` operator inside of a function prototype tells Lamm that this argument mu And off course, `:` and `?:` in function prototypes can also be extended depending on the number of arguments the function must take. -### Branching +## Branching Lamm has the following boolean expressions @@ -148,7 +150,7 @@ An `?` if statement where it's condition is false simply returns `nil`, as do `p ) ``` -### Arrays +## Arrays Lamm offers a few fundamental array operations. @@ -194,7 +196,7 @@ take 3 [1 2 3 4 5] # => [1 2 3] take'while ;y < y 10 [1 3 5 7 9 11 13 15 16] # => [1 3 5 7 9] ``` -# Lambdas +## Lambdas Lambdas are created using the `;` operator, and they are always passed as a value, so no `'` is necessary. From 10e1be04d2ba08820ad30a9f27b01e702971c5dd Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Thu, 17 Oct 2024 02:03:28 -0400 Subject: [PATCH 30/48] update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6dd4cfc..e94a288 100644 --- a/README.md +++ b/README.md @@ -50,8 +50,8 @@ Scope in Lamm consists of a single expression, such as `sqrt + ** a 2 ** b 2`. S You can create a multi-statement expression using either `()` syntax or the `~` operator, which `()` is simple syntactic sugar for. In these, only the value of the last expression is returned, the rest get ignored. This is the perfect place to put stateful function calls. ``` -( - print "Hello, world!" +. x 12 ( + print + "my favorite number is " x print "Auf Wiedersehen!" ) ``` From b116007dd58112bdef9e18917471ee51a8c261a3 Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Thu, 17 Oct 2024 02:04:10 -0400 Subject: [PATCH 31/48] update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e94a288..738ae82 100644 --- a/README.md +++ b/README.md @@ -77,10 +77,10 @@ All functions in Lamm are **scoped** similarly to variables. Functions are decla ``` : inc x + x 1 -inc 24 # => 25 + inc 24 # => 25 :. pythag a b sqrt + ** a 2.0 ** b 2.0 -pythag 3 4 # => 5 + pythag 3 4 # => 5 :::::. ten'args a b c d e f g h i j [a b c d e f g h i j] From facd7d6680fcf6fb2e42fac99511fc94ed7aa61d Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Thu, 17 Oct 2024 02:07:59 -0400 Subject: [PATCH 32/48] update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 738ae82..cda0e57 100644 --- a/README.md +++ b/README.md @@ -51,8 +51,8 @@ You can create a multi-statement expression using either `()` syntax or the `~` ``` . x 12 ( - print + "my favorite number is " x - print "Auf Wiedersehen!" + print + "My favorite number is " string x + print + "Auf Wiedersehen! Ich werde aber meine Lieblingsnummer " + string x " vermissen." ) ``` From f651c62c5c8203f72df2c4957783e4bbff432dbd Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Thu, 17 Oct 2024 02:10:06 -0400 Subject: [PATCH 33/48] update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cda0e57..e00cdd9 100644 --- a/README.md +++ b/README.md @@ -162,7 +162,7 @@ head [1 2 3 4] # => 1 tail [1 2 3 4] # => [2 3 4] init [1 2 3 4] # => [1 2 3] fini [1 2 3 4] # => 4 -bool [1 2 3] # => true +bool [1 2 3 4] # => true bool empty # => false ``` From 6f5081568a7debc229e1023dc36e3f04c726ec2a Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Thu, 17 Oct 2024 02:13:26 -0400 Subject: [PATCH 34/48] update README.md --- README.md | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index e00cdd9..622bc65 100644 --- a/README.md +++ b/README.md @@ -170,29 +170,27 @@ Using these, you can build a lot of fundamental functional paradigm functions. ``` :. map ?: f Any -> Any ?. x [Any] -> [Any] - ?? bool x - + f head x map 'f tail x - empty + ?? bool x + + f head x map 'f tail x + empty map ;y ** y 2 [1 2 3 4 5 6 7 8 9 10] # => [1 4 9 16 25 36 49 64 81 100] :: iterate : f i count -> [Any] - ?? > count 0 - + i iterate 'f f i - count 1 - empty + ?? > count 0 + + i iterate 'f f i - count 1 + empty iterate ;x + 1 x 0 10 # => [0 1 2 3 4 5 6 7 8 9] :. take ?. n Int ?. x [Any] -> [Any] - ?? > n 0 - + head x take - n 1 tail x - empty + ?? > n 0 + + head x take - n 1 tail x + empty take 3 [1 2 3 4 5] # => [1 2 3] :. take'while : pred Any -> Bool ?. x [Any] -> [Any] - ?? bool x - ?? pred head x - + head x take'while 'pred tail x - empty - empty + ?? && bool x pred head x + + head x take'while 'pred tail x + empty take'while ;y < y 10 [1 3 5 7 9 11 13 15 16] # => [1 3 5 7 9] ``` From bf7ce174cdd25827bb1c58b3020a1b808ae17bcc Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Thu, 17 Oct 2024 14:07:08 -0400 Subject: [PATCH 35/48] print now prints strings without parens --- src/executor.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/executor.rs b/src/executor.rs index a904c05..d4d4528 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1,4 +1,4 @@ -use super::{Value, Type, Function, FunctionType}; +use super::{Value, Type, Function}; use super::parser::{ParseTree, ParseError}; use std::collections::HashMap; @@ -369,6 +369,10 @@ where }, ParseTree::StringCast(x) => Ok(Value::String(format!("{}", self.exec(x, locals)?))), ParseTree::Print(x) => match self.exec(x, locals)? { + Value::String(s) => { + writeln!(self.stdout, "{s}").map_err(|e| RuntimeError::IO(e))?; + Ok(Value::Nil) + } x => { writeln!(self.stdout, "{x}").map_err(|e| RuntimeError::IO(e))?; Ok(Value::Nil) From d1082cb159ba66f2923cfa60e907a713727da856 Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Thu, 17 Oct 2024 15:53:00 -0400 Subject: [PATCH 36/48] fix recursive functions --- src/executor.rs | 64 ++++++++++++------------ src/lib.rs | 24 ++++++++- src/parser.rs | 126 +++++++++++++++++++++++++++--------------------- 3 files changed, 126 insertions(+), 88 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index d4d4528..0c22c1a 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1,4 +1,4 @@ -use super::{Value, Type, Function}; +use super::{Value, Type, Object, Evaluation, Function}; use super::parser::{ParseTree, ParseError}; use std::collections::HashMap; @@ -44,22 +44,6 @@ impl Display for RuntimeError { impl Error for RuntimeError {} -#[derive(Clone, Debug)] -enum Evaluation { - // at this point, it's type is set in stone - Computed(Value), - - // at this point, it's type is unknown, and may contradict a variable's type - // or not match the expected value of the expression, this is a runtime error - Uncomputed(Box), -} - -#[derive(Clone, Debug)] -enum Object { - Variable(Evaluation), - Function(Function), -} - /// Executes an input of ParseTrees pub struct Executor<'a, I> where @@ -114,6 +98,13 @@ where (Value::Int(x), Value::Float(y)) => Ok(Value::Float(x as f64 + y)), (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x + y)), (Value::String(x), Value::String(y)) => Ok(Value::String(format!("{x}{y}"))), + (Value::Array(xtype, x), Value::Array(ytype, y)) => { + if xtype != ytype { + return Err(RuntimeError::TypeError(xtype, ytype)); + } + + Ok(Value::Array(xtype, [x, y].concat())) + }, (Value::Array(t, x), y) => { let ytype = y.get_type(); @@ -296,25 +287,38 @@ where match obj { Some(Object::Function(f)) => { - let locals = locals.to_mut(); - assert!(f.arg_names.is_some()); - assert!(f.body.is_some()); - for ((t, name), tree) in std::iter::zip(std::iter::zip(f.t.1, f.arg_names.unwrap()), args) { - let v = self.exec(Box::new(tree), &mut Cow::Borrowed(locals))?; + let loc = std::iter::zip(std::iter::zip(f.t.1.clone(), f.arg_names.clone().unwrap()), args) + .map(|((t, name), tree)| { + let v = self.exec(Box::new(tree), locals)?; - if v.get_type() != t && t != Type::Any { - return Err(RuntimeError::TypeError(t, v.get_type())); - } + if t != v.get_type() { + return Err(RuntimeError::TypeError(t, v.get_type())); + } - locals.insert(name.clone(), match v { - Value::Function(func) => Object::Function(func), - _ => Object::Variable(Evaluation::Computed(v)) - }); + match v { + Value::Function(f) => Ok((Object::Function(f), name)), + v => Ok((Object::Variable(Evaluation::Computed(v)), name)), + } + }).collect::, RuntimeError>>()?; + + let mut locals = f.locals.clone(); + + for (obj, name) in loc.into_iter() { + locals.insert(name, obj); } - self.exec(f.body.unwrap(), &mut Cow::Borrowed(&locals)) + // the parser previously placed a copy of this function with the same name and type + // into it's locals, however it doesn't have a body. This would cause a + // panic later when attempting to execute the function during recursive calls. + // we fix this by replacing it with a *complete* copy of the function. + // also only do this if the function has a name in the first place, otherwise it panics with lambdas. + if let Some(name) = f.name.clone() { + locals.insert(name, Object::Function(f.clone())); + } + + self.exec(f.body.unwrap(), &mut Cow::Borrowed(&Box::new(locals))) } _ => Err(RuntimeError::FunctionUndefined(ident.clone())) } diff --git a/src/lib.rs b/src/lib.rs index 15de2c8..da3caa3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,7 @@ use executor::{Executor, RuntimeError}; use parser::{ParseTree, Parser}; use tokenizer::Tokenizer; +use std::collections::HashMap; use std::fmt::Display; use std::io::{Write, Read, BufRead}; use std::fmt; @@ -104,28 +105,47 @@ impl Display for FunctionType { } } +#[derive(Clone, Debug, PartialEq)] +enum Evaluation { + // at this point, it's type is set in stone + Computed(Value), + + // at this point, it's type is unknown, and may contradict a variable's type + // or not match the expected value of the expression, this is a runtime error + Uncomputed(Box), +} + +#[derive(Clone, Debug, PartialEq)] +enum Object { + Variable(Evaluation), + Function(Function), +} + #[derive(Clone, Debug, PartialEq)] pub struct Function { name: Option, t: FunctionType, + locals: HashMap, arg_names: Option>, body: Option>, } impl Function { - fn lambda(t: FunctionType, arg_names: Vec, body: Option>) -> Self { + fn lambda(t: FunctionType, arg_names: Vec, locals: HashMap, body: Option>) -> Self { Self { name: None, t, + locals, arg_names: Some(arg_names), body } } - fn named(name: &str, t: FunctionType, arg_names: Option>, body: Option>) -> Self { + fn named(name: &str, t: FunctionType, arg_names: Option>, locals: HashMap, body: Option>) -> Self { Self { name: Some(name.to_string()), t, + locals, arg_names, body } diff --git a/src/parser.rs b/src/parser.rs index c9c1572..3b5646b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,4 +1,6 @@ -use super::{Value, Type, Function, FunctionType}; +use crate::Evaluation; + +use super::{Value, Type, Object, Function, FunctionType}; use super::tokenizer::{Token, TokenizeError, Op}; use std::error; @@ -13,8 +15,6 @@ pub enum ParseError { UnexpectedEndInput, IdentifierUndefined(String), InvalidIdentifier(Token), - FunctionUndefined(String), - VariableUndefined(String), UnmatchedArrayClose, UnwantedToken(Token), TokenizeError(TokenizeError), @@ -25,10 +25,8 @@ impl Display for ParseError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { ParseError::UnexpectedEndInput => write!(f, "Input ended unexpectedly"), - ParseError::IdentifierUndefined(name) => write!(f, "Undefined variable `{name}`"), + ParseError::IdentifierUndefined(name) => write!(f, "Undefined identifier `{name}`"), ParseError::InvalidIdentifier(t) => write!(f, "Invalid identifier `{t:?}`"), - ParseError::FunctionUndefined(name) => write!(f, "Undefined function `{name}`"), - ParseError::VariableUndefined(name) => write!(f, "Undefined variable `{name}`"), ParseError::NoInput => write!(f, "No input given"), ParseError::UnmatchedArrayClose => write!(f, "there was an unmatched array closing operator `]`"), ParseError::TokenizeError(e) => write!(f, "Tokenizer Error: {e}"), @@ -122,8 +120,8 @@ macro_rules! three_arg { impl ParseTree { fn parse( tokens: &mut Peekable, - globals: &HashMap, - locals: &mut Cow>) -> Result + globals: &HashMap, + locals: &mut Cow>) -> Result where I: Iterator>, { @@ -132,16 +130,18 @@ impl ParseTree { match token { Token::Constant(c) => Ok(Self::Constant(c)), Token::Identifier(ident) => { - // If it is found to be a function, get its argument count. - // During parsing, we only keep track of function definitions - // so that we know how many arguments it takes - if let Some(f) = locals.clone().get(&ident).or(globals.clone().get(&ident)) { - let args = f.t.1.iter() - .map(|_| ParseTree::parse(tokens, globals, locals)).collect::, ParseError>>()?; - - Ok(ParseTree::FunctionCall(ident.clone(), args)) + if let Some(obj) = locals.clone().get(&ident).or(globals.clone().get(&ident)) { + match obj { + Object::Function(f) => { + let args = f.t.1.iter() + .map(|_| ParseTree::parse(tokens, globals, locals)).collect::, ParseError>>()?; + + Ok(ParseTree::FunctionCall(ident, args)) + } + Object::Variable(e) => Ok(ParseTree::Variable(ident)), + } } else { - Ok(ParseTree::Variable(ident.clone())) + Err(ParseError::IdentifierUndefined(ident)) } } Token::Operator(op) => { @@ -174,33 +174,38 @@ impl ParseTree { } } Op::FunctionDefine(arg_count) => { - let mut f = ParseTree::parse_function(tokens, arg_count)?; + let f = { + let mut f = ParseTree::parse_function(tokens, arg_count)?; - assert!(f.arg_names.is_some()); - assert!(f.name.is_some()); - assert!(f.body.is_none()); + if locals.contains_key(&f.name.clone().unwrap()) { + return Err(ParseError::ImmutableError(f.name.unwrap())); + } - if locals.contains_key(&f.name.clone().unwrap()) { - return Err(ParseError::ImmutableError(f.name.unwrap())); - } + f.locals = locals.to_mut().clone(); + + // recursion requires that f's prototype is present in locals + f.locals.insert(f.name.clone().unwrap(), Object::Function(f.clone())); + + // we also need any function parameters in local scope + for (name, t) in std::iter::zip(f.arg_names.clone().unwrap(), f.t.1.clone()) { + match t { + Type::Function(t) => { + f.locals.insert(name.clone(), Object::Function(Function::named(&name, t, None, HashMap::new(), None))); + } + _ => { + // the value isn't important, just that the identifier is there + f.locals.insert(name.clone(), Object::Variable(Evaluation::Computed(Value::Nil))); + } + } + } + + f.body = Some(Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&f.locals))?)); + + f + }; let locals = locals.to_mut(); - - // recursion requires that f's prototype is present in locals - locals.insert(f.name.clone().unwrap(), f.clone()); - - // we also need any function aprameters in local scope - for (name, t) in std::iter::zip(f.arg_names.clone().unwrap(), f.t.1.clone()) { - match t { - Type::Function(t) => { - locals.insert(name.clone(), Function::named(&name, t, None, None)); - } - _ => (), - } - } - - f.body = Some(Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&locals))?)); - assert!(f.body.is_some()); + locals.insert(f.name.clone().unwrap(), Object::Function(f.clone())); Ok(ParseTree::FunctionDefinition(f, Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&locals))?))) }, @@ -256,20 +261,29 @@ impl ParseTree { Op::And => two_arg!(And, tokens, globals, locals), Op::Or => two_arg!(Or, tokens, globals, locals), Op::LambdaDefine(arg_count) => { - let mut f = ParseTree::parse_lambda(tokens, arg_count)?; + let f = { + let mut f = ParseTree::parse_lambda(tokens, arg_count)?; - let locals = locals.to_mut(); - - for (name, t) in std::iter::zip(f.arg_names.clone().unwrap(), f.t.1.clone()) { - match t { - Type::Function(t) => { - locals.insert(name.clone(), Function::named(&name, t, None, None)); + let locals = locals.to_mut(); + f.locals = locals.clone(); + + // we need any function parameters in local scope + for (name, t) in std::iter::zip(f.arg_names.clone().unwrap(), f.t.1.clone()) { + match t { + Type::Function(t) => { + f.locals.insert(name.clone(), Object::Function(Function::named(&name, t, None, HashMap::new(), None))); + } + _ => { + // the value isn't important, just that the identifier is there + f.locals.insert(name.clone(), Object::Variable(Evaluation::Computed(Value::Nil))); + } } - _ => (), } - } + + f.body = Some(Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&f.locals))?)); - f.body = Some(Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&locals))?)); + f + }; Ok(ParseTree::LambdaDefinition(f)) } @@ -297,7 +311,7 @@ impl ParseTree { I: Iterator>, { let (t, args) = Self::parse_function_declaration(tokens, arg_count)?; - Ok(Function::lambda(t, args, None)) + Ok(Function::lambda(t, args, HashMap::new(), None)) } fn parse_function(tokens: &mut Peekable, arg_count: usize) -> Result @@ -307,7 +321,7 @@ impl ParseTree { let name = Self::get_identifier(tokens.next())?; let (t, args) = Self::parse_function_declaration(tokens, arg_count)?; - Ok(Function::named(&name, t, Some(args), None)) + Ok(Function::named(&name, t, Some(args), HashMap::new(), None)) } fn parse_function_declaration(tokens: &mut Peekable, arg_count: usize) -> Result<(FunctionType, Vec), ParseError> @@ -434,8 +448,8 @@ pub(crate) struct Parser>> { // These are used to keep track of functions in the current context // by the parser. otherwise the parser would have no way to tell // if the program `* a b 12` is supposed to be ((* a b) (12)) or (* (a b) 12) - globals: HashMap, - locals: HashMap, + globals: HashMap, + locals: HashMap, } impl>> Parser { @@ -447,7 +461,7 @@ impl>> Parser { } } - pub fn globals(self, globals: HashMap) -> Self { + pub fn globals(self, globals: HashMap) -> Self { Self { tokens: self.tokens, globals, @@ -455,7 +469,7 @@ impl>> Parser { } } - pub fn locals(self, locals: HashMap) -> Self { + pub fn locals(self, locals: HashMap) -> Self { Self { tokens: self.tokens, globals: self.globals, From 34569248d33782adacb7b5bce38bbd0c9f522681 Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Thu, 17 Oct 2024 16:05:52 -0400 Subject: [PATCH 37/48] fix parse error on undefined variables --- src/parser.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 3b5646b..5c52f30 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -157,15 +157,21 @@ impl ParseTree { .ok_or(ParseError::UnexpectedEndInput)? .map_err(|e| ParseError::TokenizeError(e))?; + let body = Box::new(ParseTree::parse(tokens, globals, locals)?); + if let Token::Identifier(ident) = token { + let locals = locals.to_mut(); + + locals.insert(ident.clone(), Object::Variable(Evaluation::Computed(Value::Nil))); + match op { - Op::Equ => Ok(ParseTree::Equ(ident.clone(), - Box::new(ParseTree::parse(tokens, globals, locals)?), - Box::new(ParseTree::parse(tokens, globals, locals)?) + Op::Equ => Ok(ParseTree::Equ(ident, + body, + Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&locals))?) )), - Op::LazyEqu => Ok(ParseTree::LazyEqu(ident.clone(), - Box::new(ParseTree::parse(tokens, globals, locals)?), - Box::new(ParseTree::parse(tokens, globals, locals)?) + Op::LazyEqu => Ok(ParseTree::LazyEqu(ident, + body, + Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&locals))?) )), _ => panic!("Operator literally changed under your nose"), } From f2cfb03fa13abed81b38254c763ceff2bdc634b5 Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Fri, 18 Oct 2024 02:21:31 -0400 Subject: [PATCH 38/48] function, object, parser, and executer rewrites --- src/executor.rs | 248 +++++++++++++------------------ src/function.rs | 79 ++++++++++ src/lib.rs | 130 +++++++++-------- src/main.rs | 2 +- src/parser.rs | 370 +++++++++++++++++++---------------------------- src/tokenizer.rs | 26 +--- 6 files changed, 401 insertions(+), 454 deletions(-) create mode 100644 src/function.rs diff --git a/src/executor.rs b/src/executor.rs index 0c22c1a..0e4f4ee 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1,11 +1,10 @@ -use super::{Value, Type, Object, Evaluation, Function}; +use super::{Value, Type, Object}; use super::parser::{ParseTree, ParseError}; use std::collections::HashMap; -use std::borrow::Cow; use std::fmt::Display; use std::error::Error; -use std::io::{self, Read, Write}; +use std::io; #[derive(Debug)] pub enum RuntimeError { @@ -47,52 +46,62 @@ impl Error for RuntimeError {} /// Executes an input of ParseTrees pub struct Executor<'a, I> where - I: Iterator>, + I: Iterator> { - exprs: I, + exprs: &'a mut I, globals: HashMap, - stdout: Box, - stdin: Box, + locals: HashMap, } impl<'a, I> Executor<'a, I> where I: Iterator>, { - pub fn new(exprs: I) -> Self { + pub fn new(exprs: &'a mut I) -> Self { Self { exprs, globals: HashMap::new(), - stdout: Box::new(io::stdout()), - stdin: Box::new(io::stdin()), + locals: HashMap::new(), } } - pub fn stdout(self, writer: impl Write + 'a) -> Self { - Self { - exprs: self.exprs, - globals: self.globals, - stdout: Box::new(writer), - stdin: self.stdin, - } + pub fn globals(mut self, globals: HashMap) -> Self { + self.globals = globals; + self } - pub fn stdin(self, reader: impl Read + 'a) -> Self { - Self { - exprs: self.exprs, - globals: self.globals, - stdout: self.stdout, - stdin: Box::new(reader), - } + pub fn _add_global(mut self, k: String, v: Object) -> Self { + self.globals.insert(k, v); + self } - fn exec( - &mut self, - tree: Box, - locals: &mut Cow>>) -> Result - { + pub fn locals(mut self, locals: HashMap) -> Self { + self.locals = locals; + self + } + + pub fn add_local(mut self, k: String, v: Object) -> Self { + self.locals.insert(k, v); + self + } + + fn _get_object(&self, ident: &String) -> Result<&Object, RuntimeError> { + self.locals.get(ident).or(self.globals.get(ident)) + .ok_or(RuntimeError::VariableUndefined(ident.clone())) + } + + fn get_object_mut(&mut self, ident: &String) -> Result<&mut Object, RuntimeError> { + self.locals.get_mut(ident).or(self.globals.get_mut(ident)) + .ok_or(RuntimeError::VariableUndefined(ident.clone())) + } + + fn variable_exists(&self, ident: &String) -> bool { + self.locals.contains_key(ident) || self.globals.contains_key(ident) + } + + pub fn exec(&mut self, tree: Box) -> Result { match *tree { - ParseTree::Add(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { + ParseTree::Add(x, y) => match (self.exec(x)?, self.exec(y)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x + y)), (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x + y as f64)), (Value::Int(x), Value::Float(y)) => Ok(Value::Float(x as f64 + y)), @@ -129,14 +138,14 @@ where }, (x, y) => Err(RuntimeError::NoOverloadForTypes("+".into(), vec![x, y])) }, - ParseTree::Sub(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { + ParseTree::Sub(x, y) => match (self.exec(x)?, self.exec(y)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x - y)), (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x - y as f64)), (Value::Int(x), Value::Float(y)) => Ok(Value::Float(x as f64 - y)), (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x - y)), (x, y) => Err(RuntimeError::NoOverloadForTypes("-".into(), vec![x, y])) }, - ParseTree::Mul(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { + ParseTree::Mul(x, y) => match (self.exec(x)?, self.exec(y)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x * y)), (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x * y as f64)), (Value::Int(x), Value::Float(y)) => Ok(Value::Float(x as f64 * y)), @@ -144,28 +153,28 @@ where (Value::String(x), Value::Int(y)) => Ok(Value::String(x.repeat(y as usize))), (x, y) => Err(RuntimeError::NoOverloadForTypes("*".into(), vec![x, y])) }, - ParseTree::Div(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { + ParseTree::Div(x, y) => match (self.exec(x)?, self.exec(y)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x / y)), (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x / y as f64)), (Value::Int(x), Value::Float(y)) => Ok(Value::Float(x as f64 / y)), (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x / y)), (x, y) => Err(RuntimeError::NoOverloadForTypes("*".into(), vec![x, y])) }, - ParseTree::Exp(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { + ParseTree::Exp(x, y) => match (self.exec(x)?, self.exec(y)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x.pow(y as u32))), (Value::Int(x), Value::Float(y)) => Ok(Value::Float((x as f64).powf(y))), (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x.powf(y as f64))), (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x.powf(y))), (x, y) => Err(RuntimeError::NoOverloadForTypes("**".into(), vec![x, y])), }, - ParseTree::Mod(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { + ParseTree::Mod(x, y) => match (self.exec(x)?, self.exec(y)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x % y)), (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x % y as f64)), (Value::Int(x), Value::Float(y)) => Ok(Value::Float(x as f64 % y)), (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x % y)), (x, y) => Err(RuntimeError::NoOverloadForTypes("%".into(), vec![x, y])), }, - ParseTree::EqualTo(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { + ParseTree::EqualTo(x, y) => match (self.exec(x)?, self.exec(y)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Bool(x == y)), (Value::Int(x), Value::Float(y)) => Ok(Value::Bool(x as f64 == y)), (Value::Float(x), Value::Int(y)) => Ok(Value::Bool(x == y as f64)), @@ -174,7 +183,7 @@ where (Value::String(x), Value::String(y)) => Ok(Value::Bool(x == y)), (x, y) => Err(RuntimeError::NoOverloadForTypes("==".into(), vec![x, y])), }, - ParseTree::NotEqualTo(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { + ParseTree::NotEqualTo(x, y) => match (self.exec(x)?, self.exec(y)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Bool(x != y)), (Value::Int(x), Value::Float(y)) => Ok(Value::Bool(x as f64 != y)), (Value::Float(x), Value::Int(y)) => Ok(Value::Bool(x != y as f64)), @@ -183,80 +192,83 @@ where (Value::String(x), Value::String(y)) => Ok(Value::Bool(x != y)), (x, y) => Err(RuntimeError::NoOverloadForTypes("!=".into(), vec![x, y])), }, - ParseTree::GreaterThan(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { + ParseTree::GreaterThan(x, y) => match (self.exec(x)?, self.exec(y)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Bool(x > y)), (Value::Int(x), Value::Float(y)) => Ok(Value::Bool(x as f64 > y)), (Value::Float(x), Value::Int(y)) => Ok(Value::Bool(x > y as f64)), (Value::Float(x), Value::Float(y)) => Ok(Value::Bool(x > y)), (x, y) => Err(RuntimeError::NoOverloadForTypes(">".into(), vec![x, y])), }, - ParseTree::GreaterThanOrEqualTo(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { + ParseTree::GreaterThanOrEqualTo(x, y) => match (self.exec(x)?, self.exec(y)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Bool(x >= y)), (Value::Int(x), Value::Float(y)) => Ok(Value::Bool(x as f64 >= y)), (Value::Float(x), Value::Int(y)) => Ok(Value::Bool(x >= y as f64)), (Value::Float(x), Value::Float(y)) => Ok(Value::Bool(x >= y)), (x, y) => Err(RuntimeError::NoOverloadForTypes(">=".into(), vec![x, y])), }, - ParseTree::LessThan(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { + ParseTree::LessThan(x, y) => match (self.exec(x)?, self.exec(y)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Bool(x < y)), (Value::Int(x), Value::Float(y)) => Ok(Value::Bool((x as f64) < y)), (Value::Float(x), Value::Int(y)) => Ok(Value::Bool(x < y as f64)), (Value::Float(x), Value::Float(y)) => Ok(Value::Bool(x < y)), (x, y) => Err(RuntimeError::NoOverloadForTypes("<".into(), vec![x, y])), }, - ParseTree::LessThanOrEqualTo(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { + ParseTree::LessThanOrEqualTo(x, y) => match (self.exec(x)?, self.exec(y)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Bool(x <= y)), (Value::Int(x), Value::Float(y)) => Ok(Value::Bool(x as f64 <= y)), (Value::Float(x), Value::Int(y)) => Ok(Value::Bool(x <= y as f64)), (Value::Float(x), Value::Float(y)) => Ok(Value::Bool(x <= y)), (x, y) => Err(RuntimeError::NoOverloadForTypes("<=".into(), vec![x, y])), }, - ParseTree::Not(x) => match self.exec(x, locals)? { + ParseTree::Not(x) => match self.exec(x)? { Value::Bool(x) => Ok(Value::Bool(!x)), x => Err(RuntimeError::NoOverloadForTypes("not".into(), vec![x])) }, - ParseTree::And(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { + ParseTree::And(x, y) => match (self.exec(x)?, self.exec(y)?) { (Value::Bool(x), Value::Bool(y)) => Ok(Value::Bool(x && y)), (x, y) => Err(RuntimeError::NoOverloadForTypes("&&".into(), vec![x, y])) }, - ParseTree::Or(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { + ParseTree::Or(x, y) => match (self.exec(x)?, self.exec(y)?) { (Value::Bool(x), Value::Bool(y)) => Ok(Value::Bool(x || y)), (x, y) => Err(RuntimeError::NoOverloadForTypes("||".into(), vec![x, y])) }, ParseTree::Equ(ident, body, scope) => { - if self.globals.contains_key(&ident) || locals.contains_key(&ident) { + if self.variable_exists(&ident) { Err(RuntimeError::ImmutableError(ident.clone())) } else { - let locals = locals.to_mut(); - let value = self.exec(body, &mut Cow::Borrowed(&locals))?; - locals.insert(ident.clone(), Object::Variable(Evaluation::Computed(value))); + let value = self.exec(body)?; - self.exec(scope, &mut Cow::Borrowed(&locals)) + Executor::new(self.exprs) + .globals(self.globals.clone()) + .locals(self.locals.clone()) + .add_local(ident, Object::value(value, self.globals.to_owned(), self.locals.to_owned())) + .exec(scope) } }, ParseTree::LazyEqu(ident, body, scope) => { - if self.globals.contains_key(&ident) || locals.contains_key(&ident) { + if self.variable_exists(&ident) { Err(RuntimeError::ImmutableError(ident.clone())) } else { - let locals = locals.to_mut(); - locals.insert(ident.clone(), Object::Variable(Evaluation::Uncomputed(body))); - - self.exec(scope, &mut Cow::Borrowed(&locals)) + Executor::new(self.exprs) + .globals(self.globals.clone()) + .locals(self.locals.clone()) + .add_local(ident, Object::variable(*body, self.globals.to_owned(), self.locals.to_owned())) + .exec(scope) } }, ParseTree::FunctionDefinition(func, scope) => { - let locals = locals.to_mut(); - - locals.insert(func.name.clone().unwrap(), Object::Function(func)); - - self.exec(scope, &mut Cow::Borrowed(&locals)) + Executor::new(self.exprs) + .globals(self.globals.clone()) + .locals(self.locals.clone()) + .add_local(func.name().unwrap().to_string(), Object::function(func, self.globals.clone(), self.locals.clone())) + .exec(scope) }, ParseTree::Compose(x, y) => { - self.exec(x, locals)?; - self.exec(y, locals) + self.exec(x)?; + self.exec(y) }, - ParseTree::Id(x) => self.exec(x, locals), - ParseTree::If(cond, body) => if match self.exec(cond, locals)? { + ParseTree::Id(x) => self.exec(x), + ParseTree::If(cond, body) => if match self.exec(cond)? { Value::Float(f) => f != 0.0, Value::Int(i) => i != 0, Value::Bool(b) => b, @@ -265,85 +277,42 @@ where Value::Nil => false, x => return Err(RuntimeError::NoOverloadForTypes("?".into(), vec![x])), } { - self.exec(body, locals) + self.exec(body) } else { Ok(Value::Nil) }, - ParseTree::IfElse(cond, istrue, isfalse) => if match self.exec(cond, locals)? { + ParseTree::IfElse(cond, istrue, isfalse) => if match self.exec(cond)? { Value::Float(f) => f != 0.0, Value::Int(i) => i != 0, Value::Bool(b) => b, Value::String(s) => !s.is_empty(), Value::Array(_, vec) => !vec.is_empty(), Value::Nil => false, - x => return Err(RuntimeError::NoOverloadForTypes("?".into(), vec![x])), + x => return Err(RuntimeError::NoOverloadForTypes("??".into(), vec![x])), } { - self.exec(istrue, locals) + self.exec(istrue) } else { - self.exec(isfalse, locals) + self.exec(isfalse) }, ParseTree::FunctionCall(ident, args) => { - let obj = locals.get(&ident).or(self.globals.get(&ident)).cloned(); + let args = args.into_iter().map(|x| Object::variable(x, self.globals.clone(), self.locals.clone())).collect(); + let obj = self.get_object_mut(&ident)?; + let v = obj.eval()?; - match obj { - Some(Object::Function(f)) => { - assert!(f.arg_names.is_some()); - - let loc = std::iter::zip(std::iter::zip(f.t.1.clone(), f.arg_names.clone().unwrap()), args) - .map(|((t, name), tree)| { - let v = self.exec(Box::new(tree), locals)?; - - if t != v.get_type() { - return Err(RuntimeError::TypeError(t, v.get_type())); - } - - match v { - Value::Function(f) => Ok((Object::Function(f), name)), - v => Ok((Object::Variable(Evaluation::Computed(v)), name)), - } - }).collect::, RuntimeError>>()?; - - let mut locals = f.locals.clone(); - - for (obj, name) in loc.into_iter() { - locals.insert(name, obj); - } - - // the parser previously placed a copy of this function with the same name and type - // into it's locals, however it doesn't have a body. This would cause a - // panic later when attempting to execute the function during recursive calls. - // we fix this by replacing it with a *complete* copy of the function. - // also only do this if the function has a name in the first place, otherwise it panics with lambdas. - if let Some(name) = f.name.clone() { - locals.insert(name, Object::Function(f.clone())); - } - - self.exec(f.body.unwrap(), &mut Cow::Borrowed(&Box::new(locals))) - } + match v { + Value::Function(mut f) => f.call(obj.globals(), obj.locals(), args), _ => Err(RuntimeError::FunctionUndefined(ident.clone())) } }, ParseTree::Variable(ident) => { - let locals = locals.to_mut(); + let obj = self.get_object_mut(&ident)?; - let obj = locals.get(&ident).or(self.globals.get(&ident)).cloned(); + let v = obj.eval()?; - if let Some(Object::Variable(eval)) = obj { - match eval { - Evaluation::Computed(v) => Ok(v), - Evaluation::Uncomputed(tree) => { - let v = self.exec(tree, &mut Cow::Borrowed(&locals))?; - locals.insert(ident, Object::Variable(Evaluation::Computed(v.clone()))); - - Ok(v) - } - } - } else { - Err(RuntimeError::VariableUndefined(ident.clone())) - } + Ok(v) }, ParseTree::Constant(value) => Ok(value), - ParseTree::IntCast(x) => match self.exec(x, locals)? { + ParseTree::IntCast(x) => match self.exec(x)? { Value::Int(x) => Ok(Value::Int(x)), Value::Float(x) => Ok(Value::Int(x as i64)), Value::Bool(x) => Ok(Value::Int(if x { 1 } else { 0 })), @@ -353,7 +322,7 @@ where } x => Err(RuntimeError::NoOverloadForTypes("int".into(), vec![x])), }, - ParseTree::FloatCast(x) => match self.exec(x, locals)? { + ParseTree::FloatCast(x) => match self.exec(x)? { Value::Int(x) => Ok(Value::Float(x as f64)), Value::Float(x) => Ok(Value::Float(x)), Value::Bool(x) => Ok(Value::Float(if x { 1.0 } else { 0.0 })), @@ -363,7 +332,7 @@ where } x => Err(RuntimeError::NoOverloadForTypes("float".into(), vec![x])), }, - ParseTree::BoolCast(x) => match self.exec(x, locals)? { + ParseTree::BoolCast(x) => match self.exec(x)? { Value::Int(x) => Ok(Value::Bool(x != 0)), Value::Float(x) => Ok(Value::Bool(x != 0.0)), Value::Bool(x) => Ok(Value::Bool(x)), @@ -371,49 +340,38 @@ where Value::Array(_, vec) => Ok(Value::Bool(!vec.is_empty())), x => Err(RuntimeError::NoOverloadForTypes("bool".into(), vec![x])), }, - ParseTree::StringCast(x) => Ok(Value::String(format!("{}", self.exec(x, locals)?))), - ParseTree::Print(x) => match self.exec(x, locals)? { + ParseTree::StringCast(x) => Ok(Value::String(format!("{}", self.exec(x)?))), + ParseTree::Print(x) => match self.exec(x)? { Value::String(s) => { - writeln!(self.stdout, "{s}").map_err(|e| RuntimeError::IO(e))?; + println!("{s}"); Ok(Value::Nil) } x => { - writeln!(self.stdout, "{x}").map_err(|e| RuntimeError::IO(e))?; + println!("{x}"); Ok(Value::Nil) } } ParseTree::LambdaDefinition(func) => Ok(Value::Function(func)), ParseTree::NonCall(name) => { - let locals = locals.to_mut(); + let obj = self.get_object_mut(&name)?; - let func = locals.get(&name).ok_or(RuntimeError::FunctionUndefined(name.clone())).cloned()?; + let v = obj.eval()?; - match func { - Object::Function(func) => Ok(Value::Function(func.clone())), - Object::Variable(var) => match var { - Evaluation::Computed(value) => Ok(value.clone()), - Evaluation::Uncomputed(tree) => { - let v = self.exec(tree, &mut Cow::Borrowed(&locals))?; - locals.insert(name, Object::Variable(Evaluation::Computed(v.clone()))); - - Ok(v) - } - } - } + Ok(v) } - ParseTree::Head(x) => match self.exec(x, locals)? { + ParseTree::Head(x) => match self.exec(x)? { Value::Array(_, x) => Ok(x.first().ok_or(RuntimeError::EmptyArray)?.clone()), t => Err(RuntimeError::NoOverloadForTypes("head".into(), vec![t])) }, - ParseTree::Tail(x) => match self.exec(x, locals)? { + ParseTree::Tail(x) => match self.exec(x)? { Value::Array(t, x) => Ok(Value::Array(t, if x.len() > 0 { x[1..].to_vec() } else { vec![] })), t => Err(RuntimeError::NoOverloadForTypes("tail".into(), vec![t])) }, - ParseTree::Init(x) => match self.exec(x, locals)? { + ParseTree::Init(x) => match self.exec(x)? { Value::Array(t, x) => Ok(Value::Array(t, if x.len() > 0 { x[..x.len() - 1].to_vec() } else { vec![] })), t => Err(RuntimeError::NoOverloadForTypes("init".into(), vec![t])) }, - ParseTree::Fini(x) => match self.exec(x, locals)? { + ParseTree::Fini(x) => match self.exec(x)? { Value::Array(_, x) => Ok(x.last().ok_or(RuntimeError::EmptyArray)?.clone()), t => Err(RuntimeError::NoOverloadForTypes("fini".into(), vec![t])) }, @@ -428,7 +386,7 @@ impl<'a, I: Iterator>> Iterator for Executo let expr = self.exprs.next(); match expr { - Some(Ok(expr)) => Some(self.exec(Box::new(expr), &mut Cow::Borrowed(&Box::new(HashMap::new())))), + Some(Ok(expr)) => Some(self.exec(Box::new(expr))), Some(Err(e)) => Some(Err(RuntimeError::ParseError(e))), None => None, } diff --git a/src/function.rs b/src/function.rs new file mode 100644 index 0000000..ceebbb4 --- /dev/null +++ b/src/function.rs @@ -0,0 +1,79 @@ +use crate::parser::ParseTree; +use crate::executor::{Executor, RuntimeError}; +use crate::{Type, Object, Value}; + +use std::collections::HashMap; +use std::fmt::{self, Display}; + +#[derive(Clone, Debug, PartialEq)] +pub struct FunctionType(pub Box, pub Vec); + +impl Display for FunctionType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Function({}, {})", self.0, self.1.iter().map(|x| format!("{x}")).collect::>().join(", ")) + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct Function { + name: Option, + t: FunctionType, + arg_names: Vec, + body: Box, +} + +impl Function { + pub(crate) fn lambda(t: FunctionType, arg_names: Vec, body: Box) -> Self { + Self { + name: None, + t, + arg_names, + body + } + } + + pub(crate) fn named(name: &str, t: FunctionType, arg_names: Vec, body: Box) -> Self { + Self { + name: Some(name.to_string()), + t, + arg_names, + body + } + } + + pub fn name(&self) -> Option<&str> { + self.name.as_ref().map(|x| x.as_str()) + } + + pub fn get_type(&self) -> FunctionType { + self.t.clone() + } + + pub(crate) fn call(&mut self, + globals: HashMap, + locals: HashMap, + args: Vec) -> Result + { + let mut tree = vec![Ok(*self.body.clone())].into_iter(); + + let mut exec = Executor::new(&mut tree) + .locals(locals.clone()) + .globals(globals.clone()); + + for (obj, name) in std::iter::zip(args.into_iter(), self.arg_names.clone().into_iter()) { + exec = exec.add_local(name.clone(), obj); + } + + if let Some(name) = self.name().map(|x| x.to_string()) { + exec = exec.add_local(name, Object::function(self.clone(), globals, locals)); + } + + exec.next().unwrap() + } +} + +impl Display for Function { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.t) + } +} diff --git a/src/lib.rs b/src/lib.rs index da3caa3..31cca04 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,15 +1,19 @@ mod tokenizer; mod parser; mod executor; +mod function; use executor::{Executor, RuntimeError}; use parser::{ParseTree, Parser}; use tokenizer::Tokenizer; +use function::{FunctionType, Function}; use std::collections::HashMap; use std::fmt::Display; -use std::io::{Write, Read, BufRead}; +use std::io::BufRead; use std::fmt; +use std::iter::Peekable; +use std::marker::PhantomData; #[derive(Clone, Debug)] pub enum Type { @@ -71,7 +75,7 @@ impl Value { Self::String(_) => Type::String, Self::Array(t, _) => Type::Array(Box::new(t.clone())), Self::Nil => Type::Nil, - Self::Function(f) => Type::Function(f.t.clone()), + Self::Function(f) => Type::Function(f.get_type()), } } } @@ -84,98 +88,96 @@ impl Display for Value { Self::Bool(x) => write!(f, "{}", if *x { "true" } else { "false" }), Self::String(x) => write!(f, "\"{x}\""), Self::Array(_t, v) => write!(f, "[{}]", v.iter().map(|x| format!("{x}")).collect::>().join(" ")), - Self::Function(func) => { - if let Some(name) = &func.name { - write!(f, "Function({}, {}, {})", name, func.t.0, func.t.1.iter().map(|x| format!("{x}")).collect::>().join(", ")) - } else { - write!(f, "{}", func.t) - } - } + Self::Function(func) => write!(f, "{func}"), Self::Nil => write!(f, "nil"), } } } #[derive(Clone, Debug, PartialEq)] -pub struct FunctionType(Box, Vec); - -impl Display for FunctionType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Function({}, {})", self.0, self.1.iter().map(|x| format!("{x}")).collect::>().join(", ")) - } +enum Cache { + Cached(Value), + Uncached(ParseTree), } #[derive(Clone, Debug, PartialEq)] -enum Evaluation { - // at this point, it's type is set in stone - Computed(Value), - - // at this point, it's type is unknown, and may contradict a variable's type - // or not match the expected value of the expression, this is a runtime error - Uncomputed(Box), -} - -#[derive(Clone, Debug, PartialEq)] -enum Object { - Variable(Evaluation), - Function(Function), -} - -#[derive(Clone, Debug, PartialEq)] -pub struct Function { - name: Option, - t: FunctionType, +struct Object { locals: HashMap, - arg_names: Option>, - body: Option>, + globals: HashMap, + value: Cache, } -impl Function { - fn lambda(t: FunctionType, arg_names: Vec, locals: HashMap, body: Option>) -> Self { +impl Object { + pub fn variable(tree: ParseTree, globals: HashMap, locals: HashMap) -> Self { Self { - name: None, - t, locals, - arg_names: Some(arg_names), - body + globals, + value: Cache::Uncached(tree), } } - fn named(name: &str, t: FunctionType, arg_names: Option>, locals: HashMap, body: Option>) -> Self { + pub fn value(v: Value, globals: HashMap, locals: HashMap) -> Self { Self { - name: Some(name.to_string()), - t, locals, - arg_names, - body + globals, + value: Cache::Cached(v), } } + + pub fn function(func: Function, globals: HashMap, locals: HashMap) -> Self { + Self { + locals, + globals, + value: Cache::Cached(Value::Function(func)), + } + } + + /// evaluate the tree inside of an object if it isn't evaluated yet, returns the value + pub fn eval(&mut self) -> Result { + match self.value.clone() { + Cache::Cached(v) => Ok(v), + Cache::Uncached(tree) => { + let mut tree = vec![Ok(tree)].into_iter(); + + let mut exec = Executor::new(&mut tree) + .locals(self.locals.clone()) + .globals(self.globals.clone()); + + let v = exec.next().unwrap()?; + + self.value = Cache::Cached(v.clone()); + + Ok(v) + } + } + } + + pub fn locals(&self) -> HashMap { + self.locals.clone() + } + + pub fn globals(&self) -> HashMap { + self.globals.clone() + } } pub struct Runtime<'a, R: BufRead> { - inner: executor::Executor<'a, parser::Parser>> + tokenizer: Peekable>, + parser: Option>>, + phantom: PhantomData>>>, } -impl<'a, R: BufRead + 'a> Runtime<'a, R> { +impl<'a, R: BufRead> Runtime<'a, R> { pub fn new(reader: R) -> Self { Self { - inner: Executor::new(Parser::new(Tokenizer::new(reader))) + tokenizer: Tokenizer::new(reader).peekable(), + parser: None, + phantom: PhantomData, } } - pub fn stdout(self, stdout: impl Write + 'a) -> Self { - Self { - inner: self.inner.stdout(stdout) - } - } - - pub fn stdin(self, stdin: impl Read + 'a) -> Self { - Self { - inner: self.inner.stdin(stdin) - } - } - - pub fn values(self) -> impl Iterator> + 'a { - self.inner + pub fn values(&'a mut self) -> impl Iterator> + 'a { + self.parser = Some(Parser::new(&mut self.tokenizer)); + Executor::new(self.parser.as_mut().unwrap()) } } diff --git a/src/main.rs b/src/main.rs index 88460f5..571aa6c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use std::io::{self, BufReader}; fn main() { - let runtime = lamm::Runtime::new(BufReader::new(io::stdin())); + let mut runtime = lamm::Runtime::new(BufReader::new(io::stdin())); for value in runtime.values() { match value { diff --git a/src/parser.rs b/src/parser.rs index 5c52f30..b73ae5b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,12 +1,10 @@ -use crate::Evaluation; -use super::{Value, Type, Object, Function, FunctionType}; +use super::{Value, Type, Function, FunctionType}; use super::tokenizer::{Token, TokenizeError, Op}; use std::error; use std::collections::HashMap; use std::fmt::Display; -use std::borrow::Cow; use std::iter::Peekable; #[derive(Debug)] @@ -93,149 +91,135 @@ pub(crate) enum ParseTree { Print(Box), } -macro_rules! one_arg { - ($op:ident, $tokens:ident, $globals:ident, $locals:ident) => { - Ok(ParseTree::$op( - Box::new(ParseTree::parse($tokens, $globals, $locals)?) - ))} +/// Parses input tokens and produces ParseTrees for an Executor +pub(crate) struct Parser<'a, I: Iterator>> { + tokens: &'a mut Peekable, + globals: HashMap, + locals: HashMap, } -macro_rules! two_arg { - ($op:ident, $tokens:ident, $globals:ident, $locals:ident) => { - Ok(ParseTree::$op( - Box::new(ParseTree::parse($tokens, $globals, $locals)?), - Box::new(ParseTree::parse($tokens, $globals, $locals)?) - ))} -} +impl<'a, I: Iterator>> Parser<'a, I> { + pub fn new(tokens: &'a mut Peekable) -> Self { + Self { + tokens: tokens, + globals: HashMap::new(), + locals: HashMap::new() + } + } -macro_rules! three_arg { - ($op:ident, $tokens:ident, $globals:ident, $locals:ident) => { - Ok(ParseTree::$op( - Box::new(ParseTree::parse($tokens, $globals, $locals)?), - Box::new(ParseTree::parse($tokens, $globals, $locals)?), - Box::new(ParseTree::parse($tokens, $globals, $locals)?) - ))} -} + pub fn globals(mut self, globals: HashMap) -> Self { + self.globals = globals; + self + } -impl ParseTree { - fn parse( - tokens: &mut Peekable, - globals: &HashMap, - locals: &mut Cow>) -> Result - where - I: Iterator>, - { - match tokens.next() { + pub fn _add_global(mut self, k: String, v: Type) -> Self { + self.globals.insert(k, v); + self + } + + pub fn locals(mut self, locals: HashMap) -> Self { + self.locals = locals; + self + } + + pub fn add_local(mut self, k: String, v: Type) -> Self { + self.locals.insert(k, v); + self + } + + fn get_object_type(&self, ident: &String) -> Result<&Type, ParseError> { + self.locals.get(ident).or(self.globals.get(ident)) + .ok_or(ParseError::IdentifierUndefined(ident.clone())) + } + + fn parse(&mut self) -> Result { + match self.tokens.next() { Some(Ok(token)) => { match token { - Token::Constant(c) => Ok(Self::Constant(c)), + Token::Constant(c) => Ok(ParseTree::Constant(c)), Token::Identifier(ident) => { - if let Some(obj) = locals.clone().get(&ident).or(globals.clone().get(&ident)) { - match obj { - Object::Function(f) => { - let args = f.t.1.iter() - .map(|_| ParseTree::parse(tokens, globals, locals)).collect::, ParseError>>()?; - - Ok(ParseTree::FunctionCall(ident, args)) - } - Object::Variable(e) => Ok(ParseTree::Variable(ident)), + match self.get_object_type(&ident)? { + Type::Function(f) => { + let args = f.1.clone().iter() + .map(|_| self.parse()).collect::, ParseError>>()?; + + Ok(ParseTree::FunctionCall(ident, args)) } - } else { - Err(ParseError::IdentifierUndefined(ident)) + _ => Ok(ParseTree::Variable(ident)), } } Token::Operator(op) => { match op { - Op::Add => two_arg!(Add, tokens, globals, locals), - Op::Sub => two_arg!(Sub, tokens, globals, locals), - Op::Mul => two_arg!(Mul, tokens, globals, locals), - Op::Div => two_arg!(Div, tokens, globals, locals), - Op::Exp => two_arg!(Exp, tokens, globals, locals), - Op::Mod => two_arg!(Mod, tokens, globals, locals), + Op::Add => Ok(ParseTree::Add(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::Sub => Ok(ParseTree::Sub(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::Mul => Ok(ParseTree::Mul(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::Div => Ok(ParseTree::Div(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::Exp => Ok(ParseTree::Exp(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::Mod => Ok(ParseTree::Mod(Box::new(self.parse()?), Box::new(self.parse()?))), Op::Equ | Op::LazyEqu => { - let token = tokens.next() + let token = self.tokens.next() .ok_or(ParseError::UnexpectedEndInput)? .map_err(|e| ParseError::TokenizeError(e))?; - let body = Box::new(ParseTree::parse(tokens, globals, locals)?); + let body = Box::new(self.parse()?); if let Token::Identifier(ident) = token { - let locals = locals.to_mut(); - - locals.insert(ident.clone(), Object::Variable(Evaluation::Computed(Value::Nil))); - match op { - Op::Equ => Ok(ParseTree::Equ(ident, - body, - Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&locals))?) - )), - Op::LazyEqu => Ok(ParseTree::LazyEqu(ident, - body, - Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&locals))?) - )), - _ => panic!("Operator literally changed under your nose"), + Op::Equ => Ok(ParseTree::Equ(ident.clone(), + body.clone(), + Box::new(Parser::new(self.tokens.by_ref()) + .locals(self.locals.clone()) + .globals(self.globals.clone()) + .add_local(ident, Type::Any) + .parse()?)) + ), + Op::LazyEqu => Ok(ParseTree::LazyEqu(ident.clone(), + body.clone(), + Box::new(Parser::new(self.tokens.by_ref()) + .locals(self.locals.clone()) + .globals(self.globals.clone()) + .add_local(ident, Type::Any) + .parse()?)) + ), + _ => unreachable!(), } } else { Err(ParseError::InvalidIdentifier(token)) } } Op::FunctionDefine(arg_count) => { - let f = { - let mut f = ParseTree::parse_function(tokens, arg_count)?; + let f = self.parse_function(arg_count)?; - if locals.contains_key(&f.name.clone().unwrap()) { - return Err(ParseError::ImmutableError(f.name.unwrap())); - } - - f.locals = locals.to_mut().clone(); - - // recursion requires that f's prototype is present in locals - f.locals.insert(f.name.clone().unwrap(), Object::Function(f.clone())); - - // we also need any function parameters in local scope - for (name, t) in std::iter::zip(f.arg_names.clone().unwrap(), f.t.1.clone()) { - match t { - Type::Function(t) => { - f.locals.insert(name.clone(), Object::Function(Function::named(&name, t, None, HashMap::new(), None))); - } - _ => { - // the value isn't important, just that the identifier is there - f.locals.insert(name.clone(), Object::Variable(Evaluation::Computed(Value::Nil))); - } - } - } - - f.body = Some(Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&f.locals))?)); - - f - }; - - let locals = locals.to_mut(); - locals.insert(f.name.clone().unwrap(), Object::Function(f.clone())); - - Ok(ParseTree::FunctionDefinition(f, Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&locals))?))) + Ok(ParseTree::FunctionDefinition(f.clone(), + Box::new( + Parser::new(self.tokens) + .globals(self.globals.clone()) + .locals(self.locals.clone()) + .add_local(f.name().unwrap().to_string(), Type::Function(f.get_type())) + .parse()? + ))) }, - Op::Compose => two_arg!(Compose, tokens, globals, locals), - Op::Id => one_arg!(Id, tokens, globals, locals), - Op::If => two_arg!(If, tokens, globals, locals), - Op::IfElse => three_arg!(IfElse, tokens, globals, locals), - Op::EqualTo => two_arg!(EqualTo, tokens, globals, locals), - Op::GreaterThan => two_arg!(GreaterThan, tokens, globals, locals), - Op::LessThan => two_arg!(LessThan, tokens, globals, locals), - Op::GreaterThanOrEqualTo => two_arg!(GreaterThanOrEqualTo, tokens, globals, locals), - Op::LessThanOrEqualTo => two_arg!(LessThanOrEqualTo, tokens, globals, locals), - Op::Not => one_arg!(Not, tokens, globals, locals), - Op::IntCast => one_arg!(IntCast, tokens, globals, locals), - Op::FloatCast => one_arg!(FloatCast, tokens, globals, locals), - Op::BoolCast => one_arg!(BoolCast, tokens, globals, locals), - Op::StringCast => one_arg!(StringCast, tokens, globals, locals), - Op::Print => one_arg!(Print, tokens, globals, locals), + Op::Compose => Ok(ParseTree::Compose(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::Id => Ok(ParseTree::Id(Box::new(self.parse()?))), + Op::IfElse => Ok(ParseTree::IfElse(Box::new(self.parse()?), Box::new(self.parse()?), Box::new(self.parse()?))), + Op::If => Ok(ParseTree::If(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::EqualTo => Ok(ParseTree::EqualTo(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::GreaterThan => Ok(ParseTree::GreaterThan(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::LessThan => Ok(ParseTree::LessThan(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::GreaterThanOrEqualTo => Ok(ParseTree::GreaterThanOrEqualTo(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::LessThanOrEqualTo => Ok(ParseTree::LessThanOrEqualTo(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::Not => Ok(ParseTree::Not(Box::new(self.parse()?))), + Op::IntCast => Ok(ParseTree::IntCast(Box::new(self.parse()?))), + Op::FloatCast => Ok(ParseTree::FloatCast(Box::new(self.parse()?))), + Op::BoolCast => Ok(ParseTree::BoolCast(Box::new(self.parse()?))), + Op::StringCast => Ok(ParseTree::StringCast(Box::new(self.parse()?))), + Op::Print => Ok(ParseTree::Print(Box::new(self.parse()?))), Op::OpenArray => { let mut depth = 1; // take tokens until we reach the end of this array // if we don't collect them here it causes rust to overflow computing the types - let array_tokens = tokens.by_ref().take_while(|t| match t { + let array_tokens = self.tokens.by_ref().take_while(|t| match t { Ok(Token::Operator(Op::OpenArray)) => { depth += 1; true @@ -247,11 +231,16 @@ impl ParseTree { _ => true, }).collect::, TokenizeError>>().map_err(|e| ParseError::TokenizeError(e))?; - let array_tokens: Vec> = array_tokens.into_iter().map(|t| Ok(t)).collect(); + let mut array_tokens = array_tokens + .into_iter() + .map(|t| Ok(t)) + .collect::>>() + .into_iter() + .peekable(); - let trees: Vec = Parser::new(array_tokens.into_iter()) - .globals(globals.clone()) - .locals(locals.to_mut().to_owned()) + let trees: Vec = Parser::new(&mut array_tokens) + .globals(self.globals.to_owned()) + .locals(self.locals.to_owned()) .collect::>()?; let tree = trees.into_iter().fold( @@ -263,44 +252,21 @@ impl ParseTree { } Op::Empty => Ok(ParseTree::Constant(Value::Array(Type::Any, vec![]))), Op::CloseArray => Err(ParseError::UnmatchedArrayClose), - Op::NotEqualTo => two_arg!(NotEqualTo, tokens, globals, locals), - Op::And => two_arg!(And, tokens, globals, locals), - Op::Or => two_arg!(Or, tokens, globals, locals), + Op::NotEqualTo => Ok(ParseTree::NotEqualTo(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::And => Ok(ParseTree::And(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::Or => Ok(ParseTree::Or(Box::new(self.parse()?), Box::new(self.parse()?))), Op::LambdaDefine(arg_count) => { - let f = { - let mut f = ParseTree::parse_lambda(tokens, arg_count)?; - - let locals = locals.to_mut(); - f.locals = locals.clone(); - - // we need any function parameters in local scope - for (name, t) in std::iter::zip(f.arg_names.clone().unwrap(), f.t.1.clone()) { - match t { - Type::Function(t) => { - f.locals.insert(name.clone(), Object::Function(Function::named(&name, t, None, HashMap::new(), None))); - } - _ => { - // the value isn't important, just that the identifier is there - f.locals.insert(name.clone(), Object::Variable(Evaluation::Computed(Value::Nil))); - } - } - } - - f.body = Some(Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&f.locals))?)); - - f - }; - + let f = self.parse_lambda(arg_count)?; Ok(ParseTree::LambdaDefinition(f)) } Op::NonCall => { - let name = Self::get_identifier(tokens.next())?; + let name = Self::get_identifier(self.tokens.next())?; Ok(ParseTree::NonCall(name)) }, - Op::Head => one_arg!(Head, tokens, globals, locals), - Op::Tail => one_arg!(Tail, tokens, globals, locals), - Op::Init => one_arg!(Init, tokens, globals, locals), - Op::Fini => one_arg!(Fini, tokens, globals, locals), + Op::Head => Ok(ParseTree::Head(Box::new(self.parse()?))), + Op::Tail => Ok(ParseTree::Tail(Box::new(self.parse()?))), + Op::Init => Ok(ParseTree::Init(Box::new(self.parse()?))), + Op::Fini => Ok(ParseTree::Fini(Box::new(self.parse()?))), op => Err(ParseError::UnwantedToken(Token::Operator(op))), } } @@ -312,28 +278,40 @@ impl ParseTree { } } - fn parse_lambda(tokens: &mut Peekable, arg_count: usize) -> Result - where - I: Iterator>, - { - let (t, args) = Self::parse_function_declaration(tokens, arg_count)?; - Ok(Function::lambda(t, args, HashMap::new(), None)) + fn parse_lambda(&mut self, arg_count: usize) -> Result { + let (t, args) = Self::parse_function_declaration(self.tokens, arg_count)?; + + let mut locals = self.locals.clone(); + + for (name, t) in std::iter::zip(args.iter(), t.1.iter()) { + locals.insert(name.clone(), t.clone()); + } + + Ok(Function::lambda(t, args, Box::new( + Parser::new(self.tokens) + .globals(self.globals.clone()) + .locals(locals).parse()?))) } - fn parse_function(tokens: &mut Peekable, arg_count: usize) -> Result - where - I: Iterator>, - { - let name = Self::get_identifier(tokens.next())?; - let (t, args) = Self::parse_function_declaration(tokens, arg_count)?; + fn parse_function(&mut self, arg_count: usize) -> Result { + let name = Self::get_identifier(self.tokens.next())?; + let (t, args) = Self::parse_function_declaration(self.tokens, arg_count)?; - Ok(Function::named(&name, t, Some(args), HashMap::new(), None)) + let mut locals = self.locals.clone(); + + for (name, t) in std::iter::zip(args.iter(), t.1.iter()) { + locals.insert(name.clone(), t.clone()); + } + + locals.insert(name.clone(), Type::Function(t.clone())); + + Ok(Function::named(&name, t, args, Box::new( + Parser::new(self.tokens) + .globals(self.globals.clone()) + .locals(locals).parse()?))) } - fn parse_function_declaration(tokens: &mut Peekable, arg_count: usize) -> Result<(FunctionType, Vec), ParseError> - where - I: Iterator> - { + fn parse_function_declaration(tokens: &mut Peekable, arg_count: usize) -> Result<(FunctionType, Vec), ParseError> { let args: Vec<(Type, String)> = (0..arg_count) .map(|_| Self::parse_function_declaration_parameter(tokens)) .collect::>()?; @@ -342,22 +320,14 @@ impl ParseTree { let (types, names): (Vec<_>, Vec<_>) = args.into_iter().unzip(); let mut ret = Type::Any; - if let Some(t) = tokens.next_if(|x| matches!(x, Ok(Token::Operator(Op::Arrow)))) - { - if let Err(e) = t { - return Err(ParseError::TokenizeError(e)); - } - + if tokens.next_if(|x| matches!(x, Ok(Token::Operator(Op::Arrow)))).is_some() { ret = Self::parse_type(tokens)?; } Ok((FunctionType(Box::new(ret), types), names)) } - fn parse_function_declaration_parameter(mut tokens: &mut Peekable) -> Result<(Type, String), ParseError> - where - I: Iterator> - { + fn parse_function_declaration_parameter(mut tokens: &mut Peekable) -> Result<(Type, String), ParseError> { match tokens.next() { // untyped variable Some(Ok(Token::Identifier(x))) => Ok((Type::Any, x)), @@ -406,10 +376,7 @@ impl ParseTree { } } - fn parse_type(tokens: &mut I) -> Result - where - I: Iterator>, - { + fn parse_type(tokens: &mut Peekable) -> Result { match tokens.next() { Some(Ok(Token::Type(t))) => Ok(t), Some(Ok(Token::Operator(Op::FunctionDefine(n)))) => { @@ -447,48 +414,11 @@ impl ParseTree { } } -/// Parses input tokens and produces ParseTrees for an Executor -pub(crate) struct Parser>> { - tokens: I, - - // These are used to keep track of functions in the current context - // by the parser. otherwise the parser would have no way to tell - // if the program `* a b 12` is supposed to be ((* a b) (12)) or (* (a b) 12) - globals: HashMap, - locals: HashMap, -} - -impl>> Parser { - pub fn new(tokens: I) -> Self { - Self { - tokens, - globals: HashMap::new(), - locals: HashMap::new() - } - } - - pub fn globals(self, globals: HashMap) -> Self { - Self { - tokens: self.tokens, - globals, - locals: self.locals, - } - } - - pub fn locals(self, locals: HashMap) -> Self { - Self { - tokens: self.tokens, - globals: self.globals, - locals, - } - } -} - -impl>> Iterator for Parser { +impl<'a, I: Iterator>> Iterator for Parser<'a, I> { type Item = Result; fn next(&mut self) -> Option { - let tree = ParseTree::parse(&mut self.tokens.by_ref().peekable(), &self.globals, &mut Cow::Borrowed(&self.locals)); + let tree = self.parse(); match tree { Ok(tree) => Some(Ok(tree)), diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 00a0353..0b43e64 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -42,7 +42,7 @@ impl Display for TokenizeError { impl error::Error for TokenizeError {} #[derive(Debug, Clone, PartialEq)] -pub(crate) enum Op { +pub enum Op { Add, Sub, Mul, @@ -85,7 +85,7 @@ pub(crate) enum Op { } #[derive(Debug, Clone)] -pub(crate) enum Token { +pub enum Token { Identifier(String), Operator(Op), Constant(Value), @@ -408,26 +408,4 @@ impl std::iter::Iterator for Tokenizer { Err(e) => Some(Err(TokenizeError::IO(e))), } } -} - -#[cfg(test)] -mod tests { - use std::str::FromStr; - - use crate::parser::{Parser, ParseTree, ParseError}; - - use super::*; - - #[test] - fn uwu() { - let program = ":. map ?: f Any -> Any ?. x [Any] -> [Any] ?? bool x + f head x map 'f tail x empty map ;x ** x 2 [1 2 3 4 5]"; - - let tokens: Vec = Tokenizer::from_str(program).unwrap().collect::>().unwrap(); - - println!("{tokens:?}"); - - let trees: Result, ParseError> = Parser::new(tokens.into_iter().map(|x| Ok(x))).collect(); - - println!("{trees:?}"); - } } \ No newline at end of file From 44e5d74e5eeaeca4fa236871a371a4d2152967e7 Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Sat, 19 Oct 2024 00:22:40 -0400 Subject: [PATCH 39/48] add statements and bug fix for arrays --- src/executor.rs | 1 + src/function.rs | 4 +- src/parser.rs | 314 ++++++++++++++++++++++++++--------------------- src/tokenizer.rs | 6 +- 4 files changed, 180 insertions(+), 145 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 0e4f4ee..7d13b2c 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -375,6 +375,7 @@ where Value::Array(_, x) => Ok(x.last().ok_or(RuntimeError::EmptyArray)?.clone()), t => Err(RuntimeError::NoOverloadForTypes("fini".into(), vec![t])) }, + ParseTree::Nop => Ok(Value::Nil), } } } diff --git a/src/function.rs b/src/function.rs index ceebbb4..0072636 100644 --- a/src/function.rs +++ b/src/function.rs @@ -41,11 +41,11 @@ impl Function { } } - pub fn name(&self) -> Option<&str> { + pub(crate) fn name(&self) -> Option<&str> { self.name.as_ref().map(|x| x.as_str()) } - pub fn get_type(&self) -> FunctionType { + pub(crate) fn get_type(&self) -> FunctionType { self.t.clone() } diff --git a/src/parser.rs b/src/parser.rs index b73ae5b..184a3e3 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -89,6 +89,7 @@ pub(crate) enum ParseTree { // Misc Print(Box), + Nop, } /// Parses input tokens and produces ParseTrees for an Executor @@ -133,148 +134,178 @@ impl<'a, I: Iterator>> Parser<'a, I> { } fn parse(&mut self) -> Result { - match self.tokens.next() { - Some(Ok(token)) => { - match token { - Token::Constant(c) => Ok(ParseTree::Constant(c)), - Token::Identifier(ident) => { - match self.get_object_type(&ident)? { - Type::Function(f) => { - let args = f.1.clone().iter() - .map(|_| self.parse()).collect::, ParseError>>()?; - - Ok(ParseTree::FunctionCall(ident, args)) - } - _ => Ok(ParseTree::Variable(ident)), - } + match self.tokens.next().ok_or(ParseError::NoInput)?.map_err(|e| ParseError::TokenizeError(e))? { + Token::Constant(c) => Ok(ParseTree::Constant(c)), + Token::Identifier(ident) => { + match self.get_object_type(&ident)? { + Type::Function(f) => { + let args = f.1.clone().iter() + .map(|_| self.parse()).collect::, ParseError>>()?; + + Ok(ParseTree::FunctionCall(ident, args)) } - Token::Operator(op) => { - match op { - Op::Add => Ok(ParseTree::Add(Box::new(self.parse()?), Box::new(self.parse()?))), - Op::Sub => Ok(ParseTree::Sub(Box::new(self.parse()?), Box::new(self.parse()?))), - Op::Mul => Ok(ParseTree::Mul(Box::new(self.parse()?), Box::new(self.parse()?))), - Op::Div => Ok(ParseTree::Div(Box::new(self.parse()?), Box::new(self.parse()?))), - Op::Exp => Ok(ParseTree::Exp(Box::new(self.parse()?), Box::new(self.parse()?))), - Op::Mod => Ok(ParseTree::Mod(Box::new(self.parse()?), Box::new(self.parse()?))), - Op::Equ | Op::LazyEqu => { - let token = self.tokens.next() - .ok_or(ParseError::UnexpectedEndInput)? - .map_err(|e| ParseError::TokenizeError(e))?; - - let body = Box::new(self.parse()?); - - if let Token::Identifier(ident) = token { - match op { - Op::Equ => Ok(ParseTree::Equ(ident.clone(), - body.clone(), - Box::new(Parser::new(self.tokens.by_ref()) - .locals(self.locals.clone()) - .globals(self.globals.clone()) - .add_local(ident, Type::Any) - .parse()?)) - ), - Op::LazyEqu => Ok(ParseTree::LazyEqu(ident.clone(), - body.clone(), - Box::new(Parser::new(self.tokens.by_ref()) - .locals(self.locals.clone()) - .globals(self.globals.clone()) - .add_local(ident, Type::Any) - .parse()?)) - ), - _ => unreachable!(), - } - } else { - Err(ParseError::InvalidIdentifier(token)) - } - } - Op::FunctionDefine(arg_count) => { - let f = self.parse_function(arg_count)?; - - Ok(ParseTree::FunctionDefinition(f.clone(), - Box::new( - Parser::new(self.tokens) - .globals(self.globals.clone()) - .locals(self.locals.clone()) - .add_local(f.name().unwrap().to_string(), Type::Function(f.get_type())) - .parse()? - ))) - }, - Op::Compose => Ok(ParseTree::Compose(Box::new(self.parse()?), Box::new(self.parse()?))), - Op::Id => Ok(ParseTree::Id(Box::new(self.parse()?))), - Op::IfElse => Ok(ParseTree::IfElse(Box::new(self.parse()?), Box::new(self.parse()?), Box::new(self.parse()?))), - Op::If => Ok(ParseTree::If(Box::new(self.parse()?), Box::new(self.parse()?))), - Op::EqualTo => Ok(ParseTree::EqualTo(Box::new(self.parse()?), Box::new(self.parse()?))), - Op::GreaterThan => Ok(ParseTree::GreaterThan(Box::new(self.parse()?), Box::new(self.parse()?))), - Op::LessThan => Ok(ParseTree::LessThan(Box::new(self.parse()?), Box::new(self.parse()?))), - Op::GreaterThanOrEqualTo => Ok(ParseTree::GreaterThanOrEqualTo(Box::new(self.parse()?), Box::new(self.parse()?))), - Op::LessThanOrEqualTo => Ok(ParseTree::LessThanOrEqualTo(Box::new(self.parse()?), Box::new(self.parse()?))), - Op::Not => Ok(ParseTree::Not(Box::new(self.parse()?))), - Op::IntCast => Ok(ParseTree::IntCast(Box::new(self.parse()?))), - Op::FloatCast => Ok(ParseTree::FloatCast(Box::new(self.parse()?))), - Op::BoolCast => Ok(ParseTree::BoolCast(Box::new(self.parse()?))), - Op::StringCast => Ok(ParseTree::StringCast(Box::new(self.parse()?))), - Op::Print => Ok(ParseTree::Print(Box::new(self.parse()?))), - Op::OpenArray => { - let mut depth = 1; - - // take tokens until we reach the end of this array - // if we don't collect them here it causes rust to overflow computing the types - let array_tokens = self.tokens.by_ref().take_while(|t| match t { - Ok(Token::Operator(Op::OpenArray)) => { - depth += 1; - true - }, - Ok(Token::Operator(Op::CloseArray)) => { - depth -= 1; - depth > 0 - } - _ => true, - }).collect::, TokenizeError>>().map_err(|e| ParseError::TokenizeError(e))?; - - let mut array_tokens = array_tokens - .into_iter() - .map(|t| Ok(t)) - .collect::>>() - .into_iter() - .peekable(); - - let trees: Vec = Parser::new(&mut array_tokens) - .globals(self.globals.to_owned()) - .locals(self.locals.to_owned()) - .collect::>()?; - - let tree = trees.into_iter().fold( - ParseTree::Constant(Value::Array(Type::Any, vec![])), - |acc, x| ParseTree::Add(Box::new(acc), Box::new(x.clone())), - ); - - Ok(tree) - } - Op::Empty => Ok(ParseTree::Constant(Value::Array(Type::Any, vec![]))), - Op::CloseArray => Err(ParseError::UnmatchedArrayClose), - Op::NotEqualTo => Ok(ParseTree::NotEqualTo(Box::new(self.parse()?), Box::new(self.parse()?))), - Op::And => Ok(ParseTree::And(Box::new(self.parse()?), Box::new(self.parse()?))), - Op::Or => Ok(ParseTree::Or(Box::new(self.parse()?), Box::new(self.parse()?))), - Op::LambdaDefine(arg_count) => { - let f = self.parse_lambda(arg_count)?; - Ok(ParseTree::LambdaDefinition(f)) - } - Op::NonCall => { - let name = Self::get_identifier(self.tokens.next())?; - Ok(ParseTree::NonCall(name)) - }, - Op::Head => Ok(ParseTree::Head(Box::new(self.parse()?))), - Op::Tail => Ok(ParseTree::Tail(Box::new(self.parse()?))), - Op::Init => Ok(ParseTree::Init(Box::new(self.parse()?))), - Op::Fini => Ok(ParseTree::Fini(Box::new(self.parse()?))), - op => Err(ParseError::UnwantedToken(Token::Operator(op))), - } - } - t => Err(ParseError::UnwantedToken(t)), + _ => Ok(ParseTree::Variable(ident)), } - }, - Some(Err(e)) => Err(ParseError::TokenizeError(e)), - None => Err(ParseError::NoInput), + } + Token::Operator(op) => { + match op { + Op::Add => Ok(ParseTree::Add(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::Sub => Ok(ParseTree::Sub(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::Mul => Ok(ParseTree::Mul(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::Div => Ok(ParseTree::Div(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::Exp => Ok(ParseTree::Exp(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::Mod => Ok(ParseTree::Mod(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::Equ | Op::LazyEqu => { + let token = self.tokens.next() + .ok_or(ParseError::UnexpectedEndInput)? + .map_err(|e| ParseError::TokenizeError(e))?; + + let body = Box::new(self.parse()?); + + if let Token::Identifier(ident) = token { + match op { + Op::Equ => Ok(ParseTree::Equ(ident.clone(), + body.clone(), + Box::new(Parser::new(self.tokens.by_ref()) + .locals(self.locals.clone()) + .globals(self.globals.clone()) + .add_local(ident, Type::Any) + .parse()?)) + ), + Op::LazyEqu => Ok(ParseTree::LazyEqu(ident.clone(), + body.clone(), + Box::new(Parser::new(self.tokens.by_ref()) + .locals(self.locals.clone()) + .globals(self.globals.clone()) + .add_local(ident, Type::Any) + .parse()?)) + ), + _ => unreachable!(), + } + } else { + Err(ParseError::InvalidIdentifier(token)) + } + } + Op::FunctionDefine(arg_count) => { + let f = self.parse_function(arg_count)?; + + Ok(ParseTree::FunctionDefinition(f.clone(), + Box::new( + Parser::new(self.tokens) + .globals(self.globals.clone()) + .locals(self.locals.clone()) + .add_local(f.name().unwrap().to_string(), Type::Function(f.get_type())) + .parse()? + ))) + }, + Op::Compose => Ok(ParseTree::Compose(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::Id => Ok(ParseTree::Id(Box::new(self.parse()?))), + Op::IfElse => Ok(ParseTree::IfElse(Box::new(self.parse()?), Box::new(self.parse()?), Box::new(self.parse()?))), + Op::If => Ok(ParseTree::If(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::EqualTo => Ok(ParseTree::EqualTo(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::GreaterThan => Ok(ParseTree::GreaterThan(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::LessThan => Ok(ParseTree::LessThan(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::GreaterThanOrEqualTo => Ok(ParseTree::GreaterThanOrEqualTo(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::LessThanOrEqualTo => Ok(ParseTree::LessThanOrEqualTo(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::Not => Ok(ParseTree::Not(Box::new(self.parse()?))), + Op::IntCast => Ok(ParseTree::IntCast(Box::new(self.parse()?))), + Op::FloatCast => Ok(ParseTree::FloatCast(Box::new(self.parse()?))), + Op::BoolCast => Ok(ParseTree::BoolCast(Box::new(self.parse()?))), + Op::StringCast => Ok(ParseTree::StringCast(Box::new(self.parse()?))), + Op::Print => Ok(ParseTree::Print(Box::new(self.parse()?))), + Op::OpenArray => { + let mut depth = 1; + + // take tokens until we reach the end of this array + // if we don't collect them here it causes rust to overflow computing the types + let array_tokens = self.tokens.by_ref().take_while(|t| match t { + Ok(Token::Operator(Op::OpenArray)) => { + depth += 1; + true + }, + Ok(Token::Operator(Op::CloseArray)) => { + depth -= 1; + depth > 0 + } + _ => true, + }).collect::, TokenizeError>>().map_err(|e| ParseError::TokenizeError(e))?; + + let mut array_tokens = array_tokens + .into_iter() + .map(|t| Ok(t)) + .collect::>>() + .into_iter() + .peekable(); + + let trees: Vec = Parser::new(&mut array_tokens) + .globals(self.globals.to_owned()) + .locals(self.locals.to_owned()) + .collect::>()?; + + let tree = trees.into_iter().fold( + ParseTree::Constant(Value::Array(Type::Any, vec![])), + |acc, x| ParseTree::Add(Box::new(acc), Box::new(x.clone())), + ); + + Ok(tree) + } + Op::OpenStatement => { + let mut depth = 1; + + // take tokens until we reach the end of this array + // if we don't collect them here it causes rust to overflow computing the types + let tokens = self.tokens.by_ref().take_while(|t| match t { + Ok(Token::Operator(Op::OpenStatement)) => { + depth += 1; + true + }, + Ok(Token::Operator(Op::CloseStatement)) => { + depth -= 1; + depth > 0 + } + _ => true, + }).collect::, TokenizeError>>().map_err(|e| ParseError::TokenizeError(e))?; + + let mut tokens = tokens + .into_iter() + .map(|t| Ok(t)) + .collect::>>() + .into_iter() + .peekable(); + + let trees: Vec = Parser::new(&mut tokens) + .globals(self.globals.to_owned()) + .locals(self.locals.to_owned()) + .collect::>()?; + + let tree = trees.into_iter().fold( + ParseTree::Nop, + |acc, x| ParseTree::Compose(Box::new(acc), Box::new(x.clone())), + ); + + Ok(tree) + } + Op::Empty => Ok(ParseTree::Constant(Value::Array(Type::Any, vec![]))), + Op::CloseArray => Err(ParseError::UnmatchedArrayClose), + Op::NotEqualTo => Ok(ParseTree::NotEqualTo(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::And => Ok(ParseTree::And(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::Or => Ok(ParseTree::Or(Box::new(self.parse()?), Box::new(self.parse()?))), + Op::LambdaDefine(arg_count) => { + let f = self.parse_lambda(arg_count)?; + Ok(ParseTree::LambdaDefinition(f)) + } + Op::NonCall => { + let name = Self::get_identifier(self.tokens.next())?; + Ok(ParseTree::NonCall(name)) + }, + Op::Head => Ok(ParseTree::Head(Box::new(self.parse()?))), + Op::Tail => Ok(ParseTree::Tail(Box::new(self.parse()?))), + Op::Init => Ok(ParseTree::Init(Box::new(self.parse()?))), + Op::Fini => Ok(ParseTree::Fini(Box::new(self.parse()?))), + op => Err(ParseError::UnwantedToken(Token::Operator(op))), + } + } + t => Err(ParseError::UnwantedToken(t)), } } @@ -316,10 +347,9 @@ impl<'a, I: Iterator>> Parser<'a, I> { .map(|_| Self::parse_function_declaration_parameter(tokens)) .collect::>()?; - let (types, names): (Vec<_>, Vec<_>) = args.into_iter().unzip(); let mut ret = Type::Any; - + if tokens.next_if(|x| matches!(x, Ok(Token::Operator(Op::Arrow)))).is_some() { ret = Self::parse_type(tokens)?; } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 0b43e64..410343b 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -74,6 +74,8 @@ pub enum Op { Print, OpenArray, CloseArray, + OpenStatement, + CloseStatement, Empty, And, Or, @@ -84,7 +86,7 @@ pub enum Op { Fini, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum Token { Identifier(String), Operator(Op), @@ -196,6 +198,8 @@ impl Tokenizer { ("!=", Op::NotEqualTo), ("[", Op::OpenArray), ("]", Op::CloseArray), + ("(", Op::OpenStatement), + (")", Op::CloseStatement), ("!", Op::Not), ("&&", Op::And), ("||", Op::Or), From 5a8354f1ad708ac3a02f4f49e7a1f0a19f0a9285 Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Sat, 19 Oct 2024 01:22:08 -0400 Subject: [PATCH 40/48] update readme (unimplemented feature now implmeented) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 622bc65..28c1e06 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Variables are **scoped** in Lamm, meaning they only exist in the single expressi Scope in Lamm consists of a single expression, such as `sqrt + ** a 2 ** b 2`. So then, what do I do when I need a variable for more than a single expression? There are multiple solutions depending on your needs. -### Multi-Statement Expression (half implemented) +### Multi-Statement Expression You can create a multi-statement expression using either `()` syntax or the `~` operator, which `()` is simple syntactic sugar for. In these, only the value of the last expression is returned, the rest get ignored. This is the perfect place to put stateful function calls. From 3027ef4820fbabe325e117116d22c56d8d03b63c Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Sat, 19 Oct 2024 22:13:41 -0400 Subject: [PATCH 41/48] add export keyword --- README.md | 2 +- src/executor.rs | 39 ++++++++++++++------------ src/function.rs | 10 +++---- src/lib.rs | 16 +++++------ src/parser.rs | 73 +++++++++++++++++++++++++++++++++--------------- src/tokenizer.rs | 18 ++++++++++++ 6 files changed, 103 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 28c1e06..d6be399 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ You can create a multi-statement expression using either `()` syntax or the `~` ) ``` -### Global Scope (unimplemented) +### Global Scope You can introduce a variable to global scope using the `export` builtin function. diff --git a/src/executor.rs b/src/executor.rs index 7d13b2c..034f3a7 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -49,7 +49,7 @@ where I: Iterator> { exprs: &'a mut I, - globals: HashMap, + globals: &'a mut HashMap, locals: HashMap, } @@ -57,20 +57,15 @@ impl<'a, I> Executor<'a, I> where I: Iterator>, { - pub fn new(exprs: &'a mut I) -> Self { + pub fn new(exprs: &'a mut I, globals: &'a mut HashMap) -> Self { Self { exprs, - globals: HashMap::new(), + globals, locals: HashMap::new(), } } - pub fn globals(mut self, globals: HashMap) -> Self { - self.globals = globals; - self - } - - pub fn _add_global(mut self, k: String, v: Object) -> Self { + pub fn _add_global(self, k: String, v: Object) -> Self { self.globals.insert(k, v); self } @@ -237,11 +232,11 @@ where Err(RuntimeError::ImmutableError(ident.clone())) } else { let value = self.exec(body)?; + let g = self.globals.clone(); - Executor::new(self.exprs) - .globals(self.globals.clone()) + Executor::new(self.exprs, &mut self.globals) .locals(self.locals.clone()) - .add_local(ident, Object::value(value, self.globals.to_owned(), self.locals.to_owned())) + .add_local(ident, Object::value(value, g, self.locals.to_owned())) .exec(scope) } }, @@ -249,18 +244,18 @@ where if self.variable_exists(&ident) { Err(RuntimeError::ImmutableError(ident.clone())) } else { - Executor::new(self.exprs) - .globals(self.globals.clone()) + let g = self.globals.clone(); + Executor::new(self.exprs, &mut self.globals) .locals(self.locals.clone()) - .add_local(ident, Object::variable(*body, self.globals.to_owned(), self.locals.to_owned())) + .add_local(ident, Object::variable(*body, g, self.locals.to_owned())) .exec(scope) } }, ParseTree::FunctionDefinition(func, scope) => { - Executor::new(self.exprs) - .globals(self.globals.clone()) + let g = self.globals.clone(); + Executor::new(self.exprs, &mut self.globals) .locals(self.locals.clone()) - .add_local(func.name().unwrap().to_string(), Object::function(func, self.globals.clone(), self.locals.clone())) + .add_local(func.name().unwrap().to_string(), Object::function(func, g, self.locals.clone())) .exec(scope) }, ParseTree::Compose(x, y) => { @@ -376,6 +371,14 @@ where t => Err(RuntimeError::NoOverloadForTypes("fini".into(), vec![t])) }, ParseTree::Nop => Ok(Value::Nil), + ParseTree::Export(names) => { + for name in names { + let obj = self.locals.remove(&name).ok_or(RuntimeError::VariableUndefined(name.clone()))?; + self.globals.insert(name, obj); + } + + Ok(Value::Nil) + } } } } diff --git a/src/function.rs b/src/function.rs index 0072636..53fa101 100644 --- a/src/function.rs +++ b/src/function.rs @@ -50,22 +50,22 @@ impl Function { } pub(crate) fn call(&mut self, - globals: HashMap, + mut globals: HashMap, locals: HashMap, args: Vec) -> Result { let mut tree = vec![Ok(*self.body.clone())].into_iter(); + let g = globals.clone(); - let mut exec = Executor::new(&mut tree) - .locals(locals.clone()) - .globals(globals.clone()); + let mut exec = Executor::new(&mut tree, &mut globals) + .locals(locals.clone()); for (obj, name) in std::iter::zip(args.into_iter(), self.arg_names.clone().into_iter()) { exec = exec.add_local(name.clone(), obj); } if let Some(name) = self.name().map(|x| x.to_string()) { - exec = exec.add_local(name, Object::function(self.clone(), globals, locals)); + exec = exec.add_local(name, Object::function(self.clone(), g, locals)); } exec.next().unwrap() diff --git a/src/lib.rs b/src/lib.rs index 31cca04..49cfbf8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,6 @@ use std::fmt::Display; use std::io::BufRead; use std::fmt; use std::iter::Peekable; -use std::marker::PhantomData; #[derive(Clone, Debug)] pub enum Type { @@ -139,9 +138,8 @@ impl Object { Cache::Uncached(tree) => { let mut tree = vec![Ok(tree)].into_iter(); - let mut exec = Executor::new(&mut tree) - .locals(self.locals.clone()) - .globals(self.globals.clone()); + let mut exec = Executor::new(&mut tree, &mut self.globals) + .locals(self.locals.clone()); let v = exec.next().unwrap()?; @@ -163,21 +161,23 @@ impl Object { pub struct Runtime<'a, R: BufRead> { tokenizer: Peekable>, + global_types: HashMap, + globals: HashMap, parser: Option>>, - phantom: PhantomData>>>, } impl<'a, R: BufRead> Runtime<'a, R> { pub fn new(reader: R) -> Self { Self { tokenizer: Tokenizer::new(reader).peekable(), + global_types: HashMap::new(), + globals: HashMap::new(), parser: None, - phantom: PhantomData, } } pub fn values(&'a mut self) -> impl Iterator> + 'a { - self.parser = Some(Parser::new(&mut self.tokenizer)); - Executor::new(self.parser.as_mut().unwrap()) + self.parser = Some(Parser::new(&mut self.tokenizer, &mut self.global_types)); + Executor::new(self.parser.as_mut().unwrap(), &mut self.globals) } } diff --git a/src/parser.rs b/src/parser.rs index 184a3e3..9973bcb 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,7 +1,10 @@ +use crate::executor::Executor; + use super::{Value, Type, Function, FunctionType}; use super::tokenizer::{Token, TokenizeError, Op}; +use std::borrow::{BorrowMut, Cow}; use std::error; use std::collections::HashMap; use std::fmt::Display; @@ -90,31 +93,32 @@ pub(crate) enum ParseTree { // Misc Print(Box), Nop, + Export(Vec), } /// Parses input tokens and produces ParseTrees for an Executor pub(crate) struct Parser<'a, I: Iterator>> { tokens: &'a mut Peekable, - globals: HashMap, + globals: &'a mut HashMap, locals: HashMap, } impl<'a, I: Iterator>> Parser<'a, I> { - pub fn new(tokens: &'a mut Peekable) -> Self { + pub fn new(tokens: &'a mut Peekable, globals: &'a mut HashMap) -> Self { Self { tokens: tokens, - globals: HashMap::new(), + globals, locals: HashMap::new() } } - pub fn globals(mut self, globals: HashMap) -> Self { - self.globals = globals; + pub fn add_global(self, k: String, v: Type) -> Self { + self.globals.insert(k, v); self } - pub fn _add_global(mut self, k: String, v: Type) -> Self { - self.globals.insert(k, v); + pub fn add_globals>(self, items: Items) -> Self { + items.for_each(|(name, t)| _ = self.globals.insert(name, t)); self } @@ -128,11 +132,20 @@ impl<'a, I: Iterator>> Parser<'a, I> { self } + pub fn add_locals>(mut self, items: Items) -> Self { + items.for_each(|(name, t)| _ = self.locals.insert(name, t)); + self + } + fn get_object_type(&self, ident: &String) -> Result<&Type, ParseError> { self.locals.get(ident).or(self.globals.get(ident)) .ok_or(ParseError::IdentifierUndefined(ident.clone())) } + fn get_object_types>(&self, items: Names) -> impl Iterator> { + items.map(|x| self.get_object_type(&x)) + } + fn parse(&mut self) -> Result { match self.tokens.next().ok_or(ParseError::NoInput)?.map_err(|e| ParseError::TokenizeError(e))? { Token::Constant(c) => Ok(ParseTree::Constant(c)), @@ -165,18 +178,16 @@ impl<'a, I: Iterator>> Parser<'a, I> { if let Token::Identifier(ident) = token { match op { Op::Equ => Ok(ParseTree::Equ(ident.clone(), - body.clone(), - Box::new(Parser::new(self.tokens.by_ref()) + body, + Box::new(Parser::new(self.tokens.by_ref(), self.globals.borrow_mut()) .locals(self.locals.clone()) - .globals(self.globals.clone()) .add_local(ident, Type::Any) .parse()?)) ), Op::LazyEqu => Ok(ParseTree::LazyEqu(ident.clone(), - body.clone(), - Box::new(Parser::new(self.tokens.by_ref()) + body, + Box::new(Parser::new(self.tokens.by_ref(), self.globals.borrow_mut()) .locals(self.locals.clone()) - .globals(self.globals.clone()) .add_local(ident, Type::Any) .parse()?)) ), @@ -191,8 +202,7 @@ impl<'a, I: Iterator>> Parser<'a, I> { Ok(ParseTree::FunctionDefinition(f.clone(), Box::new( - Parser::new(self.tokens) - .globals(self.globals.clone()) + Parser::new(self.tokens, self.globals.borrow_mut()) .locals(self.locals.clone()) .add_local(f.name().unwrap().to_string(), Type::Function(f.get_type())) .parse()? @@ -237,8 +247,7 @@ impl<'a, I: Iterator>> Parser<'a, I> { .into_iter() .peekable(); - let trees: Vec = Parser::new(&mut array_tokens) - .globals(self.globals.to_owned()) + let trees: Vec = Parser::new(&mut array_tokens, self.globals.borrow_mut()) .locals(self.locals.to_owned()) .collect::>()?; @@ -273,8 +282,7 @@ impl<'a, I: Iterator>> Parser<'a, I> { .into_iter() .peekable(); - let trees: Vec = Parser::new(&mut tokens) - .globals(self.globals.to_owned()) + let trees: Vec = Parser::new(&mut tokens, self.globals.borrow_mut()) .locals(self.locals.to_owned()) .collect::>()?; @@ -302,6 +310,27 @@ impl<'a, I: Iterator>> Parser<'a, I> { Op::Tail => Ok(ParseTree::Tail(Box::new(self.parse()?))), Op::Init => Ok(ParseTree::Init(Box::new(self.parse()?))), Op::Fini => Ok(ParseTree::Fini(Box::new(self.parse()?))), + Op::Export => { + let list = self.parse()?; + let mut g = HashMap::new(); + let list = Executor::new(&mut vec![Ok(list)].into_iter(), &mut g).next().unwrap().map_err(|_| ParseError::NoInput)?; + + if let Value::Array(Type::String, items) = list { + let names = items.into_iter().map(|x| match x { + Value::String(s) => s, + _ => unreachable!(), + }); + + for name in names.clone() { + let t = self.locals.remove(&name).ok_or(ParseError::IdentifierUndefined(name.clone()))?; + self.globals.insert(name, t); + } + + Ok(ParseTree::Export(names.collect())) + } else { + Err(ParseError::NoInput) + } + } op => Err(ParseError::UnwantedToken(Token::Operator(op))), } } @@ -319,8 +348,7 @@ impl<'a, I: Iterator>> Parser<'a, I> { } Ok(Function::lambda(t, args, Box::new( - Parser::new(self.tokens) - .globals(self.globals.clone()) + Parser::new(self.tokens, &mut self.globals) .locals(locals).parse()?))) } @@ -337,8 +365,7 @@ impl<'a, I: Iterator>> Parser<'a, I> { locals.insert(name.clone(), Type::Function(t.clone())); Ok(Function::named(&name, t, args, Box::new( - Parser::new(self.tokens) - .globals(self.globals.clone()) + Parser::new(self.tokens, &mut self.globals) .locals(locals).parse()?))) } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 410343b..b116b42 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -84,6 +84,7 @@ pub enum Op { Tail, Init, Fini, + Export, } #[derive(Debug, Clone, PartialEq)] @@ -128,6 +129,7 @@ impl Token { "tail" => Ok(Token::Operator(Op::Tail)), "init" => Ok(Token::Operator(Op::Init)), "fini" => Ok(Token::Operator(Op::Fini)), + "export" => Ok(Token::Operator(Op::Export)), // Types "Any" => Ok(Token::Type(Type::Any)), @@ -412,4 +414,20 @@ impl std::iter::Iterator for Tokenizer { Err(e) => Some(Err(TokenizeError::IO(e))), } } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use super::*; + + #[test] + fn a() { + let program = ":. map : f .? x [Any] -> [Any]"; + + let tokens: Vec = Tokenizer::from_str(program).unwrap().collect::>().unwrap(); + + println!("{tokens:?}"); + } } \ No newline at end of file From 605a6ae460eeda5c91e34baef955cb055aa9fdfa Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Sat, 19 Oct 2024 22:20:08 -0400 Subject: [PATCH 42/48] parse empty array as [Any] where a type is expected] --- src/parser.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/parser.rs b/src/parser.rs index 9973bcb..3a6496a 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -446,6 +446,23 @@ impl<'a, I: Iterator>> Parser<'a, I> { Ok(Type::Function(FunctionType(Box::new(rett), args.clone()))) }, Some(Ok(Token::Operator(Op::OpenArray))) => { + let mut depth = 1; + let array_tokens = tokens.by_ref().take_while(|t| match t { + Ok(Token::Operator(Op::OpenArray)) => { + depth += 1; + true + }, + Ok(Token::Operator(Op::CloseArray)) => { + depth -= 1; + depth > 0 + } + _ => true, + }).collect::, TokenizeError>>().map_err(|e| ParseError::TokenizeError(e))?; + + if array_tokens.len() == 0 { + return Ok(Type::Array(Box::new(Type::Any))); + } + let t = Self::parse_type(tokens)?; let _ = match tokens.next() { Some(Ok(Token::Operator(Op::CloseArray))) => (), From 0a40cca6ffe841aeb4ae5d3b9705306cd6dbf0b3 Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Tue, 22 Oct 2024 13:18:26 -0400 Subject: [PATCH 43/48] fix performance issue by using reference counting for objects --- src/executor.rs | 32 +++++++++++++++++--------------- src/function.rs | 10 ++++++---- src/lib.rs | 18 ++++++++++-------- src/parser.rs | 4 ++-- 4 files changed, 35 insertions(+), 29 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 034f3a7..3d8f4d3 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -5,6 +5,8 @@ use std::collections::HashMap; use std::fmt::Display; use std::error::Error; use std::io; +use std::rc::Rc; +use std::cell::RefCell; #[derive(Debug)] pub enum RuntimeError { @@ -49,15 +51,15 @@ where I: Iterator> { exprs: &'a mut I, - globals: &'a mut HashMap, - locals: HashMap, + globals: &'a mut HashMap>>, + locals: HashMap>>, } impl<'a, I> Executor<'a, I> where I: Iterator>, { - pub fn new(exprs: &'a mut I, globals: &'a mut HashMap) -> Self { + pub fn new(exprs: &'a mut I, globals: &'a mut HashMap>>) -> Self { Self { exprs, globals, @@ -65,27 +67,27 @@ where } } - pub fn _add_global(self, k: String, v: Object) -> Self { + pub fn _add_global(self, k: String, v: Rc>) -> Self { self.globals.insert(k, v); self } - pub fn locals(mut self, locals: HashMap) -> Self { + pub fn locals(mut self, locals: HashMap>>) -> Self { self.locals = locals; self } - pub fn add_local(mut self, k: String, v: Object) -> Self { + pub fn add_local(mut self, k: String, v: Rc>) -> Self { self.locals.insert(k, v); self } - fn _get_object(&self, ident: &String) -> Result<&Object, RuntimeError> { + fn _get_object(&self, ident: &String) -> Result<&Rc>, RuntimeError> { self.locals.get(ident).or(self.globals.get(ident)) .ok_or(RuntimeError::VariableUndefined(ident.clone())) } - fn get_object_mut(&mut self, ident: &String) -> Result<&mut Object, RuntimeError> { + fn get_object_mut(&mut self, ident: &String) -> Result<&mut Rc>, RuntimeError> { self.locals.get_mut(ident).or(self.globals.get_mut(ident)) .ok_or(RuntimeError::VariableUndefined(ident.clone())) } @@ -236,7 +238,7 @@ where Executor::new(self.exprs, &mut self.globals) .locals(self.locals.clone()) - .add_local(ident, Object::value(value, g, self.locals.to_owned())) + .add_local(ident, Rc::new(RefCell::new(Object::value(value, g, self.locals.to_owned())))) .exec(scope) } }, @@ -247,7 +249,7 @@ where let g = self.globals.clone(); Executor::new(self.exprs, &mut self.globals) .locals(self.locals.clone()) - .add_local(ident, Object::variable(*body, g, self.locals.to_owned())) + .add_local(ident, Rc::new(RefCell::new(Object::variable(*body, g, self.locals.to_owned())))) .exec(scope) } }, @@ -255,7 +257,7 @@ where let g = self.globals.clone(); Executor::new(self.exprs, &mut self.globals) .locals(self.locals.clone()) - .add_local(func.name().unwrap().to_string(), Object::function(func, g, self.locals.clone())) + .add_local(func.name().unwrap().to_string(), Rc::new(RefCell::new(Object::function(func, g, self.locals.clone())))) .exec(scope) }, ParseTree::Compose(x, y) => { @@ -292,17 +294,17 @@ where ParseTree::FunctionCall(ident, args) => { let args = args.into_iter().map(|x| Object::variable(x, self.globals.clone(), self.locals.clone())).collect(); let obj = self.get_object_mut(&ident)?; - let v = obj.eval()?; + let v = obj.borrow_mut().eval()?; match v { - Value::Function(mut f) => f.call(obj.globals(), obj.locals(), args), + Value::Function(mut f) => f.call(obj.borrow().globals(), obj.borrow().locals(), args), _ => Err(RuntimeError::FunctionUndefined(ident.clone())) } }, ParseTree::Variable(ident) => { let obj = self.get_object_mut(&ident)?; - let v = obj.eval()?; + let v = obj.borrow_mut().eval()?; Ok(v) }, @@ -350,7 +352,7 @@ where ParseTree::NonCall(name) => { let obj = self.get_object_mut(&name)?; - let v = obj.eval()?; + let v = obj.borrow_mut().eval()?; Ok(v) } diff --git a/src/function.rs b/src/function.rs index 53fa101..4290f10 100644 --- a/src/function.rs +++ b/src/function.rs @@ -1,9 +1,11 @@ +use std::cell::RefCell; use crate::parser::ParseTree; use crate::executor::{Executor, RuntimeError}; use crate::{Type, Object, Value}; use std::collections::HashMap; use std::fmt::{self, Display}; +use std::rc::Rc; #[derive(Clone, Debug, PartialEq)] pub struct FunctionType(pub Box, pub Vec); @@ -50,8 +52,8 @@ impl Function { } pub(crate) fn call(&mut self, - mut globals: HashMap, - locals: HashMap, + mut globals: HashMap>>, + locals: HashMap>>, args: Vec) -> Result { let mut tree = vec![Ok(*self.body.clone())].into_iter(); @@ -61,11 +63,11 @@ impl Function { .locals(locals.clone()); for (obj, name) in std::iter::zip(args.into_iter(), self.arg_names.clone().into_iter()) { - exec = exec.add_local(name.clone(), obj); + exec = exec.add_local(name.clone(), Rc::new(RefCell::new(obj))); } if let Some(name) = self.name().map(|x| x.to_string()) { - exec = exec.add_local(name, Object::function(self.clone(), g, locals)); + exec = exec.add_local(name, Rc::new(RefCell::new(Object::function(self.clone(), g, locals)))); } exec.next().unwrap() diff --git a/src/lib.rs b/src/lib.rs index 49cfbf8..57545fe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,8 @@ use std::fmt::Display; use std::io::BufRead; use std::fmt; use std::iter::Peekable; +use std::rc::Rc; +use std::cell::RefCell; #[derive(Clone, Debug)] pub enum Type { @@ -101,13 +103,13 @@ enum Cache { #[derive(Clone, Debug, PartialEq)] struct Object { - locals: HashMap, - globals: HashMap, + locals: HashMap>>, + globals: HashMap>>, value: Cache, } impl Object { - pub fn variable(tree: ParseTree, globals: HashMap, locals: HashMap) -> Self { + pub fn variable(tree: ParseTree, globals: HashMap>>, locals: HashMap>>) -> Self { Self { locals, globals, @@ -115,7 +117,7 @@ impl Object { } } - pub fn value(v: Value, globals: HashMap, locals: HashMap) -> Self { + pub fn value(v: Value, globals: HashMap>>, locals: HashMap>>) -> Self { Self { locals, globals, @@ -123,7 +125,7 @@ impl Object { } } - pub fn function(func: Function, globals: HashMap, locals: HashMap) -> Self { + pub fn function(func: Function, globals: HashMap>>, locals: HashMap>>) -> Self { Self { locals, globals, @@ -150,11 +152,11 @@ impl Object { } } - pub fn locals(&self) -> HashMap { + pub fn locals(&self) -> HashMap>> { self.locals.clone() } - pub fn globals(&self) -> HashMap { + pub fn globals(&self) -> HashMap>> { self.globals.clone() } } @@ -162,7 +164,7 @@ impl Object { pub struct Runtime<'a, R: BufRead> { tokenizer: Peekable>, global_types: HashMap, - globals: HashMap, + globals: HashMap>>, parser: Option>>, } diff --git a/src/parser.rs b/src/parser.rs index 3a6496a..dac0235 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -4,7 +4,7 @@ use crate::executor::Executor; use super::{Value, Type, Function, FunctionType}; use super::tokenizer::{Token, TokenizeError, Op}; -use std::borrow::{BorrowMut, Cow}; +use std::borrow::BorrowMut; use std::error; use std::collections::HashMap; use std::fmt::Display; @@ -106,7 +106,7 @@ pub(crate) struct Parser<'a, I: Iterator>> { impl<'a, I: Iterator>> Parser<'a, I> { pub fn new(tokens: &'a mut Peekable, globals: &'a mut HashMap) -> Self { Self { - tokens: tokens, + tokens, globals, locals: HashMap::new() } From d9a576897b14730bbf5df79d90ebdb6371523886 Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Tue, 22 Oct 2024 13:26:48 -0400 Subject: [PATCH 44/48] replace Rc with Arc so that Lamm may be used in an async context --- src/executor.rs | 24 ++++++++++++------------ src/function.rs | 10 +++++----- src/lib.rs | 18 +++++++++--------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 3d8f4d3..394ccee 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -5,7 +5,7 @@ use std::collections::HashMap; use std::fmt::Display; use std::error::Error; use std::io; -use std::rc::Rc; +use std::sync::Arc; use std::cell::RefCell; #[derive(Debug)] @@ -51,15 +51,15 @@ where I: Iterator> { exprs: &'a mut I, - globals: &'a mut HashMap>>, - locals: HashMap>>, + globals: &'a mut HashMap>>, + locals: HashMap>>, } impl<'a, I> Executor<'a, I> where I: Iterator>, { - pub fn new(exprs: &'a mut I, globals: &'a mut HashMap>>) -> Self { + pub fn new(exprs: &'a mut I, globals: &'a mut HashMap>>) -> Self { Self { exprs, globals, @@ -67,27 +67,27 @@ where } } - pub fn _add_global(self, k: String, v: Rc>) -> Self { + pub fn _add_global(self, k: String, v: Arc>) -> Self { self.globals.insert(k, v); self } - pub fn locals(mut self, locals: HashMap>>) -> Self { + pub fn locals(mut self, locals: HashMap>>) -> Self { self.locals = locals; self } - pub fn add_local(mut self, k: String, v: Rc>) -> Self { + pub fn add_local(mut self, k: String, v: Arc>) -> Self { self.locals.insert(k, v); self } - fn _get_object(&self, ident: &String) -> Result<&Rc>, RuntimeError> { + fn _get_object(&self, ident: &String) -> Result<&Arc>, RuntimeError> { self.locals.get(ident).or(self.globals.get(ident)) .ok_or(RuntimeError::VariableUndefined(ident.clone())) } - fn get_object_mut(&mut self, ident: &String) -> Result<&mut Rc>, RuntimeError> { + fn get_object_mut(&mut self, ident: &String) -> Result<&mut Arc>, RuntimeError> { self.locals.get_mut(ident).or(self.globals.get_mut(ident)) .ok_or(RuntimeError::VariableUndefined(ident.clone())) } @@ -238,7 +238,7 @@ where Executor::new(self.exprs, &mut self.globals) .locals(self.locals.clone()) - .add_local(ident, Rc::new(RefCell::new(Object::value(value, g, self.locals.to_owned())))) + .add_local(ident, Arc::new(RefCell::new(Object::value(value, g, self.locals.to_owned())))) .exec(scope) } }, @@ -249,7 +249,7 @@ where let g = self.globals.clone(); Executor::new(self.exprs, &mut self.globals) .locals(self.locals.clone()) - .add_local(ident, Rc::new(RefCell::new(Object::variable(*body, g, self.locals.to_owned())))) + .add_local(ident, Arc::new(RefCell::new(Object::variable(*body, g, self.locals.to_owned())))) .exec(scope) } }, @@ -257,7 +257,7 @@ where let g = self.globals.clone(); Executor::new(self.exprs, &mut self.globals) .locals(self.locals.clone()) - .add_local(func.name().unwrap().to_string(), Rc::new(RefCell::new(Object::function(func, g, self.locals.clone())))) + .add_local(func.name().unwrap().to_string(), Arc::new(RefCell::new(Object::function(func, g, self.locals.clone())))) .exec(scope) }, ParseTree::Compose(x, y) => { diff --git a/src/function.rs b/src/function.rs index 4290f10..c357454 100644 --- a/src/function.rs +++ b/src/function.rs @@ -5,7 +5,7 @@ use crate::{Type, Object, Value}; use std::collections::HashMap; use std::fmt::{self, Display}; -use std::rc::Rc; +use std::sync::Arc; #[derive(Clone, Debug, PartialEq)] pub struct FunctionType(pub Box, pub Vec); @@ -52,8 +52,8 @@ impl Function { } pub(crate) fn call(&mut self, - mut globals: HashMap>>, - locals: HashMap>>, + mut globals: HashMap>>, + locals: HashMap>>, args: Vec) -> Result { let mut tree = vec![Ok(*self.body.clone())].into_iter(); @@ -63,11 +63,11 @@ impl Function { .locals(locals.clone()); for (obj, name) in std::iter::zip(args.into_iter(), self.arg_names.clone().into_iter()) { - exec = exec.add_local(name.clone(), Rc::new(RefCell::new(obj))); + exec = exec.add_local(name.clone(), Arc::new(RefCell::new(obj))); } if let Some(name) = self.name().map(|x| x.to_string()) { - exec = exec.add_local(name, Rc::new(RefCell::new(Object::function(self.clone(), g, locals)))); + exec = exec.add_local(name, Arc::new(RefCell::new(Object::function(self.clone(), g, locals)))); } exec.next().unwrap() diff --git a/src/lib.rs b/src/lib.rs index 57545fe..4cc12e4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ use std::fmt::Display; use std::io::BufRead; use std::fmt; use std::iter::Peekable; -use std::rc::Rc; +use std::sync::Arc; use std::cell::RefCell; #[derive(Clone, Debug)] @@ -103,13 +103,13 @@ enum Cache { #[derive(Clone, Debug, PartialEq)] struct Object { - locals: HashMap>>, - globals: HashMap>>, + locals: HashMap>>, + globals: HashMap>>, value: Cache, } impl Object { - pub fn variable(tree: ParseTree, globals: HashMap>>, locals: HashMap>>) -> Self { + pub fn variable(tree: ParseTree, globals: HashMap>>, locals: HashMap>>) -> Self { Self { locals, globals, @@ -117,7 +117,7 @@ impl Object { } } - pub fn value(v: Value, globals: HashMap>>, locals: HashMap>>) -> Self { + pub fn value(v: Value, globals: HashMap>>, locals: HashMap>>) -> Self { Self { locals, globals, @@ -125,7 +125,7 @@ impl Object { } } - pub fn function(func: Function, globals: HashMap>>, locals: HashMap>>) -> Self { + pub fn function(func: Function, globals: HashMap>>, locals: HashMap>>) -> Self { Self { locals, globals, @@ -152,11 +152,11 @@ impl Object { } } - pub fn locals(&self) -> HashMap>> { + pub fn locals(&self) -> HashMap>> { self.locals.clone() } - pub fn globals(&self) -> HashMap>> { + pub fn globals(&self) -> HashMap>> { self.globals.clone() } } @@ -164,7 +164,7 @@ impl Object { pub struct Runtime<'a, R: BufRead> { tokenizer: Peekable>, global_types: HashMap, - globals: HashMap>>, + globals: HashMap>>, parser: Option>>, } From 2a987ae4658988be5443831d926f1cb39d3fd88a Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Tue, 22 Oct 2024 13:50:28 -0400 Subject: [PATCH 45/48] final version with Arc> --- src/executor.rs | 56 +++++++++++++++++++++++++++++++++++-------------- src/function.rs | 10 ++++----- src/lib.rs | 26 ++++++++++++++--------- 3 files changed, 61 insertions(+), 31 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 394ccee..b20834c 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -5,7 +5,7 @@ use std::collections::HashMap; use std::fmt::Display; use std::error::Error; use std::io; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use std::cell::RefCell; #[derive(Debug)] @@ -51,15 +51,15 @@ where I: Iterator> { exprs: &'a mut I, - globals: &'a mut HashMap>>, - locals: HashMap>>, + globals: &'a mut HashMap>>, + locals: HashMap>>, } impl<'a, I> Executor<'a, I> where I: Iterator>, { - pub fn new(exprs: &'a mut I, globals: &'a mut HashMap>>) -> Self { + pub fn new(exprs: &'a mut I, globals: &'a mut HashMap>>) -> Self { Self { exprs, globals, @@ -67,27 +67,27 @@ where } } - pub fn _add_global(self, k: String, v: Arc>) -> Self { + pub fn _add_global(self, k: String, v: Arc>) -> Self { self.globals.insert(k, v); self } - pub fn locals(mut self, locals: HashMap>>) -> Self { + pub fn locals(mut self, locals: HashMap>>) -> Self { self.locals = locals; self } - pub fn add_local(mut self, k: String, v: Arc>) -> Self { + pub fn add_local(mut self, k: String, v: Arc>) -> Self { self.locals.insert(k, v); self } - fn _get_object(&self, ident: &String) -> Result<&Arc>, RuntimeError> { + fn _get_object(&self, ident: &String) -> Result<&Arc>, RuntimeError> { self.locals.get(ident).or(self.globals.get(ident)) .ok_or(RuntimeError::VariableUndefined(ident.clone())) } - fn get_object_mut(&mut self, ident: &String) -> Result<&mut Arc>, RuntimeError> { + fn get_object_mut(&mut self, ident: &String) -> Result<&mut Arc>, RuntimeError> { self.locals.get_mut(ident).or(self.globals.get_mut(ident)) .ok_or(RuntimeError::VariableUndefined(ident.clone())) } @@ -96,6 +96,30 @@ where self.locals.contains_key(ident) || self.globals.contains_key(ident) } + fn eval(obj: &mut Arc>) -> Result { + let mut guard = obj.lock().unwrap(); + + let v = guard.eval()?; + + Ok(v) + } + + fn obj_locals(obj: &Arc>) -> HashMap>> { + let guard = obj.lock().unwrap(); + + let locals = guard.locals(); + + locals + } + + fn obj_globals(obj: &Arc>) -> HashMap>> { + let guard = obj.lock().unwrap(); + + let locals = guard.globals(); + + locals + } + pub fn exec(&mut self, tree: Box) -> Result { match *tree { ParseTree::Add(x, y) => match (self.exec(x)?, self.exec(y)?) { @@ -238,7 +262,7 @@ where Executor::new(self.exprs, &mut self.globals) .locals(self.locals.clone()) - .add_local(ident, Arc::new(RefCell::new(Object::value(value, g, self.locals.to_owned())))) + .add_local(ident, Arc::new(Mutex::new(Object::value(value, g, self.locals.to_owned())))) .exec(scope) } }, @@ -249,7 +273,7 @@ where let g = self.globals.clone(); Executor::new(self.exprs, &mut self.globals) .locals(self.locals.clone()) - .add_local(ident, Arc::new(RefCell::new(Object::variable(*body, g, self.locals.to_owned())))) + .add_local(ident, Arc::new(Mutex::new(Object::variable(*body, g, self.locals.to_owned())))) .exec(scope) } }, @@ -257,7 +281,7 @@ where let g = self.globals.clone(); Executor::new(self.exprs, &mut self.globals) .locals(self.locals.clone()) - .add_local(func.name().unwrap().to_string(), Arc::new(RefCell::new(Object::function(func, g, self.locals.clone())))) + .add_local(func.name().unwrap().to_string(), Arc::new(Mutex::new(Object::function(func, g, self.locals.clone())))) .exec(scope) }, ParseTree::Compose(x, y) => { @@ -294,17 +318,17 @@ where ParseTree::FunctionCall(ident, args) => { let args = args.into_iter().map(|x| Object::variable(x, self.globals.clone(), self.locals.clone())).collect(); let obj = self.get_object_mut(&ident)?; - let v = obj.borrow_mut().eval()?; + let v = Self::eval(obj)?; match v { - Value::Function(mut f) => f.call(obj.borrow().globals(), obj.borrow().locals(), args), + Value::Function(mut f) => f.call(Self::obj_globals(obj), Self::obj_locals(obj), args), _ => Err(RuntimeError::FunctionUndefined(ident.clone())) } }, ParseTree::Variable(ident) => { let obj = self.get_object_mut(&ident)?; - let v = obj.borrow_mut().eval()?; + let v = obj.lock().unwrap().eval()?; Ok(v) }, @@ -352,7 +376,7 @@ where ParseTree::NonCall(name) => { let obj = self.get_object_mut(&name)?; - let v = obj.borrow_mut().eval()?; + let v = obj.lock().unwrap().eval()?; Ok(v) } diff --git a/src/function.rs b/src/function.rs index c357454..bf9fd7c 100644 --- a/src/function.rs +++ b/src/function.rs @@ -5,7 +5,7 @@ use crate::{Type, Object, Value}; use std::collections::HashMap; use std::fmt::{self, Display}; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; #[derive(Clone, Debug, PartialEq)] pub struct FunctionType(pub Box, pub Vec); @@ -52,8 +52,8 @@ impl Function { } pub(crate) fn call(&mut self, - mut globals: HashMap>>, - locals: HashMap>>, + mut globals: HashMap>>, + locals: HashMap>>, args: Vec) -> Result { let mut tree = vec![Ok(*self.body.clone())].into_iter(); @@ -63,11 +63,11 @@ impl Function { .locals(locals.clone()); for (obj, name) in std::iter::zip(args.into_iter(), self.arg_names.clone().into_iter()) { - exec = exec.add_local(name.clone(), Arc::new(RefCell::new(obj))); + exec = exec.add_local(name.clone(), Arc::new(Mutex::new(obj))); } if let Some(name) = self.name().map(|x| x.to_string()) { - exec = exec.add_local(name, Arc::new(RefCell::new(Object::function(self.clone(), g, locals)))); + exec = exec.add_local(name, Arc::new(Mutex::new(Object::function(self.clone(), g, locals)))); } exec.next().unwrap() diff --git a/src/lib.rs b/src/lib.rs index 4cc12e4..de35b96 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ use std::fmt::Display; use std::io::BufRead; use std::fmt; use std::iter::Peekable; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use std::cell::RefCell; #[derive(Clone, Debug)] @@ -101,15 +101,21 @@ enum Cache { Uncached(ParseTree), } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] struct Object { - locals: HashMap>>, - globals: HashMap>>, + locals: HashMap>>, + globals: HashMap>>, value: Cache, } +impl PartialEq for Object { + fn eq(&self, other: &Self) -> bool { + self.value == other.value + } +} + impl Object { - pub fn variable(tree: ParseTree, globals: HashMap>>, locals: HashMap>>) -> Self { + pub fn variable(tree: ParseTree, globals: HashMap>>, locals: HashMap>>) -> Self { Self { locals, globals, @@ -117,7 +123,7 @@ impl Object { } } - pub fn value(v: Value, globals: HashMap>>, locals: HashMap>>) -> Self { + pub fn value(v: Value, globals: HashMap>>, locals: HashMap>>) -> Self { Self { locals, globals, @@ -125,7 +131,7 @@ impl Object { } } - pub fn function(func: Function, globals: HashMap>>, locals: HashMap>>) -> Self { + pub fn function(func: Function, globals: HashMap>>, locals: HashMap>>) -> Self { Self { locals, globals, @@ -152,11 +158,11 @@ impl Object { } } - pub fn locals(&self) -> HashMap>> { + pub fn locals(&self) -> HashMap>> { self.locals.clone() } - pub fn globals(&self) -> HashMap>> { + pub fn globals(&self) -> HashMap>> { self.globals.clone() } } @@ -164,7 +170,7 @@ impl Object { pub struct Runtime<'a, R: BufRead> { tokenizer: Peekable>, global_types: HashMap, - globals: HashMap>>, + globals: HashMap>>, parser: Option>>, } From 8fbb4780df6e60ea12f3f631b8187382ef7a3b1c Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Tue, 22 Oct 2024 14:40:39 -0400 Subject: [PATCH 46/48] move args to make more sense --- src/executor.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index b20834c..88d2a30 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -316,12 +316,17 @@ where self.exec(isfalse) }, ParseTree::FunctionCall(ident, args) => { - let args = args.into_iter().map(|x| Object::variable(x, self.globals.clone(), self.locals.clone())).collect(); let obj = self.get_object_mut(&ident)?; let v = Self::eval(obj)?; match v { - Value::Function(mut f) => f.call(Self::obj_globals(obj), Self::obj_locals(obj), args), + Value::Function(mut f) => { + let args = args.into_iter() + .map(|x| Object::variable(x, self.globals.clone(), self.locals.clone())) + .collect(); + + f.call(Self::obj_globals(obj), Self::obj_locals(obj), args) + }, _ => Err(RuntimeError::FunctionUndefined(ident.clone())) } }, From ae7600ba81eb0887d4c1ad4b17e6f2feceb2e3aa Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Wed, 23 Oct 2024 22:10:48 -0400 Subject: [PATCH 47/48] fix type parsing bugs --- src/executor.rs | 4 ++- src/function.rs | 2 +- src/parser.rs | 69 ++++++++++++++++++++++++++++-------------------- src/tokenizer.rs | 21 ++++++++++++--- 4 files changed, 62 insertions(+), 34 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 88d2a30..ef6fc50 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -317,6 +317,8 @@ where }, ParseTree::FunctionCall(ident, args) => { let obj = self.get_object_mut(&ident)?; + let globals = Self::obj_globals(obj); + let locals = Self::obj_locals(obj); let v = Self::eval(obj)?; match v { @@ -325,7 +327,7 @@ where .map(|x| Object::variable(x, self.globals.clone(), self.locals.clone())) .collect(); - f.call(Self::obj_globals(obj), Self::obj_locals(obj), args) + f.call(globals, locals, args) }, _ => Err(RuntimeError::FunctionUndefined(ident.clone())) } diff --git a/src/function.rs b/src/function.rs index bf9fd7c..49db30a 100644 --- a/src/function.rs +++ b/src/function.rs @@ -18,7 +18,7 @@ impl Display for FunctionType { #[derive(Clone, Debug, PartialEq)] pub struct Function { - name: Option, + pub(crate) name: Option, t: FunctionType, arg_names: Vec, body: Box, diff --git a/src/parser.rs b/src/parser.rs index dac0235..a577859 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -112,13 +112,16 @@ impl<'a, I: Iterator>> Parser<'a, I> { } } + pub fn add_global(self, k: String, v: Type) -> Self { self.globals.insert(k, v); self } pub fn add_globals>(self, items: Items) -> Self { - items.for_each(|(name, t)| _ = self.globals.insert(name, t)); + items.for_each(|(name, t)| { + self.globals.insert(name, t); + }); self } @@ -133,7 +136,9 @@ impl<'a, I: Iterator>> Parser<'a, I> { } pub fn add_locals>(mut self, items: Items) -> Self { - items.for_each(|(name, t)| _ = self.locals.insert(name, t)); + items.for_each(|(name, t)| { + self.locals.insert(name, t); + }); self } @@ -369,22 +374,27 @@ impl<'a, I: Iterator>> Parser<'a, I> { .locals(locals).parse()?))) } - fn parse_function_declaration(tokens: &mut Peekable, arg_count: usize) -> Result<(FunctionType, Vec), ParseError> { + fn parse_function_declaration( + tokens: &mut Peekable, + arg_count: usize) -> Result<(FunctionType, Vec), ParseError> + { let args: Vec<(Type, String)> = (0..arg_count) .map(|_| Self::parse_function_declaration_parameter(tokens)) .collect::>()?; let (types, names): (Vec<_>, Vec<_>) = args.into_iter().unzip(); - let mut ret = Type::Any; - - if tokens.next_if(|x| matches!(x, Ok(Token::Operator(Op::Arrow)))).is_some() { - ret = Self::parse_type(tokens)?; - } + let ret = if tokens.next_if(|x| matches!(x, Ok(Token::Operator(Op::Arrow)))).is_some() { + Self::parse_type(tokens)? + } else { + Type::Any + }; Ok((FunctionType(Box::new(ret), types), names)) } - fn parse_function_declaration_parameter(mut tokens: &mut Peekable) -> Result<(Type, String), ParseError> { + fn parse_function_declaration_parameter( + mut tokens: &mut Peekable) -> Result<(Type, String), ParseError> + { match tokens.next() { // untyped variable Some(Ok(Token::Identifier(x))) => Ok((Type::Any, x)), @@ -412,7 +422,7 @@ impl<'a, I: Iterator>> Parser<'a, I> { let mut ret = Type::Any; // this is annoying - // inside of the next_if closure, we already can know that its an error + // inside the next_if closure, we already can know that its an error // and return it, but we cannot return out of a closure if let Some(t) = tokens.next_if(|x| matches!(x, Ok(Token::Operator(Op::Arrow)))) { @@ -433,20 +443,16 @@ impl<'a, I: Iterator>> Parser<'a, I> { } } - fn parse_type(tokens: &mut Peekable) -> Result { + // for some dumbass reason, + // this is the only code that breaks if it doesn't take an impl Iterator instead of simply I ... + fn parse_type(tokens: &mut Peekable>>) -> Result { match tokens.next() { Some(Ok(Token::Type(t))) => Ok(t), - Some(Ok(Token::Operator(Op::FunctionDefine(n)))) => { - let args: Vec = (0..n) - .map(|_| Self::parse_type(tokens)) - .collect::>()?; - - let rett = Self::parse_type(tokens)?; - - Ok(Type::Function(FunctionType(Box::new(rett), args.clone()))) - }, Some(Ok(Token::Operator(Op::OpenArray))) => { let mut depth = 1; + + // take tokens until we reach the end of this array + // if we don't collect them here it causes rust to overflow computing the types let array_tokens = tokens.by_ref().take_while(|t| match t { Ok(Token::Operator(Op::OpenArray)) => { depth += 1; @@ -459,18 +465,25 @@ impl<'a, I: Iterator>> Parser<'a, I> { _ => true, }).collect::, TokenizeError>>().map_err(|e| ParseError::TokenizeError(e))?; - if array_tokens.len() == 0 { - return Ok(Type::Array(Box::new(Type::Any))); - } + // ... thanks to this conversion here. The compiler complains that the types don't + // match. there is code elsewhere in this codebase that looks exactly like this and + // still simply uses &mut Peekable as the type. I don't understand why this code + // is special, but we have to do horribleness for it to work. + let mut array_tokens = array_tokens + .into_iter() + .map(|t| Ok(t)) + .collect::>>() + .into_iter() + .peekable(); - let t = Self::parse_type(tokens)?; - let _ = match tokens.next() { - Some(Ok(Token::Operator(Op::CloseArray))) => (), - _ => return Err(ParseError::UnmatchedArrayClose), + let t = match Self::parse_type(&mut array_tokens) { + Ok(t) => t, + Err(ParseError::UnexpectedEndInput) => Type::Any, + Err(e) => return Err(e), }; Ok(Type::Array(Box::new(t))) - } + }, Some(Ok(t)) => Err(ParseError::UnwantedToken(t.clone())), Some(Err(e)) => Err(ParseError::TokenizeError(e)), None => Err(ParseError::UnexpectedEndInput), diff --git a/src/tokenizer.rs b/src/tokenizer.rs index b116b42..34df5da 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -419,15 +419,28 @@ impl std::iter::Iterator for Tokenizer { #[cfg(test)] mod tests { use std::str::FromStr; - + use crate::parser::Parser; use super::*; #[test] - fn a() { - let program = ":. map : f .? x [Any] -> [Any]"; + fn tokenizer() { + let program = ": length ?. x [] -> Int ?? x + 1 length tail x 0 length [ 1 2 3 ]"; let tokens: Vec = Tokenizer::from_str(program).unwrap().collect::>().unwrap(); - println!("{tokens:?}"); + println!("{tokens:#?}"); + } + + #[test] + fn a() { + let program = ": length ?. x [] -> Int ?? x + 1 length tail x 0 length [ 1 2 3 ]"; + + let mut tokenizer = Tokenizer::from_str(program).unwrap().peekable(); + + let mut globals = HashMap::new(); + let mut parser = Parser::new(&mut tokenizer, &mut globals); + + let tree = parser.next(); + println!("{tree:#?}"); } } \ No newline at end of file From 421430e6105f92ccce1a4804a0a2d47f18b8ecd5 Mon Sep 17 00:00:00 2001 From: mins <149207799+minneelyyyy@users.noreply.github.com> Date: Wed, 23 Oct 2024 22:22:28 -0400 Subject: [PATCH 48/48] update version to 0.2.0 --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cb41074..517aea6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "lamm" -version = "0.1.0" +version = "0.2.0" edition = "2021" license = "MIT" description = "a simple, functional paradigm programming language which uses Polish notation" repository = "https://github.com/minneelyyyy/lamm" [dependencies] -regex = "1.11" \ No newline at end of file +regex = "1.11"