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!(), _ => {