use crate::*;
pub struct ConstantExpression {
pub tokens: Vec<ConstantExpressionToken>,
}
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<usize, ConstantExpressionEvaluationError> {
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(_) => "<error>",
};
match i {
0 => write!(f, "{string}")?,
_ => write!(f, " {string}")?,
}
}
return Ok(());
}
}