instruct/interpreter/
stack.rs

1use log::trace;
2use std::{cell::RefCell, collections::HashMap, rc::Rc};
3use thiserror::Error;
4
5#[derive(Error, Debug)]
6pub enum Error {
7    #[error("a variable '{0}' that not allocated was accessed, this should not happen!")]
8    UnallocatedVariableAccessed(String),
9    #[error("tried to access undefined variable '{0}'")]
10    UndefinedVariableAccessed(String),
11}
12
13pub type StackRef = Rc<RefCell<Stack>>;
14
15pub struct Stack {
16    variables: HashMap<String, Option<String>>,
17    parent: Option<StackRef>,
18    height: u16,
19}
20
21impl Stack {
22    pub fn new() -> Self {
23        Self {
24            variables: HashMap::new(),
25            parent: None,
26            height: 0,
27        }
28    }
29
30    pub fn inherit_new(parent: &StackRef) -> Stack {
31        Self {
32            variables: HashMap::new(),
33            parent: Some(parent.clone()),
34            height: parent.borrow().height + 1,
35        }
36    }
37
38    pub fn get(&self, name: &str) -> anyhow::Result<String> {
39        trace!("Getting '{}' from stack {}@{:p}", name, self.height, self);
40        match self.variables.get(name) {
41            Some(Some(val)) => Ok(val.into()),
42            Some(None) | None => match &self.parent {
43                Some(child) => child.borrow().get(name),
44                None => Err(Error::UnallocatedVariableAccessed(name.into()).into()),
45            },
46        }
47    }
48
49    pub fn set(&mut self, name: String, value: String) -> anyhow::Result<()> {
50        if value.len() > 10 {
51            trace!(
52                "Setting '{}' to {:?}..{:?}' for stack {}@{:p}",
53                &name,
54                &value[..10],
55                &value[value.len() - 10..],
56                self.height,
57                self
58            );
59        } else {
60            trace!(
61                "Setting '{}' to {:?} for stack {}@{:p}",
62                &name,
63                &value,
64                self.height,
65                self
66            );
67        }
68        if self.variables.insert(name.clone(), Some(value)).is_none() {
69            return Err(Error::UnallocatedVariableAccessed(name).into());
70        }
71        Ok(())
72    }
73
74    pub fn allocate(&mut self, name: String) {
75        trace!(
76            "Allocating '{}' for stack {}@{:p}",
77            &name,
78            self.height,
79            self
80        );
81        self.variables.insert(name, None);
82    }
83
84    pub fn assert_allocated(&self, name: &str) -> anyhow::Result<()> {
85        trace!(
86            "Asserting allocation '{}' for stack {}@{:p}",
87            &name,
88            self.height,
89            self
90        );
91        match self.variables.contains_key(name) {
92            true => Ok(()),
93            false => match &self.parent {
94                Some(parent) => parent.borrow().assert_allocated(name),
95                None => Err(Error::UndefinedVariableAccessed(name.into()).into()),
96            },
97        }
98    }
99}
100
101impl Default for Stack {
102    fn default() -> Self {
103        Self::new()
104    }
105}
106
107impl From<Vec<(&'static str, &'static str)>> for Stack {
108    fn from(values: Vec<(&str, &str)>) -> Stack {
109        let mut stack = Stack::new();
110
111        for (key, value) in values {
112            stack.allocate(key.into());
113            stack.set(key.into(), value.into()).unwrap();
114        }
115
116        stack
117    }
118}
119
120impl From<Stack> for StackRef {
121    fn from(stack: Stack) -> StackRef {
122        Rc::new(RefCell::new(stack))
123    }
124}