add type casting

This commit is contained in:
2024-10-14 15:27:01 -04:00
parent 5645282112
commit 9317c83dec
3 changed files with 57 additions and 5 deletions

View File

@@ -16,6 +16,7 @@ pub enum RuntimeError {
FunctionUndeclared(String), FunctionUndeclared(String),
FunctionUndefined(String), FunctionUndefined(String),
NotAVariable(String), NotAVariable(String),
ParseFail(String, Type),
} }
impl Display for RuntimeError { impl Display for RuntimeError {
@@ -30,6 +31,7 @@ impl Display for RuntimeError {
Self::FunctionUndeclared(ident) => write!(f, "function `{ident}` was not declared"), Self::FunctionUndeclared(ident) => write!(f, "function `{ident}` was not declared"),
Self::FunctionUndefined(ident) => write!(f, "function `{ident}` was not defined"), 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::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<I: Iterator<Item = Result<ParseTree, ParseError>>> Executor<I> {
(Value::Float(x), Value::Int(y)) => Ok(Value::Float(x + y as f64)), (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::Int(x), Value::Float(y)) => Ok(Value::Float(x as f64 + y)),
(Value::Float(x), Value::Float(y)) => Ok(Value::Float(x + 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])) (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)?) {
@@ -96,6 +99,7 @@ impl<I: Iterator<Item = Result<ParseTree, ParseError>>> Executor<I> {
(Value::Float(x), Value::Int(y)) => Ok(Value::Float(x * y as f64)), (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::Int(x), Value::Float(y)) => Ok(Value::Float(x as f64 * y)),
(Value::Float(x), Value::Float(y)) => Ok(Value::Float(x * 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])) (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)?) {
@@ -264,6 +268,34 @@ impl<I: Iterator<Item = Result<ParseTree, ParseError>>> Executor<I> {
} }
}, },
ParseTree::Constant(value) => Ok(value), 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)?))),
} }
} }
} }

View File

@@ -70,6 +70,12 @@ pub enum ParseTree {
FunctionCall(String, Vec<ParseTree>), FunctionCall(String, Vec<ParseTree>),
Variable(String), Variable(String),
Constant(Value), Constant(Value),
// Type Casts
ToInt(Box<ParseTree>),
ToFloat(Box<ParseTree>),
ToBool(Box<ParseTree>),
ToString(Box<ParseTree>),
} }
impl ParseTree { impl ParseTree {
@@ -218,7 +224,11 @@ impl ParseTree {
Box::new(ParseTree::parse(tokens, globals, locals)?), Box::new(ParseTree::parse(tokens, globals, locals)?),
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<I: Iterator<Item = Result<Token, TokenizeError>>> Iterator for Parser<I> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::commands::eval::tokenizer::Tokenizer; use crate::commands::eval::tokenizer::Tokenizer;
use super::{*, ParseTree::*}; use super::*;
use std::str::FromStr; use std::str::FromStr;

View File

@@ -55,6 +55,10 @@ pub enum Op {
GreaterThanOrEqualTo, GreaterThanOrEqualTo,
LessThanOrEqualTo, LessThanOrEqualTo,
Not, Not,
IntCast,
FloatCast,
BoolCast,
StringCast,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -113,13 +117,19 @@ impl Token {
"false" => Ok(Token::Constant(Value::Bool(false))), "false" => Ok(Token::Constant(Value::Bool(false))),
"not" => Ok(Token::Operator(Op::Not)), "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 // then variable length keywords, constants, and identifiers
_ => { _ => {
if s.starts_with(':') { if s.starts_with(':') {
Ok(Token::Operator(Op::FunctionDeclare( Ok(Token::Operator(Op::FunctionDeclare(
get_dot_count(s).map(|x| x - 1).ok_or(TokenizeError::InvalidDynamicOperator(s.to_string()))? 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::<i64>() { if let Ok(int) = s.parse::<i64>() {
Ok(Token::Constant(Value::Int(int))) Ok(Token::Constant(Value::Int(int)))
} else if let Ok(float) = s.parse::<f64>() { } else if let Ok(float) = s.parse::<f64>() {
@@ -175,7 +185,7 @@ impl<R: BufRead> std::iter::Iterator for Tokenizer<R> {
Ok(0) => None, Ok(0) => None,
Err(e) => Some(Err(TokenizeError::IO(e))), 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) { for token in re.find_iter(input.as_str()).map(|mat| mat.as_str()).map(Token::parse) {
match token { match token {
@@ -197,7 +207,7 @@ mod tests {
#[test] #[test]
fn test_string_parsing() { fn test_string_parsing() {
let program = r#"+ "hi" "bye" "whats good""#; let program = r#"- -1 2"#;
let tokenizer = Tokenizer::from_str(program).unwrap(); let tokenizer = Tokenizer::from_str(program).unwrap();
let tokens: Vec<Token> = tokenizer.collect::<Result<_, TokenizeError>>().expect("tokenizer failure"); let tokens: Vec<Token> = tokenizer.collect::<Result<_, TokenizeError>>().expect("tokenizer failure");