Skip to main content

huff_neo_lexer/
context_stack.rs

1use huff_neo_utils::error::LexicalErrorKind;
2use huff_neo_utils::lexer_context::Context;
3
4// The context stack is used to keep track of the current context.
5//
6// Complete context hierarchy showing all possible parent-child relationships:
7//
8// Context::Global
9// ├─ Context::Constant
10// ├─ Context::Abi
11// │  └─ Context::AbiArgs
12// ├─ Context::MacroDefinition
13// │  └─ Context::MacroBody
14// │     ├─ Context::BuiltinFunction
15// │     ├─ Context::MacroArgs
16// │     └─ Context::ForLoopBody
17// │        ├─ Context::BuiltinFunction
18// │        ├─ Context::MacroArgs
19// │        └─ Context::ForLoopBody (nested)
20// └─ Context::CodeTableBody
21
22/// The context stack is used to keep track of the current context
23pub struct ContextStack {
24    /// The stack of contexts
25    stack: Vec<Context>,
26}
27
28impl ContextStack {
29    /// Creates a new context stack with the global context
30    pub fn new() -> Self {
31        ContextStack { stack: vec![Context::Global] }
32    }
33
34    /// Replaces the last context on the stack with a new context
35    pub fn replace(&mut self, context: Context) {
36        let last = self.stack.last_mut().unwrap();
37        *last = context;
38    }
39
40    /// Pushes a new context onto the stack
41    pub fn push(&mut self, context: Context) {
42        self.stack.push(context);
43    }
44
45    /// Pops the last context off the stack
46    pub fn pop(&mut self, count: u8) -> Result<(), LexicalErrorKind> {
47        for _ in 0..count {
48            self.stack.pop();
49            if self.stack.is_empty() {
50                return Err(LexicalErrorKind::StackUnderflow);
51            }
52        }
53
54        Ok(())
55    }
56
57    /// Returns the last context on the stack
58    pub fn top(&self) -> &Context {
59        self.stack.last().unwrap()
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66    use huff_neo_utils::lexer_context::Context;
67
68    #[test]
69    fn test_context_stack_new() {
70        let stack = ContextStack::new();
71        assert_eq!(stack.top(), &Context::Global);
72    }
73
74    #[test]
75    fn test_context_stack_replace() {
76        let mut stack = ContextStack::new();
77        stack.push(Context::MacroDefinition);
78        assert_eq!(stack.top(), &Context::MacroDefinition);
79
80        stack.replace(Context::MacroBody);
81        assert_eq!(stack.top(), &Context::MacroBody);
82    }
83
84    #[test]
85    fn test_context_stack_push_pop() {
86        let mut stack = ContextStack::new();
87        stack.push(Context::MacroDefinition);
88        assert_eq!(stack.top(), &Context::MacroDefinition);
89
90        stack.pop(1).unwrap();
91        assert_eq!(stack.top(), &Context::Global);
92    }
93
94    #[test]
95    fn test_context_stack_underflow() {
96        let mut stack = ContextStack::new();
97        stack.pop(1).unwrap_err();
98    }
99
100    #[test]
101    fn test_context_stack_push_pop_multiple() {
102        let mut stack = ContextStack::new();
103        stack.push(Context::Abi);
104        stack.push(Context::AbiArgs);
105        assert_eq!(stack.top(), &Context::AbiArgs);
106
107        stack.pop(1).unwrap();
108        assert_eq!(stack.top(), &Context::Abi);
109
110        stack.pop(1).unwrap();
111        assert_eq!(stack.top(), &Context::Global);
112    }
113}