diff --git a/src/commands/eval/executor.rs b/src/commands/eval/executor.rs index 99ecbc2..02bbc93 100644 --- a/src/commands/eval/executor.rs +++ b/src/commands/eval/executor.rs @@ -16,6 +16,7 @@ pub enum RuntimeError { FunctionUndeclared(String), FunctionUndefined(String), NotAVariable(String), + ParseFail(String, Type), } impl Display for RuntimeError { @@ -30,6 +31,7 @@ impl Display for RuntimeError { 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), } } } @@ -82,6 +84,7 @@ impl>> Executor { (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)?) { @@ -96,6 +99,7 @@ impl>> Executor { (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)?) { @@ -264,6 +268,34 @@ impl>> Executor { } }, 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)?))), } } } diff --git a/src/commands/eval/parser.rs b/src/commands/eval/parser.rs index efa2b76..156740d 100644 --- a/src/commands/eval/parser.rs +++ b/src/commands/eval/parser.rs @@ -70,6 +70,12 @@ pub enum ParseTree { FunctionCall(String, Vec), Variable(String), Constant(Value), + + // Type Casts + ToInt(Box), + ToFloat(Box), + ToBool(Box), + ToString(Box), } impl ParseTree { @@ -218,7 +224,11 @@ impl ParseTree { 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::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)?))), } } } @@ -275,7 +285,7 @@ impl>> Iterator for Parser { #[cfg(test)] mod tests { use crate::commands::eval::tokenizer::Tokenizer; - use super::{*, ParseTree::*}; + use super::*; use std::str::FromStr; diff --git a/src/commands/eval/tokenizer.rs b/src/commands/eval/tokenizer.rs index 20fe8a8..32e6849 100644 --- a/src/commands/eval/tokenizer.rs +++ b/src/commands/eval/tokenizer.rs @@ -55,6 +55,10 @@ pub enum Op { GreaterThanOrEqualTo, LessThanOrEqualTo, Not, + IntCast, + FloatCast, + BoolCast, + StringCast, } #[derive(Debug, Clone)] @@ -113,13 +117,19 @@ impl Token { "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)) { + } 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::() { @@ -175,7 +185,7 @@ impl std::iter::Iterator for Tokenizer { 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"); + 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 { @@ -197,7 +207,7 @@ mod tests { #[test] fn test_string_parsing() { - let program = r#"+ "hi" "bye" "whats good""#; + let program = r#"- -1 2"#; let tokenizer = Tokenizer::from_str(program).unwrap(); let tokens: Vec = tokenizer.collect::>().expect("tokenizer failure");