add more bool operators

This commit is contained in:
2024-10-15 18:22:48 -04:00
parent 4784cc295e
commit 5701bf8268
3 changed files with 110 additions and 81 deletions

View File

@@ -165,6 +165,15 @@ where
(Value::String(x), Value::String(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])), (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)?) { 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::Int(y)) => Ok(Value::Bool(x > y)),
(Value::Int(x), Value::Float(y)) => Ok(Value::Bool(x as f64 > 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)), Value::Bool(x) => Ok(Value::Bool(!x)),
x => Err(RuntimeError::NoOverloadForTypes("not".into(), vec![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) => { ParseTree::Equ(ident, body, scope) => {
if self.globals.contains_key(&ident) || locals.contains_key(&ident) { if self.globals.contains_key(&ident) || locals.contains_key(&ident) {
Err(RuntimeError::ImmutableError(ident.clone())) Err(RuntimeError::ImmutableError(ident.clone()))

View File

@@ -47,11 +47,14 @@ pub(crate) enum ParseTree {
// Boolean Operations // Boolean Operations
EqualTo(Box<ParseTree>, Box<ParseTree>), EqualTo(Box<ParseTree>, Box<ParseTree>),
NotEqualTo(Box<ParseTree>, Box<ParseTree>),
GreaterThan(Box<ParseTree>, Box<ParseTree>), GreaterThan(Box<ParseTree>, Box<ParseTree>),
GreaterThanOrEqualTo(Box<ParseTree>, Box<ParseTree>), GreaterThanOrEqualTo(Box<ParseTree>, Box<ParseTree>),
LessThan(Box<ParseTree>, Box<ParseTree>), LessThan(Box<ParseTree>, Box<ParseTree>),
LessThanOrEqualTo(Box<ParseTree>, Box<ParseTree>), LessThanOrEqualTo(Box<ParseTree>, Box<ParseTree>),
Not(Box<ParseTree>), Not(Box<ParseTree>),
And(Box<ParseTree>, Box<ParseTree>),
Or(Box<ParseTree>, Box<ParseTree>),
// Defining Objects // Defining Objects
Equ(String, Box<ParseTree>, Box<ParseTree>), Equ(String, Box<ParseTree>, Box<ParseTree>),
@@ -81,6 +84,30 @@ pub(crate) enum ParseTree {
Print(Box<ParseTree>), Print(Box<ParseTree>),
} }
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 { impl ParseTree {
fn parse<I>( fn parse<I>(
tokens: &mut I, tokens: &mut I,
@@ -108,30 +135,12 @@ impl ParseTree {
} }
Token::Operator(op) => { Token::Operator(op) => {
match op { match op {
Op::Add => Ok(ParseTree::Add( Op::Add => two_arg!(Add, tokens, globals, locals),
Box::new(ParseTree::parse(tokens, globals, locals)?), Op::Sub => two_arg!(Sub, tokens, globals, locals),
Box::new(ParseTree::parse(tokens, globals, locals)?) Op::Mul => two_arg!(Mul, tokens, globals, locals),
)), Op::Div => two_arg!(Div, tokens, globals, locals),
Op::Sub => Ok(ParseTree::Sub( Op::Exp => two_arg!(Exp, tokens, globals, locals),
Box::new(ParseTree::parse(tokens, globals, locals)?), Op::Mod => two_arg!(Mod, 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::Equ | Op::LazyEqu => {
let token = tokens.next() let token = tokens.next()
.ok_or(ParseError::UnexpectedEndInput)? .ok_or(ParseError::UnexpectedEndInput)?
@@ -183,48 +192,21 @@ impl ParseTree {
Err(ParseError::InvalidIdentifier) Err(ParseError::InvalidIdentifier)
} }
} }
Op::Compose => Ok(ParseTree::Compose( Op::Compose => two_arg!(Compose, tokens, globals, locals),
Box::new(ParseTree::parse(tokens, globals, locals)?), Op::Id => one_arg!(Id, tokens, globals, locals),
Box::new(ParseTree::parse(tokens, globals, locals)?) Op::If => two_arg!(If, tokens, globals, locals),
)), Op::IfElse => three_arg!(IfElse, tokens, globals, locals),
Op::Id => Ok(ParseTree::Id( Op::EqualTo => two_arg!(EqualTo, tokens, globals, locals),
Box::new(ParseTree::parse(tokens, globals, locals)?) Op::GreaterThan => two_arg!(GreaterThan, tokens, globals, locals),
)), Op::LessThan => two_arg!(LessThan, tokens, globals, locals),
Op::If => Ok(ParseTree::If( Op::GreaterThanOrEqualTo => two_arg!(GreaterThanOrEqualTo, tokens, globals, locals),
Box::new(ParseTree::parse(tokens, globals, locals)?), Op::LessThanOrEqualTo => two_arg!(LessThanOrEqualTo, tokens, globals, locals),
Box::new(ParseTree::parse(tokens, globals, locals)?) Op::Not => one_arg!(Not, tokens, globals, locals),
)), Op::IntCast => one_arg!(IntCast, tokens, globals, locals),
Op::IfElse => Ok(ParseTree::IfElse( Op::FloatCast => one_arg!(FloatCast, tokens, globals, locals),
Box::new(ParseTree::parse(tokens, globals, locals)?), Op::BoolCast => one_arg!(BoolCast, tokens, globals, locals),
Box::new(ParseTree::parse(tokens, globals, locals)?), Op::StringCast => one_arg!(StringCast, tokens, globals, locals),
Box::new(ParseTree::parse(tokens, globals, locals)?) Op::Print => one_arg!(Print, 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::OpenArray => { Op::OpenArray => {
let mut depth = 1; let mut depth = 1;
@@ -258,6 +240,9 @@ impl ParseTree {
} }
Op::Empty => Ok(ParseTree::Constant(Value::Array(vec![]))), Op::Empty => Ok(ParseTree::Constant(Value::Array(vec![]))),
Op::CloseArray => Err(ParseError::UnmatchedArrayClose), 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),
} }
} }
} }

View File

@@ -57,6 +57,7 @@ pub(crate) enum Op {
GreaterThan, GreaterThan,
LessThan, LessThan,
EqualTo, EqualTo,
NotEqualTo,
GreaterThanOrEqualTo, GreaterThanOrEqualTo,
LessThanOrEqualTo, LessThanOrEqualTo,
Not, Not,
@@ -68,6 +69,8 @@ pub(crate) enum Op {
OpenArray, OpenArray,
CloseArray, CloseArray,
Empty, Empty,
And,
Or,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -101,7 +104,6 @@ impl Token {
// Match keywords first // Match keywords first
"true" => Ok(Token::Constant(Value::Bool(true))), "true" => Ok(Token::Constant(Value::Bool(true))),
"false" => Ok(Token::Constant(Value::Bool(false))), "false" => Ok(Token::Constant(Value::Bool(false))),
"not" => Ok(Token::Operator(Op::Not)),
"int" => Ok(Token::Operator(Op::IntCast)), "int" => Ok(Token::Operator(Op::IntCast)),
"float" => Ok(Token::Operator(Op::FloatCast)), "float" => Ok(Token::Operator(Op::FloatCast)),
"bool" => Ok(Token::Operator(Op::BoolCast)), "bool" => Ok(Token::Operator(Op::BoolCast)),
@@ -164,8 +166,12 @@ impl<R: BufRead> Tokenizer<R> {
(">=", Op::GreaterThanOrEqualTo), (">=", Op::GreaterThanOrEqualTo),
("<=", Op::LessThanOrEqualTo), ("<=", Op::LessThanOrEqualTo),
("==", Op::EqualTo), ("==", Op::EqualTo),
("!=", Op::NotEqualTo),
("[", Op::OpenArray), ("[", Op::OpenArray),
("]", Op::CloseArray), ("]", Op::CloseArray),
("!", Op::Not),
("&&", Op::And),
("||", Op::Or),
]); ]);
let c = if let Some(c) = iter.next() { let c = if let Some(c) = iter.next() {
@@ -217,11 +223,15 @@ impl<R: BufRead> Tokenizer<R> {
let mut token = String::from(c); let mut token = String::from(c);
loop { loop {
// get a list of all tokens this current token could possibly be
let possible: HashMap<&'static str, Op> = operators let possible: HashMap<&'static str, Op> = operators
.clone().into_iter() .clone().into_iter()
.filter(|(key, _)| key.starts_with(&token)) .filter(|(key, _)| key.starts_with(&token))
.collect(); .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| let is_expected = |c: &char|
possible.iter().any(|(op, _)| match op.chars().nth(token.len()) { possible.iter().any(|(op, _)| match op.chars().nth(token.len()) {
Some(i) => *c == i, Some(i) => *c == i,
@@ -230,7 +240,12 @@ impl<R: BufRead> Tokenizer<R> {
match possible.len() { match possible.len() {
1 => { 1 => {
self.tokens.push_back(Ok(Token::Operator(match possible.get(token.as_str()).unwrap().clone() { // 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) => { Op::FunctionDeclare(n) => {
let count = match get_dot_count(&mut iter) { let count = match get_dot_count(&mut iter) {
Some(count) => count, Some(count) => count,
@@ -241,9 +256,21 @@ impl<R: BufRead> Tokenizer<R> {
}; };
Op::FunctionDeclare(n + count) Op::FunctionDeclare(n + count)
} }
op => op, op => op.clone(),
}))); })));
break; 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!(), 0 => unreachable!(),
_ => { _ => {