libdusk/
autopop.rs

1use std::cell::RefCell;
2use std::fmt::Debug;
3use std::sync::{Arc, Mutex};
4use std::mem;
5
6#[derive(Debug)]
7pub struct AutoPopStack<T: Debug> {
8    pub stack: Arc<Mutex<RefCell<Vec<T>>>>,
9}
10
11impl<T: Debug> Clone for AutoPopStack<T> {
12    fn clone(&self) -> Self {
13        AutoPopStack { stack: self.stack.clone() }
14    }
15}
16
17impl<T: Debug> Default for AutoPopStack<T> {
18    fn default() -> Self {
19        Self { stack: Default::default() }
20    }
21}
22
23impl<T: Debug> AutoPopStack<T> {
24    pub fn new() -> Self {
25        Self::default()
26    }
27    pub fn push<Id>(&mut self, id: Id, entry: T) -> AutoPopStackEntry<T, Id> where Id: PartialEq<T> + Debug + Copy {
28        self.stack.lock().unwrap().borrow_mut().push(entry);
29        AutoPopStackEntry::new(id, self.clone())
30    }
31    pub fn peek_mut<U>(&mut self, f: impl FnOnce(Option<&mut T>) -> U) -> U {
32        f(self.stack.lock().unwrap().borrow_mut().last_mut())
33    }
34}
35
36impl<T: Debug + Clone> AutoPopStack<T> {
37    pub fn peek(&self) -> Option<T> {
38        self.stack.lock().unwrap().borrow().last().cloned()
39    }
40}
41
42/// Because parser methods often exit early on failure, it previously would've been possible to push to the stack, exit
43/// due to an error, and never end up popping. This type prevents that scenario from happening, by automatically
44/// popping on drop, and checking that the expected value has been popped.
45/// TODO: this problem almost certainly exists right now for comp_decl_stack, so
46/// I should use it there as well.
47#[must_use]
48pub struct AutoPopStackEntry<T: Debug, Id=T> where Id: PartialEq<T> + Debug + Copy {
49    id: Id,
50    pub stack: AutoPopStack<T>,
51}
52
53impl<T: Debug, Id: PartialEq<T> + Debug + Copy> AutoPopStackEntry<T, Id> {
54    fn new(id: Id, stack: AutoPopStack<T>) -> Self {
55        Self { id, stack }
56    }
57
58    pub fn id(&self) -> Id { self.id }
59    pub fn make_permanent(self) { mem::forget(self); }
60}
61
62impl<T: Debug, Id: PartialEq<T> + Debug + Copy> Drop for AutoPopStackEntry<T, Id> {
63    fn drop(&mut self) {
64        let top = self.stack.stack.lock().unwrap().borrow_mut().pop();
65        debug_assert_eq!(
66            self.id,
67            top.expect("internal compiler error: tried to pop from empty stack"),
68            "internal compiler error: popped incorrect value"
69        );
70    }
71}