miden_assembly_syntax/ast/item/resolver/
symbol_table.rs1use alloc::{collections::BTreeMap, sync::Arc, vec::Vec};
2
3use miden_debug_types::{SourceManager, Span, Spanned};
4
5use super::{SymbolResolution, SymbolResolutionError};
6use crate::{
7 Path,
8 ast::{AliasTarget, Ident, ItemIndex},
9};
10
11pub trait SymbolTable {
13 type SymbolIter: Iterator<Item = LocalSymbol>;
15
16 fn symbols(&self, source_manager: Arc<dyn SourceManager>) -> Self::SymbolIter;
19}
20
21impl SymbolTable for &crate::library::ModuleInfo {
22 type SymbolIter = alloc::vec::IntoIter<LocalSymbol>;
23
24 fn symbols(&self, _source_manager: Arc<dyn SourceManager>) -> Self::SymbolIter {
25 let module_items = self.items();
26 let mut items = Vec::with_capacity(module_items.len());
27
28 for (index, item) in module_items {
29 let name = item.name().clone();
30 let span = name.span();
31
32 assert_eq!(index.as_usize(), items.len());
33 items.push(LocalSymbol::Item {
34 name,
35 resolved: SymbolResolution::Local(Span::new(span, index)),
36 });
37 }
38
39 items.into_iter()
40 }
41}
42
43impl SymbolTable for &crate::ast::Module {
44 type SymbolIter = alloc::vec::IntoIter<LocalSymbol>;
45
46 fn symbols(&self, source_manager: Arc<dyn SourceManager>) -> Self::SymbolIter {
47 use crate::ast::{AliasTarget, Export};
48
49 let mut items = Vec::with_capacity(self.items.len());
50
51 for (i, item) in self.items.iter().enumerate() {
52 let id = ItemIndex::new(i);
53 let name = item.name().clone();
54 let span = name.span();
55 let name = name.into_inner();
56
57 if let Export::Alias(alias) = item {
58 match alias.target() {
59 AliasTarget::MastRoot(root) => {
60 items.push(LocalSymbol::Import {
61 name: Span::new(span, name),
62 resolution: Ok(SymbolResolution::MastRoot(*root)),
63 });
64 },
65 AliasTarget::Path(path) => {
66 let expanded = LocalSymbolTable::expand(
67 |name| self.get_import(name).map(|alias| alias.target().clone()),
68 path.as_deref(),
69 &source_manager,
70 );
71 items.push(LocalSymbol::Import {
72 name: Span::new(span, name),
73 resolution: expanded,
74 });
75 },
76 }
77 } else {
78 items.push(LocalSymbol::Item {
79 name: Ident::from_raw_parts(Span::new(span, name)),
80 resolved: SymbolResolution::Local(Span::new(span, id)),
81 });
82 }
83 }
84
85 items.into_iter()
86 }
87}
88
89#[derive(Debug)]
91pub enum LocalSymbol {
92 Item { name: Ident, resolved: SymbolResolution },
94 Import {
96 name: Span<Arc<str>>,
97 resolution: Result<SymbolResolution, SymbolResolutionError>,
98 },
99}
100
101impl LocalSymbol {
102 pub fn name(&self) -> &str {
103 match self {
104 Self::Item { name, .. } => name.as_str(),
105 Self::Import { name, .. } => name,
106 }
107 }
108}
109
110pub(super) struct LocalSymbolTable {
112 source_manager: Arc<dyn SourceManager>,
113 symbols: BTreeMap<Arc<str>, ItemIndex>,
114 items: Vec<LocalSymbol>,
115}
116
117impl core::ops::Index<ItemIndex> for LocalSymbolTable {
118 type Output = LocalSymbol;
119
120 #[inline(always)]
121 fn index(&self, index: ItemIndex) -> &Self::Output {
122 &self.items[index.as_usize()]
123 }
124}
125
126impl LocalSymbolTable {
127 pub fn new<S>(iter: S, source_manager: Arc<dyn SourceManager>) -> Self
128 where
129 S: SymbolTable,
130 {
131 let mut symbols = BTreeMap::default();
132 let mut items = Vec::with_capacity(16);
133
134 for (i, symbol) in iter.symbols(source_manager.clone()).enumerate() {
135 log::debug!(target: "symbol-table::new", "registering {} symbol: {}", match symbol {
136 LocalSymbol::Item { .. } => "local",
137 LocalSymbol::Import { .. } => "imported",
138 }, symbol.name());
139
140 let id = ItemIndex::new(i);
141 let name = match &symbol {
142 LocalSymbol::Item { name, .. } => name.clone().into_inner(),
143 LocalSymbol::Import { name, .. } => name.clone().into_inner(),
144 };
145
146 symbols.insert(name, id);
147 items.push(symbol);
148 }
149
150 Self { source_manager, symbols, items }
151 }
152
153 #[inline(always)]
154 pub fn source_manager(&self) -> &dyn SourceManager {
155 &self.source_manager
156 }
157
158 #[inline(always)]
159 pub fn source_manager_arc(&self) -> Arc<dyn SourceManager> {
160 self.source_manager.clone()
161 }
162}
163
164impl LocalSymbolTable {
165 pub fn get(&self, name: Span<&str>) -> Result<SymbolResolution, SymbolResolutionError> {
175 log::debug!(target: "symbol-table", "attempting to resolve '{name}'");
176 let (span, name) = name.into_parts();
177 let Some(item) = self.symbols.get(name).copied() else {
178 return Err(SymbolResolutionError::undefined(span, &self.source_manager));
179 };
180 match &self.items[item.as_usize()] {
181 LocalSymbol::Item { resolved, .. } => {
182 log::debug!(target: "symbol-table", "resolved '{name}' to {resolved:?}");
183 Ok(resolved.clone())
184 },
185 LocalSymbol::Import { name, resolution } => {
186 log::debug!(target: "symbol-table", "'{name}' refers to an import");
187 match resolution {
188 Ok(resolved) => {
189 log::debug!(target: "symbol-table", "resolved '{name}' to {resolved:?}");
190 Ok(resolved.clone())
191 },
192 Err(err) => {
193 log::error!(target: "symbol-table", "resolution of '{name}' failed: {err}");
194 Err(err.clone())
195 },
196 }
197 },
198 }
199 }
200
201 pub fn expand<F>(
235 get_import: F,
236 path: Span<&Path>,
237 source_manager: &dyn SourceManager,
238 ) -> Result<SymbolResolution, SymbolResolutionError>
239 where
240 F: Fn(&str) -> Option<AliasTarget>,
241 {
242 let (module_name, rest) = path.split_first().unwrap();
243 if let Some(target) = get_import(module_name) {
244 match target {
245 AliasTarget::MastRoot(digest) if rest.is_empty() => {
246 Ok(SymbolResolution::MastRoot(digest))
247 },
248 AliasTarget::MastRoot(digest) => Err(SymbolResolutionError::invalid_alias_target(
249 digest.span(),
250 path.span(),
251 source_manager,
252 )),
253 AliasTarget::Path(shadowed) if shadowed.as_deref() == path => {
261 Ok(SymbolResolution::External(shadowed.clone()))
262 },
263 AliasTarget::Path(path) => {
264 let path = path.clone();
265 let resolved = Self::expand(get_import, path.as_deref(), source_manager)?;
266 match resolved {
267 SymbolResolution::Module { id, path } => {
268 if rest.is_empty() {
271 Ok(SymbolResolution::Module { id, path })
272 } else {
273 Ok(SymbolResolution::External(path.map(|p| p.join(rest).into())))
274 }
275 },
276 SymbolResolution::External(resolved) => {
277 Ok(SymbolResolution::External(
280 resolved.map(|p| p.to_absolute().join(rest).into()),
281 ))
282 },
283 res @ (SymbolResolution::MastRoot(_)
284 | SymbolResolution::Local(_)
285 | SymbolResolution::Exact { .. })
286 if rest.is_empty() =>
287 {
288 Ok(res)
289 },
290 SymbolResolution::MastRoot(digest) => {
291 Err(SymbolResolutionError::invalid_alias_target(
292 digest.span(),
293 path.span(),
294 source_manager,
295 ))
296 },
297 SymbolResolution::Exact { path: item_path, .. } => {
298 Err(SymbolResolutionError::invalid_alias_target(
299 item_path.span(),
300 path.span(),
301 source_manager,
302 ))
303 },
304 SymbolResolution::Local(item) => {
305 Err(SymbolResolutionError::invalid_alias_target(
306 item.span(),
307 path.span(),
308 source_manager,
309 ))
310 },
311 }
312 },
313 }
314 } else {
315 Ok(SymbolResolution::External(path.map(|p| p.to_absolute().into_owned().into())))
317 }
318 }
319}