microcad_lang/eval/symbols/
stack.rs1use crate::{eval::*, model::*, resolve::*};
5
6#[derive(Default)]
15pub struct Stack(Vec<StackFrame>);
16
17impl Stack {
18 pub fn put_local(&mut self, id: Option<Identifier>, symbol: Symbol) -> EvalResult<()> {
21 let id = if let Some(id) = id { id } else { symbol.id() };
22 let name = symbol.full_name();
23 for (pos, frame) in self.0.iter_mut().rev().enumerate() {
24 match frame {
25 StackFrame::Source(_, locals)
26 | StackFrame::Workbench(_, _, locals)
27 | StackFrame::Init(locals)
28 | StackFrame::Body(locals)
29 | StackFrame::Module(_, locals)
30 | StackFrame::Function(locals) => {
31 let op = if locals.insert(id.clone(), symbol).is_some() {
32 "Added"
33 } else {
34 "Set"
35 };
36 if name.is_qualified() {
37 log::debug!("{op} {name} as {id:?} to local stack");
38 } else {
39 log::debug!("{op} {id:?} to local stack");
40 }
41
42 log::trace!("Local Stack:\n{self}");
43 return Ok(());
44 }
45 StackFrame::Call {
46 symbol: _,
47 args: _,
48 src_ref: _,
49 } => {
50 if pos > 0 {
52 return Err(EvalError::WrongStackFrame(id, "call"));
53 }
54 }
55 }
56 }
57 Err(EvalError::LocalStackEmpty(id))
58 }
59
60 fn current_workbench_id(&self) -> Option<&Identifier> {
61 self.0.iter().rev().find_map(|frame| {
62 if let StackFrame::Workbench(_, id, _) = frame {
63 Some(id)
64 } else {
65 None
66 }
67 })
68 }
69
70 pub fn current_module_name(&self) -> QualifiedName {
72 if self.0.is_empty() {
73 QualifiedName::default()
74 } else {
75 let mut module_name = QualifiedName::default();
76 for (n, frame) in self.0.iter().rev().enumerate() {
77 match frame {
78 StackFrame::Source(id, ..) | StackFrame::Module(id, ..) => {
79 module_name.insert(0, id.clone());
80 }
81 StackFrame::Call { symbol, .. } => {
82 if n > 0 {
83 module_name =
85 symbol.full_name().remove_last().with_prefix(&module_name);
86 break;
87 }
88 }
89 _ => (),
90 }
91 }
92
93 module_name
95 }
96 }
97
98 pub fn current_workbench_name(&self) -> Option<QualifiedName> {
100 if let Some(id) = self.current_workbench_id() {
101 let name = QualifiedName::new(vec![id.clone()], id.src_ref());
102 Some(name.with_prefix(&self.current_module_name()))
103 } else {
104 None
105 }
106 }
107
108 pub fn current_frame(&self) -> Option<&StackFrame> {
110 self.0.last()
111 }
112
113 pub fn pretty_print_call_trace(
115 &self,
116 f: &mut dyn std::fmt::Write,
117 source_by_hash: &impl super::GetSourceByHash,
118 ) -> std::fmt::Result {
119 let mut none: bool = true;
120 for (idx, frame) in self
121 .0
122 .iter()
123 .filter(|frame| {
124 matches!(
125 frame,
126 StackFrame::Call {
127 symbol: _,
128 args: _,
129 src_ref: _
130 }
131 )
132 })
133 .enumerate()
134 {
135 none = false;
136 frame.print_stack(f, source_by_hash, idx)?;
137 }
138 if none {
139 writeln!(f, crate::invalid!(STACK))?
140 }
141 Ok(())
142 }
143
144 pub(crate) fn current_symbol(&self) -> Option<Symbol> {
145 self.0.iter().rev().find_map(|frame| frame.symbol())
146 }
147}
148
149impl Locals for Stack {
150 fn open(&mut self, frame: StackFrame) {
151 if let Some(id) = frame.id() {
152 log::trace!("Opening {} stack frame '{id}'", frame.kind_str());
153 } else {
154 log::trace!("Opening {} stack frame", frame.kind_str());
155 }
156 self.0.push(frame);
157 }
158
159 fn close(&mut self) {
160 if let Some(frame) = self.0.pop() {
161 log::trace!("Closing {} stack frame", frame.kind_str());
162 }
163 }
164
165 fn set_local_value(&mut self, id: Identifier, value: Value) -> EvalResult<()> {
166 self.put_local(
167 Some(id.clone()),
168 Symbol::new(
169 SymbolDefinition::Constant(Visibility::Private, id, value),
170 None,
171 ),
172 )
173 }
174
175 fn get_local_value(&self, id: &Identifier) -> EvalResult<Value> {
176 match self.fetch(id) {
177 Ok(symbol) => match &symbol.borrow().def {
178 SymbolDefinition::Constant(.., value) | SymbolDefinition::Argument(.., value) => {
179 Ok(value.clone())
180 }
181 _ => Err(EvalError::LocalNotFound(id.clone())),
182 },
183 Err(_) => Err(EvalError::LocalNotFound(id.clone())),
184 }
185 }
186
187 fn get_model(&self) -> EvalResult<Model> {
188 match self
189 .0
190 .iter()
191 .rev()
192 .find(|frame| matches!(frame, StackFrame::Workbench(_, _, _)))
193 {
194 Some(StackFrame::Workbench(model, _, _)) => Ok(model.clone()),
195 _ => Err(EvalError::NoModelInWorkbench),
196 }
197 }
198
199 fn fetch(&self, id: &Identifier) -> EvalResult<Symbol> {
200 for (n, frame) in self.0.iter().rev().enumerate() {
202 match frame {
203 StackFrame::Source(_, locals)
204 | StackFrame::Body(locals)
205 | StackFrame::Workbench(_, _, locals)
206 | StackFrame::Init(locals)
207 | StackFrame::Function(locals) => {
208 if let Some(local) = locals.get(id) {
209 log::trace!("fetched {id:?} from locals");
210 return Ok(local.clone());
211 }
212 }
213 StackFrame::Module(_, _) => {
215 log::trace!("stop at call frame");
216 break;
217 }
218 StackFrame::Call {
220 symbol: _,
221 args: _,
222 src_ref: _,
223 } => {
224 if n > 0 {
225 break;
226 }
227 }
228 }
229 }
230 Err(EvalError::LocalNotFound(id.clone()))
231 }
232
233 fn current_name(&self) -> QualifiedName {
235 if let Some(id) = self.current_workbench_id() {
236 let name = QualifiedName::new(vec![id.clone()], id.src_ref());
237 name.with_prefix(&self.current_module_name())
238 } else {
239 self.current_module_name()
240 }
241 }
242}
243
244impl std::fmt::Display for Stack {
245 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
246 if self.0.is_empty() {
247 writeln!(f, crate::invalid!(STACK))
248 } else {
249 for (n, locals) in self.0.iter().enumerate() {
250 locals.print_locals(f, n, 0)?;
251 }
252 Ok(())
253 }
254 }
255}
256
257#[test]
258#[allow(clippy::unwrap_used)]
259fn local_stack() {
260 let mut stack = Stack::default();
261
262 let make_int = |id, value| {
263 Symbol::new(
264 SymbolDefinition::Constant(Visibility::Private, id, Value::Integer(value)),
265 None,
266 )
267 };
268
269 let fetch_int = |stack: &Stack, id: &str| -> Option<i64> {
270 match stack.fetch(&id.into()) {
271 Ok(node) => match &node.borrow().def {
272 SymbolDefinition::Constant(.., Value::Integer(value)) => Some(*value),
273 _ => todo!("error"),
274 },
275 _ => None,
276 }
277 };
278
279 let root_name = "test".into();
280 let root_id = QualifiedName::from_id(root_name);
281 stack.open(StackFrame::Source("test".into(), SymbolMap::default()));
282 assert!(stack.current_module_name() == root_id);
283
284 assert!(stack.put_local(None, make_int("a".into(), 1)).is_ok());
285
286 println!("{stack}");
287
288 assert!(fetch_int(&stack, "a").unwrap() == 1);
289 assert!(fetch_int(&stack, "b").is_none());
290 assert!(fetch_int(&stack, "c").is_none());
291
292 stack.open(StackFrame::Body(SymbolMap::default()));
293 assert!(stack.current_module_name() == root_id);
294
295 assert!(fetch_int(&stack, "a").unwrap() == 1);
296 assert!(fetch_int(&stack, "b").is_none());
297 assert!(fetch_int(&stack, "c").is_none());
298
299 assert!(stack.put_local(None, make_int("b".into(), 2)).is_ok());
300
301 assert!(fetch_int(&stack, "a").unwrap() == 1);
302 assert!(fetch_int(&stack, "b").unwrap() == 2);
303 assert!(fetch_int(&stack, "c").is_none());
304
305 assert!(stack
307 .put_local(Some("x".into()), make_int("x".into(), 3))
308 .is_ok());
309
310 assert!(fetch_int(&stack, "a").unwrap() == 1);
311 assert!(fetch_int(&stack, "b").unwrap() == 2);
312 assert!(fetch_int(&stack, "x").unwrap() == 3);
313
314 stack.close();
315 assert!(stack.current_module_name() == root_id);
316
317 assert!(fetch_int(&stack, "a").unwrap() == 1);
318 assert!(fetch_int(&stack, "b").is_none());
319 assert!(fetch_int(&stack, "c").is_none());
320
321 stack.close();
322 assert!(stack.current_module_name().is_empty());
323}