microcad_lang/eval/symbols/
stack_frame.rs

1// Copyright © 2025 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4use crate::{eval::*, model::*};
5
6/// Frame in [Stack] for *local variables*, *aliases* (*use statements*) and *calls*.
7///
8/// A *stack frame* can have different types and some provide a storage for *local variables*
9/// like [`StackFrame::Source`] and [`StackFrame::Body`]) and some do not, some have a *id*
10/// like [`StackFrame::Source`] amd [`StackFrame::Module`]) and some do not and
11/// [`Call`] is used for procedural calls.
12///
13/// Each frame store some of these information:
14///   - an [`Identifier`]
15///   - local variables in a [`SymbolMap`] (e.g. `i = 5;`)
16///   - local aliases in a [`SymbolMap`] (e.g. `use std::print;`)
17///   - argument value list (e.g. `f(x = 0, y = 1);`
18pub enum StackFrame {
19    /// Source file with locals.
20    Source(Identifier, SymbolMap),
21    /// Module scope with locals.
22    Module(Identifier, SymbolMap),
23    /// initializer scope with locals.
24    Init(SymbolMap),
25    /// Part scope with locals.
26    Workbench(Model, Identifier, SymbolMap),
27    /// Body (scope)  with locals.
28    Body(SymbolMap),
29    /// Function body
30    Function(SymbolMap),
31    /// A call (e.g. og function or  part).
32    Call {
33        /// Symbol that was called.
34        symbol: Symbol,
35        /// Evaluated arguments.
36        args: ArgumentValueList,
37        /// Source code reference.
38        src_ref: SrcRef,
39    },
40}
41
42impl StackFrame {
43    /// Get identifier if available or panic.
44    pub fn id(&self) -> Option<Identifier> {
45        match self {
46            StackFrame::Source(id, _) | StackFrame::Module(id, _) => Some(id.clone()),
47            _ => None,
48        }
49    }
50
51    /// Return symbol of the stack frame, if there is any.
52    pub fn symbol(&self) -> Option<Symbol> {
53        match &self {
54            StackFrame::Call { symbol, .. } => Some(symbol.clone()),
55            _ => None,
56        }
57    }
58
59    /// Return stack frame kind as str
60    pub fn kind_str(&self) -> &'static str {
61        match self {
62            StackFrame::Source(_, _) => "source",
63            StackFrame::Module(_, _) => "module",
64            StackFrame::Init(_) => "init",
65            StackFrame::Workbench(_, _, _) => "workbench",
66            StackFrame::Body(_) => "body",
67            StackFrame::Function(_) => "function",
68            StackFrame::Call {
69                symbol: _,
70                args: _,
71                src_ref: _,
72            } => "call",
73        }
74    }
75
76    /// Print stack frame.
77    pub fn print_locals(
78        &self,
79        f: &mut std::fmt::Formatter<'_>,
80        idx: usize,
81        mut depth: usize,
82    ) -> std::fmt::Result {
83        let locals = match self {
84            StackFrame::Source(id, locals) => {
85                writeln!(f, "{:depth$}[{idx}] Source: {id:?}", "")?;
86                locals
87            }
88            StackFrame::Module(id, locals) => {
89                writeln!(f, "{:depth$}[{idx}] Module: {id:?}", "")?;
90                locals
91            }
92            StackFrame::Init(locals) => {
93                writeln!(f, "{:depth$}[{idx}] Init", "")?;
94                locals
95            }
96            StackFrame::Workbench(_, id, locals) => {
97                writeln!(f, "{:depth$}[{idx}] Workbench: {id:?}", "")?;
98                locals
99            }
100            StackFrame::Body(locals) => {
101                writeln!(f, "{:depth$}[{idx}] Body:", "")?;
102                locals
103            }
104            StackFrame::Function(locals) => {
105                writeln!(f, "{:depth$}[{idx}] Function:", "")?;
106                locals
107            }
108            StackFrame::Call {
109                symbol,
110                args,
111                src_ref: _,
112            } => {
113                return writeln!(
114                    f,
115                    "{:depth$}[{idx}] Call: {name:?}({args})",
116                    "",
117                    args = args,
118                    name = symbol.full_name()
119                );
120            }
121        };
122
123        depth += 4;
124
125        for (id, symbol) in locals.iter() {
126            let full_name = symbol.full_name();
127            let full_name = if full_name != id.into() {
128                format!(" [{full_name}]")
129            } else {
130                String::new()
131            };
132            match &symbol.borrow().def {
133                SymbolDefinition::Constant(visibility, id, value) => writeln!(
134                    f,
135                    "{:depth$}- {visibility}{id:?} = {value}{full_name} (constant)",
136                    ""
137                )?,
138                SymbolDefinition::Argument(id, value) => {
139                    writeln!(f, "{:depth$}- {id:?} = {value}{full_name} (argument)", "")?
140                }
141                SymbolDefinition::SourceFile(source) => {
142                    writeln!(f, "{:depth$}- {:?} (source)", "", source.filename())?
143                }
144                SymbolDefinition::Module(def) => {
145                    writeln!(f, "{:depth$}- {:?}{full_name} (module)", "", def.id)?
146                }
147                SymbolDefinition::Workbench(def) => {
148                    writeln!(f, "{:depth$}- {:?}{full_name} (workbench)", "", def.id)?
149                }
150                SymbolDefinition::Function(def) => {
151                    writeln!(f, "{:depth$}- {:?}{full_name} (function)", "", def.id)?
152                }
153                SymbolDefinition::Builtin(builtin) => {
154                    writeln!(f, "{:depth$}- {:?}{full_name} (builtin)", "", builtin.id)?
155                }
156                SymbolDefinition::Alias(visibility, id, name) => writeln!(
157                    f,
158                    "{:depth$}- {visibility}{id:?}{full_name} -> {name} (alias)",
159                    ""
160                )?,
161                SymbolDefinition::UseAll(visibility, name) => {
162                    writeln!(f, "{:depth$}- {visibility}{name}{full_name} (use all)", "")?
163                }
164                #[cfg(test)]
165                SymbolDefinition::Tester(id) => writeln!(f, "{:depth$}- {id} (tester)", "")?,
166            }
167        }
168
169        Ok(())
170    }
171
172    /// Pretty print single call stack frame.
173    pub fn print_stack(
174        &self,
175        f: &mut dyn std::fmt::Write,
176        source_by_hash: &impl GetSourceByHash,
177        idx: usize,
178    ) -> std::fmt::Result {
179        match self {
180            StackFrame::Source(_identifier, _locals) => todo!(),
181            StackFrame::Module(_identifier, _locals) => todo!(),
182            StackFrame::Init(_locals) => todo!(),
183            StackFrame::Workbench(_kind, _identifier, _locals) => todo!(),
184            StackFrame::Body(_locals) => todo!(),
185            StackFrame::Function(_locals) => todo!(),
186            StackFrame::Call {
187                symbol,
188                args,
189                src_ref,
190            } => {
191                writeln!(f, "{:>4}: {name}({args})", idx, name = symbol.full_name())?;
192
193                if let Some(line_col) = src_ref.at() {
194                    let source_file = source_by_hash.get_by_hash(src_ref.source_hash());
195                    writeln!(
196                        f,
197                        "            at {filename}:{line_col}",
198                        filename = source_file
199                            .as_ref()
200                            .map(|sf| sf.filename_as_str())
201                            .unwrap_or(crate::invalid!(FILE)),
202                    )?;
203                }
204            }
205        }
206
207        Ok(())
208    }
209}