datex_core/compiler/precompiler/
scope_stack.rs1use crate::{
2 compiler::error::CompilerError,
3 compiler::precompiler::{
4 precompiled_ast::{AstMetadata, VariableMetadata, VariableShape},
5 scope::PrecompilerScope,
6 },
7};
8
9#[derive(Debug, Clone)]
10pub struct PrecompilerScopeStack {
11 pub scopes: Vec<PrecompilerScope>,
12}
13
14impl Default for PrecompilerScopeStack {
15 fn default() -> Self {
16 PrecompilerScopeStack {
17 scopes: vec![PrecompilerScope::default()],
18 }
19 }
20}
21
22impl PrecompilerScopeStack {
23 pub fn push_scope(&mut self) {
24 self.scopes.push(PrecompilerScope::new_with_realm_index(
25 self.scopes.last().map_or(0, |s| s.realm_index),
26 ));
27 }
28
29 pub fn pop_scope(&mut self) {
30 if !self.scopes.is_empty() {
31 self.scopes.pop();
32 } else {
33 unreachable!("Cannot pop scope from an empty scope stack");
34 }
35 }
36
37 pub fn increment_realm_index(&mut self) {
39 if let Some(scope) = self.scopes.last_mut() {
40 scope.realm_index += 1;
41 } else {
42 unreachable!("Scope stack must always have at least one scope");
43 }
44 }
45
46 pub fn current_realm_index(&self) -> usize {
47 self.scopes.last().map_or(0, |s| s.realm_index)
48 }
49
50 pub fn add_new_variable(
51 &mut self,
52 name: String,
53 id: usize,
54 kind: VariableShape,
55 ) -> VariableMetadata {
56 let current_realm_index =
57 self.scopes.last().map_or(0, |s| s.realm_index);
58 let var_metadata = VariableMetadata {
59 is_cross_realm: false,
60 original_realm_index: current_realm_index,
61 shape: kind,
62 var_type: None,
63 name: name.clone(),
64 };
65 self.set_variable(name, id);
66 var_metadata
67 }
68
69 pub fn get_variable_and_update_metadata(
70 &self,
71 name: &str,
72 metadata: &mut AstMetadata,
73 ) -> Result<usize, CompilerError> {
74 if let Some(var_id) = self.get_variable(name) {
76 let var_metadata = metadata.variable_metadata_mut(var_id).unwrap();
77 if var_metadata.original_realm_index != self.current_realm_index() {
79 var_metadata.is_cross_realm = true;
80 }
81 Ok(var_id)
82 } else {
83 Err(CompilerError::UndeclaredVariable(name.to_string()))
84 }
85 }
86
87 pub fn set_variable(&mut self, name: String, id: usize) {
88 self.get_active_scope_mut()
89 .variable_ids_by_name
90 .insert(name, id);
91 }
92
93 fn get_active_scope_index(&self) -> usize {
94 if self.scopes.len() > 1 {
96 self.scopes.len() - 2
97 } else {
98 0
99 }
100 }
101
102 pub fn get_active_scope(&self) -> &PrecompilerScope {
103 self.scopes.get(self.get_active_scope_index()).unwrap()
104 }
105
106 pub fn get_active_scope_mut(&mut self) -> &mut PrecompilerScope {
107 let index = self.get_active_scope_index();
108 self.scopes.get_mut(index).unwrap()
109 }
110
111 pub fn get_variable(&self, name: &str) -> Option<usize> {
112 for scope in self.scopes.iter().rev() {
113 if let Some(id) = scope.variable_ids_by_name.get(name) {
114 return Some(*id);
115 }
116 }
117 None
118 }
119 pub fn has_variable(&self, name: &str) -> bool {
120 self.get_variable(name).is_some()
121 }
122
123 pub fn metadata<'a>(
124 &self,
125 name: &str,
126 metadata: &'a AstMetadata,
127 ) -> Option<&'a VariableMetadata> {
128 if let Some(var_id) = self.get_variable(name) {
129 metadata.variable_metadata(var_id)
130 } else {
131 None
132 }
133 }
134 pub fn variable_kind(
135 &self,
136 name: &str,
137 metadata: &AstMetadata,
138 ) -> Option<VariableShape> {
139 if let Some(var_id) = self.get_variable(name) {
140 metadata.variable_metadata(var_id).map(|v| v.shape)
141 } else {
142 None
143 }
144 }
145}