add type casting
This commit is contained in:
@@ -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)?))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user