use crate::*; pub struct ConstantExpression { pub tokens: Vec, } impl ConstantExpression { pub fn from_str(string: &str, tokeniser: &Tokeniser) -> Self { parse_constant_expression(string, tokeniser) } } pub struct ConstantExpressionToken { pub source: SourceSpan, pub variant: ConstantExpressionTokenVariant, } pub enum ConstantExpressionTokenVariant { SymbolReference(String), IntegerLiteral(usize), Operator(Operator), Error(ConstantExpressionParseError), } pub enum Operator { Equal, NotEqual, LessThan, GreaterThan, Add, Subtract, LeftShift, RightShift, And, Or, Xor, Not, } pub enum ConstantExpressionParseError { InvalidHexadecimalLiteral(String), } impl ConstantExpression { pub fn evaluate(&self, environment: &Environment) -> Result { use ConstantExpressionTokenVariant as Token; use ConstantExpressionEvaluationError as EvalErr; let mut stack = Vec::new(); macro_rules! push { ($value:expr) => { stack.push($value) }; } macro_rules! pop { ($name:ident) => { let $name = match stack.pop() { Some(value) => value, None => return Err(EvalErr::StackUnderflow), }; }; } macro_rules! truth { ($bool:expr) => { match $bool { true => 1, false => 0 } }; } for token in &self.tokens { match &token.variant { Token::IntegerLiteral(value) => push!(*value), Token::SymbolReference(name) => match environment.get_integer(name) { Ok(value) => push!(value), Err(_) => todo!(), } Token::Operator(operator) => match operator { Operator::Equal => { pop!(b); pop!(a); push!(truth!(a==b)) }, Operator::NotEqual => { pop!(b); pop!(a); push!(truth!(a!=b)) }, Operator::LessThan => { pop!(b); pop!(a); push!(truth!(a < b)) }, Operator::GreaterThan => { pop!(b); pop!(a); push!(truth!(a > b)) }, Operator::Add => { pop!(b); pop!(a); push!(a + b) }, Operator::Subtract => { pop!(b); pop!(a); push!(a - b) }, Operator::LeftShift => { pop!(b); pop!(a); push!(a << b) }, Operator::RightShift => { pop!(b); pop!(a); push!(a >> b) }, Operator::And => { pop!(b); pop!(a); push!(a & b) }, Operator::Or => { pop!(b); pop!(a); push!(a | b) }, Operator::Xor => { pop!(b); pop!(a); push!(a ^ b) }, Operator::Not => { pop!(a); push!(!a) }, } Token::Error(_) => (), } } match stack.len() { 0 => Err(EvalErr::NoReturnValue), 1 => Ok(stack[0]), _ => Err(EvalErr::MultipleReturnValues), } } } pub enum ConstantExpressionEvaluationError { StackUnderflow, MultipleReturnValues, NoReturnValue, } impl std::fmt::Debug for ConstantExpression { fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { use ConstantExpressionTokenVariant as TokenVar; for (i, token) in self.tokens.iter().enumerate() { let string = match &token.variant { TokenVar::SymbolReference(name) => name, TokenVar::IntegerLiteral(value) => &value.to_string(), TokenVar::Operator(operator) => match operator { Operator::Equal => "=", Operator::NotEqual => "!", Operator::LessThan => "<", Operator::GreaterThan => ">", Operator::Add => "+", Operator::Subtract => "-", Operator::LeftShift => "<<", Operator::RightShift => ">>", Operator::And => "&", Operator::Or => "|", Operator::Xor => "^", Operator::Not => "~", } TokenVar::Error(_) => "", }; match i { 0 => write!(f, "{string}")?, _ => write!(f, " {string}")?, } } return Ok(()); } }