summaryrefslogtreecommitdiff
path: root/src/tokens/constant_expression.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tokens/constant_expression.rs')
-rw-r--r--src/tokens/constant_expression.rs134
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(());
+ }
+}