use crate::tokens::{Token, TokenType};
use crate::visitor::ExprVisitor;
#[derive(Clone)]
pub(crate) enum Expr {
Variable(VariableExpr),
Logical(LogicalExpr),
Binary(BinaryExpr),
Unary(UnaryExpr),
Call(CallExpr),
Literal(ExprValue),
}
impl Expr {
pub(crate) fn new_variable(name: Token) -> Self {
Self::Variable(VariableExpr { name })
}
pub(crate) fn new_logical(left: Expr, operator: TokenType, right: Expr) -> Self {
Self::Logical(LogicalExpr {
left: Box::new(left),
operator,
right: Box::new(right),
})
}
pub(crate) fn new_binary(left: Expr, operator: TokenType, right: Expr) -> Self {
Self::Binary(BinaryExpr {
left: Box::new(left),
operator,
right: Box::new(right),
})
}
pub(crate) fn new_unary(operator: TokenType, right: Expr) -> Self {
Self::Unary(UnaryExpr {
operator,
right: Box::new(right),
})
}
pub(crate) fn new_call(name: String, arguments: Vec<Expr>) -> Self {
Self::Call(CallExpr { name, arguments })
}
pub(crate) fn new_literal(value: ExprValue) -> Self {
Self::Literal(value)
}
pub(crate) fn accept<U, V>(&self, visitor: &mut V) -> U
where
V: ExprVisitor<U>,
{
match self {
Expr::Variable(expr) => visitor.visit_variable_expr(expr),
Expr::Logical(expr) => visitor.visit_logical_expr(expr),
Expr::Binary(expr) => visitor.visit_binary_expr(expr),
Expr::Unary(expr) => visitor.visit_unary_expr(expr),
Expr::Call(expr) => visitor.visit_call_expr(expr),
Expr::Literal(expr) => visitor.visit_literal_expr(expr),
}
}
}
#[derive(Clone)]
pub(crate) struct AssignExpr {
pub(crate) name: Token,
pub(crate) value: Box<Expr>,
}
#[derive(Clone)]
pub(crate) struct VariableExpr {
pub(crate) name: Token,
}
#[derive(Clone)]
pub(crate) struct LogicalExpr {
pub(crate) left: Box<Expr>,
pub(crate) operator: TokenType,
pub(crate) right: Box<Expr>,
}
#[derive(Clone)]
pub(crate) struct BinaryExpr {
pub(crate) left: Box<Expr>,
pub(crate) operator: TokenType,
pub(crate) right: Box<Expr>,
}
#[derive(Clone)]
pub(crate) struct UnaryExpr {
pub(crate) operator: TokenType,
pub(crate) right: Box<Expr>,
}
#[derive(Clone)]
pub(crate) struct CallExpr {
pub(crate) name: String,
pub(crate) arguments: Vec<Expr>,
}
#[derive(Clone, PartialEq, Debug)]
pub enum ExprValue {
Boolean(bool),
Integer(i64),
Decimal(f64),
String(String),
}
impl ExprValue {
pub fn is_integer(&self) -> bool {
matches!(self, ExprValue::Boolean(_) | ExprValue::Integer(_))
}
pub fn is_decimal(&self) -> bool {
matches!(self, ExprValue::Decimal(_))
}
pub fn is_string(&self) -> bool {
matches!(self, ExprValue::String(_))
}
pub fn into_string(self) -> String {
match self {
ExprValue::Boolean(x) => {
if x {
"true".to_string()
} else {
"false".to_string()
}
}
ExprValue::Integer(x) => x.to_string(),
ExprValue::Decimal(x) => x.to_string(),
ExprValue::String(x) => x,
}
}
pub fn into_integer(self) -> i64 {
match self {
ExprValue::Boolean(x) => {
if x {
1
} else {
0
}
}
ExprValue::Integer(x) => x,
_ => panic!("Not an integer"),
}
}
pub fn into_decimal(self) -> f64 {
match self {
ExprValue::Boolean(x) => x as i32 as f64,
ExprValue::Integer(x) => x as f64,
ExprValue::Decimal(x) => x,
ExprValue::String(_) => panic!("Not a decimal"),
}
}
pub fn into_boolean(self) -> Option<bool> {
match self {
ExprValue::Boolean(x) => Some(x),
ExprValue::Integer(x) => Some(x != 0),
ExprValue::Decimal(x) => Some(x != 0.0),
ExprValue::String(_) => None,
}
}
pub(crate) fn increment(&mut self) -> Result<(), &'static str> {
match self {
ExprValue::Integer(x) => *x += 1,
ExprValue::Decimal(x) => *x += 1.0,
_ => return Err("Can only increment numbers."),
}
Ok(())
}
}