diff --git a/Cargo.lock b/Cargo.lock index cbad13b..faf2749 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -120,9 +120,9 @@ name = "bot" version = "0.1.0" dependencies = [ "dotenv", + "lamm", "poise", "rand", - "regex", "tokio", ] @@ -734,6 +734,14 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "lamm" +version = "0.1.0" +source = "git+https://github.com/minneelyyyy/lamm?branch=dev#4b2fefd7982f0701c9526846dece0541b6224a8f" +dependencies = [ + "regex", +] + [[package]] name = "libc" version = "0.2.159" diff --git a/Cargo.toml b/Cargo.toml index f6b39ab..5b423d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,4 +8,4 @@ poise = "0.6.1" tokio = { version = "1", features = ["full"] } dotenv = "0.15.0" rand = "0.8.5" -regex = "1.11" \ No newline at end of file +lamm = { git = "https://github.com/minneelyyyy/lamm", branch = "dev" } diff --git a/src/commands/eval.rs b/src/commands/eval.rs new file mode 100644 index 0000000..de0a461 --- /dev/null +++ b/src/commands/eval.rs @@ -0,0 +1,28 @@ +use crate::common::{Context, Error}; +use std::io::Cursor; + +/// Evaluates a Lamm program +#[poise::command(slash_command, prefix_command)] +pub async fn eval(ctx: Context<'_>, + #[rest] + expr: String) -> Result<(), Error> +{ + let values = lamm::evaluate(Cursor::new(expr)); + + let output = values.fold(Ok(String::new()), |acc, v| { + if acc.is_err() { + return acc; + }; + + let x = acc.unwrap(); + + match v { + Ok(v) => Ok(format!("{x}\n{v}")), + Err(e) => Err(e), + } + }); + + ctx.reply(output?).await?; + + Ok(()) +} \ No newline at end of file diff --git a/src/commands/eval/executor.rs b/src/commands/eval/executor.rs deleted file mode 100644 index 02bbc93..0000000 --- a/src/commands/eval/executor.rs +++ /dev/null @@ -1,338 +0,0 @@ -use super::{Value, Type, FunctionDeclaration}; -use super::parser::{ParseTree, ParseError}; -use super::tokenizer::Op; - -use std::collections::HashMap; -use std::borrow::Cow; -use std::fmt::Display; -use std::error::Error; - -#[derive(Debug)] -pub enum RuntimeError { - ParseError(ParseError), - NoOverloadForTypes(String, Vec), - ImmutableError(String), - VariableUndefined(String), - FunctionUndeclared(String), - FunctionUndefined(String), - NotAVariable(String), - ParseFail(String, Type), -} - -impl Display for RuntimeError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::ParseError(e) => write!(f, "{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(", ")), - Self::ImmutableError(ident) => write!(f, "`{ident}` already exists and cannot be redefined"), - Self::VariableUndefined(ident) => write!(f, "variable `{ident}` was not defined"), - Self::FunctionUndeclared(ident) => write!(f, "function `{ident}` was not declared"), - 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), - } - } -} - -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)] -struct Function { - decl: FunctionDeclaration, - body: Option>, -} - -#[derive(Clone, Debug)] -enum Object { - Variable(Evaluation), - Function(Function), -} - -pub struct Executor>> { - exprs: I, - globals: HashMap, -} - -impl>> Executor { - pub fn new(exprs: I) -> Self { - Self { - exprs, - globals: HashMap::new(), - } - } - - fn exec( - &mut self, - tree: ParseTree, - locals: &mut Cow>) -> Result - { - 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)), - (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x + y)), - (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)?) { - (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)?) { - (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)), - (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)?) { - (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)?) { - (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)?) { - (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)?) { - (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)), - (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)?) { - (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)?) { - (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)?) { - (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)? { - Value::Bool(x) => Ok(Value::Bool(!x)), - x => Err(RuntimeError::NoOverloadForTypes("not".into(), vec![x])) - }, - ParseTree::Equ(ident, body, scope) => { - if self.globals.contains_key(&ident) || locals.contains_key(&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))); - - self.exec(*scope, &mut Cow::Borrowed(&locals)) - } - }, - ParseTree::LazyEqu(ident, body, scope) => { - if self.globals.contains_key(&ident) || locals.contains_key(&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)) - } - }, - ParseTree::GlobalEqu(ident, body) => todo!(), - ParseTree::LazyGlobalEqu(ident, body) => todo!(), - ParseTree::FunctionDefinition(ident, args, r, body, scope) => { - let existing = locals.get(&ident).or(self.globals.get(&ident)).cloned(); - - match existing { - Some(_) => Err(RuntimeError::ImmutableError(ident.clone())), - None => { - let locals = locals.to_mut(); - - locals.insert(ident.clone(), Object::Function(Function { - decl: FunctionDeclaration { name: ident.clone(), r, args }, - body: Some(body) - })); - - self.exec(*scope, &mut Cow::Borrowed(&locals)) - } - } - }, - ParseTree::Compose(x, y) => { - 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)? { - 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) - } else { - Ok(Value::Nil) - }, - 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) - } else { - self.exec(*isfalse, locals) - }, - ParseTree::FunctionCall(ident, args) => { - eprintln!("{locals:?}"); - 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()))?; - - 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))?))); - } - - self.exec(*body, &mut Cow::Borrowed(&locals)) - } else { - Err(RuntimeError::FunctionUndeclared(ident.clone())) - } - }, - ParseTree::Variable(ident) => { - let locals = locals.to_mut(); - - let obj = locals.get(&ident).or(self.globals.get(&ident)).cloned(); - - 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())) - } - }, - ParseTree::Constant(value) => Ok(value), - 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 })), - Value::String(x) => { - let r: i64 = x.parse().map_err(|_| RuntimeError::ParseFail(x.clone(), Type::Int))?; - Ok(Value::Int(r)) - } - x => Err(RuntimeError::NoOverloadForTypes("int".into(), vec![x])), - }, - 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 })), - Value::String(x) => { - let r: f64 = x.parse().map_err(|_| RuntimeError::ParseFail(x.clone(), Type::Int))?; - Ok(Value::Float(r)) - } - x => Err(RuntimeError::NoOverloadForTypes("float".into(), vec![x])), - }, - 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)?))), - } - } -} - -impl>> Iterator for Executor { - type Item = Result; - - fn next(&mut self) -> Option { - let expr = self.exprs.next(); - - match expr { - Some(Ok(expr)) => Some(self.exec(expr, &mut Cow::Borrowed(&HashMap::new()))), - Some(Err(e)) => Some(Err(RuntimeError::ParseError(e))), - None => None, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::commands::eval::tokenizer; - use crate::commands::eval::parser; - use std::str::FromStr; - - #[test] - fn recursion() { - let program = r#":. sum'range n m - ?? < n m - + n sum'range + n 1 m - n - sum'range 1 10"#; - let tok = tokenizer::Tokenizer::from_str(program).unwrap(); - let parse = parser::Parser::new(tok); - - let mut run = Executor::new(parse.inspect(|t| eprintln!("{t:?}"))); - - assert_eq!(run.next().unwrap().unwrap(), Value::Int(45)); - } -} \ No newline at end of file diff --git a/src/commands/eval/mod.rs b/src/commands/eval/mod.rs deleted file mode 100644 index 9ffb8c8..0000000 --- a/src/commands/eval/mod.rs +++ /dev/null @@ -1,94 +0,0 @@ -use crate::common::{Context, Error}; - -use std::str::FromStr; -use std::fmt::Display; - -mod tokenizer; -mod parser; -mod executor; - -/// Evaluates an expression (uses Polish Notation) -#[poise::command(slash_command, prefix_command)] -pub async fn eval(ctx: Context<'_>, - #[rest] - expr: String) -> Result<(), Error> -{ - let expr = expr.strip_prefix("```").and_then(|s| s.strip_suffix("```")).unwrap_or(&expr); - - let tok = tokenizer::Tokenizer::from_str(&expr).unwrap(); // Error type is () and never returned - let exprs = parser::Parser::new(tok); - let exec = executor::Executor::new(exprs); - - let values: Vec = exec.collect::>()?; - - let reply: String = values.iter().fold(String::new(), |acc, s| acc + &format!("{s}\n")); - - ctx.reply(reply).await?; - - Ok(()) -} - -#[derive(Clone, Debug)] -enum Type { - Float, - Int, - Bool, - String, - Nil, - Any, - Function(Box, Vec), -} - -impl Display for Type { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", match self { - Self::Float => "Float".into(), - Self::Int => "Int".into(), - Self::Bool => "Bool".into(), - Self::String => "String".into(), - Self::Nil => "Nil".into(), - Self::Any => "Any".into(), - Self::Function(r, _) => format!("Function -> {}", *r) - }) - } -} - -#[derive(Clone, Debug, PartialEq)] -pub enum Value { - Float(f64), - Int(i64), - Bool(bool), - String(String), - Nil, -} - -impl Value { - pub fn get_type(&self) -> Type { - match self { - Self::Float(_) => Type::Float, - Self::Int(_) => Type::Int, - Self::Bool(_) => Type::Bool, - Self::String(_) => Type::String, - Self::Nil => Type::Nil, - } - } -} - -impl Display for Value { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - 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::Nil => write!(f, "nil"), - } - } -} - -#[derive(Clone, Debug)] -pub struct FunctionDeclaration { - name: String, - r: Type, - args: Vec<(String, Type)>, -} diff --git a/src/commands/eval/parser.rs b/src/commands/eval/parser.rs deleted file mode 100644 index 156740d..0000000 --- a/src/commands/eval/parser.rs +++ /dev/null @@ -1,303 +0,0 @@ -use super::{Type, Value, FunctionDeclaration}; -use super::tokenizer::{Token, TokenizeError, Op}; - -use std::error; -use std::collections::HashMap; -use std::fmt::Display; -use std::borrow::Cow; - -#[derive(Debug)] -pub enum ParseError { - NoInput, - UnexpectedEndInput, - IdentifierUndefined(String), - InvalidIdentifier, - FunctionUndefined(String), - VariableUndefined(String), - TokenizeError(TokenizeError), -} - -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::InvalidIdentifier => write!(f, "Invalid identifier"), - ParseError::FunctionUndefined(name) => write!(f, "Undefined function `{name}`"), - ParseError::VariableUndefined(name) => write!(f, "Undefined variable `{name}`"), - ParseError::NoInput => write!(f, "No input given"), - ParseError::TokenizeError(e) => write!(f, "{e}"), - } - } -} - -impl error::Error for ParseError {} - -#[derive(Clone, Debug)] -pub enum ParseTree { - // Mathematical Operators - Add(Box, Box), - Sub(Box, Box), - Mul(Box, Box), - Div(Box, Box), - Exp(Box, Box), - Mod(Box, Box), - - // Boolean Operations - EqualTo(Box, Box), - GreaterThan(Box, Box), - GreaterThanOrEqualTo(Box, Box), - LessThan(Box, Box), - LessThanOrEqualTo(Box, Box), - Not(Box), - - // Defining Objects - Equ(String, Box, Box), - LazyEqu(String, Box, Box), - GlobalEqu(String, Box), - LazyGlobalEqu(String, Box), - FunctionDefinition(String, Vec<(String, Type)>, Type, Box, Box), - - // Functional Operations - Compose(Box, Box), - Id(Box), - - // Branching - If(Box, Box), - IfElse(Box, Box, Box), - - // Evaluations - FunctionCall(String, Vec), - Variable(String), - Constant(Value), - - // Type Casts - ToInt(Box), - ToFloat(Box), - ToBool(Box), - ToString(Box), -} - -impl ParseTree { - fn parse( - tokens: &mut I, - globals: &HashMap, - locals: &mut Cow>) -> Result - where - I: Iterator>, - { - match tokens.next() { - Some(Ok(token)) => { - 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(decl) = locals.clone().get(&ident).or(globals.clone().get(&ident)) { - let args = decl.args.iter() - .map(|_| ParseTree::parse(tokens, globals, locals)).collect::, ParseError>>()?; - - Ok(ParseTree::FunctionCall(ident.clone(), args)) - } else { - Ok(ParseTree::Variable(ident.clone())) - } - } - 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::Equ | Op::LazyEqu | Op::GlobalEqu | Op::LazyGlobalEqu => { - let token = tokens.next() - .ok_or(ParseError::UnexpectedEndInput)? - .map_err(|e| ParseError::TokenizeError(e))?; - - if let Token::Identifier(ident) = token { - match op { - Op::Equ => Ok(ParseTree::Equ(ident.clone(), - Box::new(ParseTree::parse(tokens, globals, locals)?), - Box::new(ParseTree::parse(tokens, globals, locals)?) - )), - Op::LazyEqu => Ok(ParseTree::LazyEqu(ident.clone(), - Box::new(ParseTree::parse(tokens, globals, locals)?), - Box::new(ParseTree::parse(tokens, globals, locals)?) - )), - Op::GlobalEqu => Ok(ParseTree::GlobalEqu(ident.clone(), - Box::new(ParseTree::parse(tokens, globals, locals)?) - )), - Op::LazyGlobalEqu => Ok(ParseTree::LazyGlobalEqu(ident.clone(), - Box::new(ParseTree::parse(tokens, globals, locals)?) - )), - _ => panic!("Operator literally changed under your nose"), - } - } else { - Err(ParseError::InvalidIdentifier) - } - } - Op::FunctionDeclare(nargs) => { - let token = tokens.next() - .ok_or(ParseError::UnexpectedEndInput)? - .map_err(|e| ParseError::TokenizeError(e))?; - - if let Token::Identifier(ident) = token { - let args: Vec<(String, Type)> = tokens.take(nargs) - .map(|token| match token { - Ok(Token::Identifier(ident)) => Ok((ident, Type::Any)), - Ok(_) => Err(ParseError::InvalidIdentifier), - Err(e) => Err(ParseError::TokenizeError(e)), - }) - .collect::, ParseError>>()?; - - let locals = locals.to_mut(); - - 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 { - 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::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)?))), - } - } - } - }, - Some(Err(e)) => Err(ParseError::TokenizeError(e)), - None => Err(ParseError::NoInput), - } - } -} - -pub 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(mut self, g: HashMap) -> Self { - self.globals = g; - self - } -} - -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)); - - match tree { - Ok(tree) => Some(Ok(tree)), - Err(e) => { - match e { - ParseError::NoInput => None, - _ => Some(Err(e)), - } - } - } - } -} - -#[cfg(test)] -mod tests { - use crate::commands::eval::tokenizer::Tokenizer; - use super::*; - - use std::str::FromStr; - - #[test] - fn parsing() { - let program = "+ 2 2 = pi 3.14 : area r * pi ** r 2 area 16"; - - let tokenizer = Tokenizer::from_str(program).expect("couldnt create a paser"); - let parser = Parser::new(tokenizer); - - for tree in parser { - println!("{tree:?}"); - } - } -} diff --git a/src/commands/eval/tokenizer.rs b/src/commands/eval/tokenizer.rs deleted file mode 100644 index 32e6849..0000000 --- a/src/commands/eval/tokenizer.rs +++ /dev/null @@ -1,216 +0,0 @@ -use std::{error, io}; -use std::collections::VecDeque; - -use super::Value; -use std::fmt::{Display, Formatter}; -use std::io::{BufRead, Cursor}; - -#[derive(Debug)] -pub enum TokenizeError { - InvalidDynamicOperator(String), - InvalidNumericConstant(String), - InvalidIdentifier(String), - UnableToMatchToken(String), - IO(io::Error), -} - -impl Display for TokenizeError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - TokenizeError::InvalidDynamicOperator(op) - => write!(f, "invalid dynamic operator `{op}`"), - TokenizeError::InvalidNumericConstant(t) - => write!(f, "invalid numeric constant `{t}`"), - TokenizeError::InvalidIdentifier(ident) - => write!(f, "invalid identifier `{ident}`"), - TokenizeError::UnableToMatchToken(token) - => write!(f, "the token `{token}` was unable to be parsed"), - TokenizeError::IO(io) => write!(f, "{io}") - } - } -} - -impl error::Error for TokenizeError {} - -#[derive(Debug, Clone)] -pub enum Op { - Add, - Sub, - Mul, - Div, - Exp, - Equ, - Mod, - LazyEqu, - GlobalEqu, - LazyGlobalEqu, - FunctionDeclare(usize), - Compose, - Id, - If, - IfElse, - GreaterThan, - LessThan, - EqualTo, - GreaterThanOrEqualTo, - LessThanOrEqualTo, - Not, - IntCast, - FloatCast, - BoolCast, - StringCast, -} - -#[derive(Debug, Clone)] -pub enum Token { - Identifier(String), - Operator(Op), - 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 valid_identifier(c: char) -> bool { - c.is_alphanumeric() || c == '\'' || c == '_' -} - -impl Token { - fn parse(s: &str) -> Result { - let string = regex::Regex::new(r#"".+""#).expect("LOL!"); - - if string.is_match(s) { - return Ok(Token::Constant(Value::String(s[1..s.len() - 1].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::GlobalEqu)), - ".>" => Ok(Token::Operator(Op::LazyGlobalEqu)), - "~" => 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, constants, and identifiers - _ => { - 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 s.starts_with(|c| char::is_digit(c, 10) || c == '-') { - 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 if s.starts_with(valid_identifier) { - let valid = s.chars().skip(1).all(valid_identifier); - valid.then(|| Token::Identifier(s.to_string())).ok_or(TokenizeError::InvalidIdentifier(s.to_string())) - } else { - Err(TokenizeError::UnableToMatchToken(s.to_string())) - } - } - } - } -} - -pub struct Tokenizer { - reader: R, - tokens: VecDeque, -} - -impl Tokenizer { - pub fn new(reader: R) -> Self { - Self { - reader, - tokens: VecDeque::new(), - } - } -} - -impl std::str::FromStr for Tokenizer> { - type Err = (); - - fn from_str(s: &str) -> Result { - let cursor = Cursor::new(s.to_string()); - Ok(Tokenizer::new(cursor)) - } -} - -impl std::iter::Iterator for Tokenizer { - type Item = Result; - - fn next(&mut self) -> Option { - if let Some(token) = self.tokens.pop_front() { - return Some(Ok(token)); - } - - let mut input = String::new(); - - match self.reader.read_to_string(&mut input) { - Ok(0) => None, - Err(e) => Some(Err(TokenizeError::IO(e))), - _ => { - let re = regex::Regex::new(r#"(\-?[a-zA-Z0-9\.'_]+)|[`~!@#\$%\^&\*\(\)\+-=\[\]\{\}\\|;:,<\.>/\?]+|("[^"]+")"#).expect("This wont fail promise :3"); - - for token in re.find_iter(input.as_str()).map(|mat| mat.as_str()).map(Token::parse) { - match token { - Ok(token) => self.tokens.push_back(token), - Err(e) => return Some(Err(e)), - } - } - - self.tokens.pop_front().map(|x| Ok(x)) - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::str::FromStr; - - #[test] - fn test_string_parsing() { - let program = r#"- -1 2"#; - let tokenizer = Tokenizer::from_str(program).unwrap(); - let tokens: Vec = tokenizer.collect::>().expect("tokenizer failure"); - - println!("{tokens:?}"); - } -}