add type casting
This commit is contained in:
@@ -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<I: Iterator<Item = Result<ParseTree, ParseError>>> Executor<I> {
|
||||
(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<I: Iterator<Item = Result<ParseTree, ParseError>>> Executor<I> {
|
||||
(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<I: Iterator<Item = Result<ParseTree, ParseError>>> Executor<I> {
|
||||
}
|
||||
},
|
||||
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)?))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,12 @@ pub enum ParseTree {
|
||||
FunctionCall(String, Vec<ParseTree>),
|
||||
Variable(String),
|
||||
Constant(Value),
|
||||
|
||||
// Type Casts
|
||||
ToInt(Box<ParseTree>),
|
||||
ToFloat(Box<ParseTree>),
|
||||
ToBool(Box<ParseTree>),
|
||||
ToString(Box<ParseTree>),
|
||||
}
|
||||
|
||||
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<I: Iterator<Item = Result<Token, TokenizeError>>> Iterator for Parser<I> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::commands::eval::tokenizer::Tokenizer;
|
||||
use super::{*, ParseTree::*};
|
||||
use super::*;
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
|
||||
@@ -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::<i64>() {
|
||||
Ok(Token::Constant(Value::Int(int)))
|
||||
} else if let Ok(float) = s.parse::<f64>() {
|
||||
@@ -175,7 +185,7 @@ impl<R: BufRead> std::iter::Iterator for Tokenizer<R> {
|
||||
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<Token> = tokenizer.collect::<Result<_, TokenizeError>>().expect("tokenizer failure");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user