summaryrefslogtreecommitdiff
path: root/src/types/expression_stack.rs
blob: d14d808e4d0750c90a2d4aa09fc075a3e0ba31fe (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
use crate::*;


pub struct ExpressionStack {
    stack: Vec<isize>,
}

impl ExpressionStack {
    pub fn new() -> Self {
        Self {
            stack: Vec::new(),
        }
    }

    pub fn pull_result(mut self) -> Result<isize, StackError> {
        match self.stack.len() {
            0 => Err(StackError::NoReturnValue),
            1 => Ok(self.stack.pop().unwrap()),
            _ => Err(StackError::MultipleReturnValues),
        }
    }

    pub fn push(&mut self, value: isize) {
        self.stack.push(value);
    }

    pub fn apply(&mut self, operator: Operator, source: &SourceSpan) -> Result<(), Tracked<StackError>> {
        macro_rules! push {
            ($val:expr) => { self.stack.push($val) }
        }
        macro_rules! pop {
            ($name:ident) => {
                let $name = match self.stack.pop() {
                    Some(value) => value,
                    None => return Err(Tracked::from(StackError::Underflow, source.clone())),
                };
            }
        }
        macro_rules! truth {
            ($bool:expr) => { match $bool { true => 1, false => 0 } };
        }
        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::LessThanEqual    => { pop!(b); pop!(a); push!(truth!(a <= b)) },
            Operator::GreaterThanEqual => { 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::Multiply         => { pop!(b); pop!(a); push!(a * b) },
            Operator::Divide           => { pop!(b); pop!(a); push!(a / b) },
            Operator::Modulo           => { pop!(b); pop!(a); push!(a % b) },
            Operator::Exponent         => { pop!(b); pop!(a); push!(
                if let Ok(b) = u32::try_from(b) { a.saturating_pow(b) } else { 0 }  ) },
            Operator::LeftShift        => { pop!(b); pop!(a); push!(
                if b < 0 { a >> -b } else { a << b }  ) },
            Operator::RightShift       => { pop!(b); pop!(a); push!(
                if b < 0 { a << -b } else { a >> b }  ) },
            Operator::BitAnd           => { pop!(b); pop!(a); push!(a & b) },
            Operator::BitOr            => { pop!(b); pop!(a); push!(a | b) },
            Operator::BitXor           => { pop!(b); pop!(a); push!(a ^ b) },
            Operator::BitNot           => {          pop!(a); push!(!a) },
            Operator::Length           => {          pop!(a); push!(width(a) as isize) },
        }
        return Ok(());
    }
}

/// Find the number of bits required to hold an integer.
pub fn width(value: isize) -> u32 {
    match value.cmp(&0) {
        std::cmp::Ordering::Less => (-value).ilog2() + 2,
        std::cmp::Ordering::Equal => 0,
        std::cmp::Ordering::Greater => value.ilog2() + 1,
    }
}

pub enum StackError {
    Underflow,
    MultipleReturnValues,
    NoReturnValue,
}


pub fn report_stack_error(error: &Tracked<StackError>, source_code: &str) {
    let context = Context { source_code: &source_code, source: &error.source };
    let message = match &error.value {
        StackError::Underflow =>
            "A stack underflow occurred while evaluating this operator",
        StackError::MultipleReturnValues =>
            "More than one value was left on the stack after this expression was evaluated",
        StackError::NoReturnValue =>
            "No value was left on the stack after this expression was evaluated",
    };

    report_source_issue(LogLevel::Error, &context, message);
}