y_lang/typechecker/
typescope.rs

1use std::{cell::RefCell, collections::HashMap, rc::Rc};
2
3use super::{error::TypeError, variabletype::VariableType};
4
5#[derive(Debug, Clone)]
6pub struct Variable {
7    pub variable_type: VariableType,
8    pub is_mutable: bool,
9}
10
11type ScopeFrame = HashMap<String, Variable>;
12
13type ScopeFrameReference = Rc<RefCell<ScopeFrame>>;
14
15#[derive(Default, Debug, Clone)]
16pub struct TypeScope {
17    scope_stack: Vec<ScopeFrameReference>,
18}
19
20impl PartialEq for TypeScope {
21    fn eq(&self, _: &Self) -> bool {
22        true
23    }
24}
25
26impl Eq for TypeScope {}
27
28impl TypeScope {
29    /// Find a value/reference in this scope by iterating over the scopes from back to front.
30    pub fn find(&self, name: &str) -> Option<VariableType> {
31        let mut scopes = self.scope_stack.clone();
32        scopes.reverse();
33        for scope in scopes {
34            if let Some(variable) = scope.borrow().get(name) {
35                return Some(variable.variable_type.clone());
36            }
37        }
38
39        None
40    }
41
42    pub fn is_mutable(&self, name: &str) -> bool {
43        for (index, scope) in self.scope_stack.iter().rev().enumerate() {
44            if let Some(Variable { is_mutable, .. }) = scope.borrow().get(name) {
45                if *is_mutable || index == 0 {
46                    return true;
47                }
48            }
49        }
50
51        false
52    }
53
54    /// Check, if a variable with a given name is present.
55    pub fn contains(&self, name: &str) -> bool {
56        let mut scopes = self.scope_stack.clone();
57        scopes.reverse();
58        for scope in &scopes {
59            if scope.borrow().contains_key(name) {
60                return true;
61            }
62        }
63
64        false
65    }
66
67    /// Check, if a variable is present in the current scope.
68    pub fn contains_in_current_scope(&self, name: &str) -> bool {
69        let Some(last) = self.scope_stack.last() else {
70            return false;
71        };
72        return last.borrow().contains_key(name);
73    }
74
75    /// Push a new scope frame.
76    pub fn push(&mut self) {
77        self.scope_stack.push(Rc::new(RefCell::new(HashMap::new())))
78    }
79
80    /// Pop the last scope frame.
81    pub fn pop(&mut self) {
82        self.scope_stack.pop();
83    }
84
85    /// Create a new variable on the current scope.
86    pub fn set(&mut self, name: &str, value: VariableType, is_mutable: bool) {
87        if let Some(scope) = self.scope_stack.last_mut() {
88            let variable = Variable {
89                variable_type: value,
90                is_mutable,
91            };
92            scope.borrow_mut().insert(name.to_owned(), variable);
93        }
94    }
95
96    /// Update a value of an already present variable.
97    pub fn update(
98        &mut self,
99        name: &str,
100        value: VariableType,
101        position: &(String, usize, usize),
102    ) -> Result<(), TypeError> {
103        let mut scopes = self.scope_stack.clone();
104        scopes.reverse();
105
106        for scope in &mut scopes {
107            let mut scope = scope.borrow_mut();
108            if let Some(old_variable) = scope.get(name) {
109                let old_type = &old_variable.variable_type;
110                if old_type != &value {
111                    return Err(TypeError {
112                        message: format!(
113                            "Could not assign variable '{name}' with type '{old_type}' a value of type '{value}'"
114                        ),
115                        position: position.to_owned(),
116                    });
117                }
118                let mut new_variable = old_variable.clone();
119                new_variable.variable_type = value;
120                scope.insert(name.to_owned(), new_variable);
121
122                break;
123            }
124        }
125
126        scopes.reverse();
127        self.scope_stack = scopes;
128
129        Ok(())
130    }
131
132    pub fn flatten(&self) -> HashMap<String, Variable> {
133        let mut entries = HashMap::default();
134
135        for scope in &self.scope_stack {
136            let scope = scope.borrow();
137
138            for (key, value) in scope.iter() {
139                entries.insert(key.to_owned(), value.to_owned());
140            }
141        }
142
143        entries
144    }
145}
146
147pub fn setup_scope() -> TypeScope {
148    let mut scope = TypeScope::default();
149
150    scope.push();
151
152    scope
153}