use microcad_lang_base::{Identifier, ResourceLocation, SrcRef};
use crate::{
eval::*,
model::*,
symbol::{Symbol, SymbolDef, SymbolMap},
};
pub enum StackFrame {
Source(Identifier, SymbolMap),
Init(SymbolMap),
Workbench(Model, Identifier, SymbolMap),
Body(SymbolMap),
Function(Identifier, SymbolMap),
Call {
symbol: Symbol,
args: ArgumentValueList,
src_ref: SrcRef,
},
}
impl StackFrame {
pub fn id(&self) -> Option<Identifier> {
match self {
StackFrame::Source(id, _) => Some(id.clone()),
_ => None,
}
}
pub fn symbol(&self) -> Option<Symbol> {
match &self {
StackFrame::Call { symbol, .. } => Some(symbol.clone()),
_ => None,
}
}
pub fn kind_str(&self) -> &'static str {
match self {
StackFrame::Source(..) => "source",
StackFrame::Init(..) => "init",
StackFrame::Workbench(..) => "workbench",
StackFrame::Body(..) => "body",
StackFrame::Function(..) => "function",
StackFrame::Call { .. } => "call",
}
}
pub fn locals(&self) -> Option<&SymbolMap> {
match self {
StackFrame::Source(_, locals)
| StackFrame::Init(locals)
| StackFrame::Workbench(.., locals)
| StackFrame::Body(locals)
| StackFrame::Function(_, locals) => Some(locals),
StackFrame::Call { .. } => None,
}
}
pub fn print_locals(
&self,
f: &mut std::fmt::Formatter<'_>,
idx: usize,
mut depth: usize,
) -> std::fmt::Result {
use crate::lower::Identifiable;
let locals = match self {
StackFrame::Source(id, locals) => {
writeln!(f, "{:depth$}[{idx}] Source: {id:?}", "")?;
locals
}
StackFrame::Init(locals) => {
writeln!(f, "{:depth$}[{idx}] Init", "")?;
locals
}
StackFrame::Workbench(_, id, locals) => {
writeln!(f, "{:depth$}[{idx}] Workbench: {id:?}", "")?;
locals
}
StackFrame::Body(locals) => {
writeln!(f, "{:depth$}[{idx}] Body:", "")?;
locals
}
StackFrame::Function(id, locals) => {
writeln!(f, "{:depth$}[{idx}] Function: {id:?}", "")?;
locals
}
StackFrame::Call {
symbol,
args,
src_ref: _,
} => {
return writeln!(
f,
"{:depth$}[{idx}] Call: {name:?}({args:?})",
"",
args = args,
name = symbol.full_name()
);
}
};
depth += 4;
for (id, symbol) in locals.iter() {
let full_name = symbol.full_name();
let full_name = if full_name != id.into() {
format!(" [{full_name:?}]")
} else {
String::new()
};
let entry = symbol.with_def(|def| match def {
SymbolDef::Root => unreachable!("<ROOT> cannot be a local"),
SymbolDef::Value(id, value) => {
format!("{id:?} = {value:?}{full_name} (local value)",)
}
SymbolDef::Assignment(a) => {
format!("{a}{full_name} (assignment)")
}
SymbolDef::SourceFile(source) => {
format!("{:?} (source)", source.filename())
}
SymbolDef::Module(def) => {
format!("{:?}{full_name} (module)", def.id_ref())
}
SymbolDef::Workbench(def) => {
format!("{:?}{full_name} (workbench)", def.id_ref())
}
SymbolDef::Function(def) => {
format!("{:?}{full_name} (function)", def.id_ref())
}
SymbolDef::Builtin(builtin) => {
format!("{:?}{full_name} (builtin)", builtin.id_ref())
}
SymbolDef::Alias(visibility, id, name) => {
format!("{visibility}{id:?}{full_name} -> {name:?} (alias)")
}
SymbolDef::UseAll(visibility, name) => {
format!("{visibility}{name:?}{full_name} (use all)",)
}
#[cfg(test)]
SymbolDef::Tester(id) => format!("{id:?} (tester)"),
});
if symbol.is_used() {
writeln!(f, "{:depth$}- {entry}", "")?;
}
}
Ok(())
}
pub fn print_stack(
&self,
f: &mut dyn std::fmt::Write,
source_by_hash: &impl GetSourceByHash,
idx: usize,
) -> std::fmt::Result {
match self {
StackFrame::Call {
symbol,
args,
src_ref,
} => {
writeln!(
f,
"{:>4}: {name:?}({args:?})",
idx,
name = symbol.full_name()
)?;
if let Some(line_col) = src_ref.at() {
let source_file = source_by_hash.get_by_hash(src_ref.source_hash());
writeln!(
f,
" at {filename}:{line_col}",
filename = source_file
.as_ref()
.map(|sf| sf
.to_file_path()
.unwrap_or(std::path::PathBuf::from("<NO FILE>")))
.unwrap_or(std::path::PathBuf::from("<NO FILE>"))
.to_string_lossy()
)?;
}
}
_ => unreachable!(),
}
Ok(())
}
}