xbasic 0.3.2

A library that allows adding a scripting language onto your project with ease. This lets your users write their own arbitrary logic.
Documentation
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>,
}

/// Represents an xBASIC value.
#[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(())
	}
}