diff options
Diffstat (limited to 'src/tokens/constant_expression.rs')
-rw-r--r-- | src/tokens/constant_expression.rs | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/src/tokens/constant_expression.rs b/src/tokens/constant_expression.rs new file mode 100644 index 0000000..e4aa099 --- /dev/null +++ b/src/tokens/constant_expression.rs @@ -0,0 +1,134 @@ +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(()); + } +} |