microcad_lang/eval/symbols/
symbol_table.rs1use crate::{eval::*, model::*, rc::*, resolve::*, syntax::*};
5
6#[derive(Default)]
17pub struct SymbolTable {
18 sources: Sources,
19 pub root: Symbol,
21 pub stack: Stack,
23 pub symbols: SymbolMap,
25 pub diag_handler: DiagHandler,
27}
28
29impl SymbolTable {
30 pub fn new(root: Identifier, symbols: SymbolMap, sources: Sources) -> ResolveResult<Self> {
35 let symbol_table = Self {
38 sources,
39 root: symbols.search(&QualifiedName::from_id(root))?,
40 stack: Default::default(),
41 symbols,
42 diag_handler: Default::default(),
43 };
44 log::trace!("Initial symbol table:\n{symbol_table}");
45 Ok(symbol_table)
46 }
47
48 #[cfg(test)]
50 pub fn fetch_local(&self, id: &Identifier) -> EvalResult<Symbol> {
51 self.stack.fetch(id)
52 }
53
54 pub fn lookup_global(&mut self, name: &QualifiedName) -> ResolveResult<Symbol> {
56 log::trace!("Looking for global symbol '{name:?}'");
57 let symbol = match self.symbols.search(name) {
58 Ok(symbol) => symbol.clone(),
59 Err(err) => return Err(err)?,
60 };
61 log::trace!(
62 "{found} global symbol: {symbol}",
63 found = crate::mark!(FOUND),
64 );
65 Ok(symbol)
66 }
67
68 fn lookup_local(&mut self, name: &QualifiedName) -> EvalResult<Symbol> {
70 log::trace!("Looking for local symbol '{name:?}'");
71 let symbol = if let Some(id) = name.single_identifier() {
72 self.stack.fetch(id)
73 } else {
74 let (id, mut tail) = name.split_first();
75 let local = self.stack.fetch(&id)?;
76 let mut alias = local.full_name();
77 alias.append(&mut tail);
78 log::trace!("Following alias {alias}");
79 self.lookup(&alias)
80 };
81
82 match symbol {
83 Ok(symbol) => {
84 log::trace!(
85 "{found} local symbol: {symbol}",
86 found = crate::mark!(FOUND),
87 );
88 Ok(symbol)
89 }
90 Err(err) => Err(err),
91 }
92 }
93
94 fn lookup_within(&mut self, what: &QualifiedName, within: QualifiedName) -> EvalResult<Symbol> {
95 log::trace!("Looking for symbol '{what:?}' within '{within:?}':",);
96
97 let (what, within) = what.dissolve_super(within);
99
100 let parents = self.symbols.path_to(&within)?;
101 for (n, parent) in parents.iter().rev().enumerate() {
102 log::trace!(" Looking in: {:?} for {:?}", parent.full_name(), what);
103 if let Some(symbol) = parent.search(&what) {
104 let alias = self.follow_alias(&symbol)?;
105 if n > 0 {
106 if symbol.is_private() {
107 return Err(EvalError::SymbolIsPrivate {
108 what: what.clone(),
109 within,
110 });
111 }
112 if alias != symbol && alias.is_private() {
113 return Err(EvalError::SymbolBehindAliasIsPrivate {
114 what: what.clone(),
115 alias: alias.full_name(),
116 within,
117 });
118 }
119 }
120 return Ok(alias);
121 }
122 }
123 Err(EvalError::SymbolNotFound(what.clone()))
124 }
125
126 fn lookup_workbench(&mut self, name: &QualifiedName) -> EvalResult<Symbol> {
127 if let Some(workbench) = &self.stack.current_workbench_name() {
128 log::trace!("Looking for symbol '{name:?}' in current workbench '{workbench:?}'");
129 let name = &name.with_prefix(workbench);
130 match self.lookup_global(name) {
131 Ok(symbol) => {
132 if symbol.full_name() == *name {
133 log::trace!(
134 "{found} symbol in current module: {symbol}",
135 found = crate::mark!(FOUND),
136 );
137 return self.follow_alias(&symbol);
138 }
139 }
140 Err(err) => return Err(err)?,
141 };
142 }
143 Err(EvalError::SymbolNotFound(name.clone()))
144 }
145
146 fn de_alias(&mut self, name: &QualifiedName) -> QualifiedName {
147 for p in (1..name.len()).rev() {
148 if let Ok(symbol) = self.lookup_global(&QualifiedName::no_ref(name[0..p].to_vec())) {
149 if let SymbolDefinition::Alias(.., alias) = &symbol.borrow().def {
150 let suffix: QualifiedName = name[p..].iter().cloned().collect();
151 let new_name = suffix.with_prefix(alias);
152 log::trace!("De-aliased name: {name:?} into {new_name:?}");
153 return new_name;
154 }
155 }
156 }
157 name.clone()
158 }
159
160 fn follow_alias(&mut self, symbol: &Symbol) -> EvalResult<Symbol> {
161 let def = &symbol.borrow().def;
163 if let SymbolDefinition::Alias(.., name) = def {
164 log::trace!("{found} alias => {name:?}", found = crate::mark!(FOUND));
165 Ok(self.lookup(name)?)
166 } else {
167 Ok(symbol.clone())
168 }
169 }
170
171 pub fn search_paths(&self) -> &Vec<std::path::PathBuf> {
173 self.sources.search_paths()
174 }
175
176 pub fn is_code(&self) -> bool {
178 !matches!(self.stack.current_frame(), Some(StackFrame::Module(..)))
179 }
180
181 pub fn is_module(&self) -> bool {
183 matches!(
184 self.stack.current_frame(),
185 Some(StackFrame::Module(..) | StackFrame::Source(..))
186 )
187 }
188}
189
190impl Lookup for SymbolTable {
191 fn lookup(&mut self, name: &QualifiedName) -> EvalResult<Symbol> {
192 log::debug!("Lookup symbol '{name:?}' (at line {:?}):", name.src_ref());
193
194 let name = &self.de_alias(name);
195
196 log::trace!("- lookups -------------------------------------------------------");
197 let result = [
199 ("local", self.lookup_local(name)),
200 (
201 "module",
202 self.lookup_within(name, self.stack.current_module_name()),
203 ),
204 ("workbench", self.lookup_workbench(name)),
205 ("global", self.lookup_global(name).map_err(|e| e.into())),
206 ]
207 .into_iter();
208
209 log::trace!("- result --------------------------------------------------------");
210 let mut errors = Vec::new();
211
212 let (found, mut ambiguous) = result.fold(
214 (Vec::new(), Vec::new()),
215 |(mut oks, mut ambiguity), (origin, r)| {
216 match r {
217 Ok(symbol) => oks.push((origin, symbol)),
218 Err(EvalError::AmbiguousSymbol { ambiguous, others }) => {
219 ambiguity.push((origin, EvalError::AmbiguousSymbol { ambiguous, others }))
220 }
221 Err(
222 EvalError::SymbolNotFound(_)
223 | EvalError::ResolveError(ResolveError::SymbolNotFound(_))
224 | EvalError::LocalNotFound(_)
225 | EvalError::ResolveError(ResolveError::ExternalPathNotFound(_))
226 | EvalError::ResolveError(ResolveError::NulHash),
227 ) => (),
228 Err(err) => errors.push((origin, err)),
229 }
230 (oks, ambiguity)
231 },
232 );
233
234 if !errors.is_empty() {
236 log::debug!("Unexpected errors while lookup symbol '{name:?}':");
237 errors
238 .iter()
239 .for_each(|(origin, err)| log::error!("Lookup ({origin}) error: {err}"));
240
241 return Err(errors.remove(0).1);
242 }
243
244 if !ambiguous.is_empty() {
246 log::debug!(
247 "{ambiguous} Symbol '{name:?}':\n{}",
248 ambiguous
249 .iter()
250 .map(|(origin, err)| format!("{origin}: {err}"))
251 .collect::<Vec<_>>()
252 .join("\n"),
253 ambiguous = crate::mark!(AMBIGUOUS)
254 );
255 return Err(ambiguous.remove(0).1);
256 }
257
258 let found: Vec<_> = found
260 .into_iter()
261 .filter_map(|(origin, symbol)| {
262 if let Ok(symbol) = self.follow_alias(&symbol) {
263 Some((origin, symbol))
264 } else {
265 None
266 }
267 })
268 .collect();
269
270 match found.first() {
272 Some((origin, first)) => {
273 if !found.iter().all(|(_, x)| Rc::ptr_eq(x, first)) {
275 log::debug!(
276 "{ambiguous} symbol '{name:?}' in {origin}:\n{self}",
277 ambiguous = crate::mark!(AMBIGUOUS),
278 origin = found
279 .iter()
280 .map(|(id, _)| *id)
281 .collect::<Vec<_>>()
282 .join(" and ")
283 );
284 Err(EvalError::AmbiguousSymbol {
285 ambiguous: name.clone(),
286 others: found.iter().map(|(_, x)| x.clone()).collect(),
287 })
288 } else {
289 log::debug!(
290 "{found} symbol '{name:?}' in {origin}",
291 found = crate::mark!(FOUND_INTERIM)
292 );
293 Ok(first.clone())
294 }
295 }
296 None => {
297 log::debug!(
298 "{not_found} Symbol '{name:?}'",
299 not_found = crate::mark!(NOT_FOUND_INTERIM)
300 );
301
302 Err(EvalError::SymbolNotFound(name.clone()))
303 }
304 }
305 }
306}
307
308impl Locals for SymbolTable {
309 fn set_local_value(&mut self, id: Identifier, value: Value) -> EvalResult<()> {
310 self.stack.set_local_value(id, value)
311 }
312
313 fn get_local_value(&self, id: &Identifier) -> EvalResult<Value> {
314 self.stack.get_local_value(id)
315 }
316
317 fn open(&mut self, frame: StackFrame) {
318 self.stack.open(frame);
319 }
320
321 fn close(&mut self) {
322 self.stack.close();
323 }
324
325 fn fetch(&self, id: &Identifier) -> EvalResult<Symbol> {
326 self.stack.fetch(id)
327 }
328
329 fn get_model(&self) -> EvalResult<Model> {
330 self.stack.get_model()
331 }
332
333 fn current_name(&self) -> QualifiedName {
334 self.stack.current_name()
335 }
336}
337
338impl UseSymbol for SymbolTable {
339 fn use_symbol(
340 &mut self,
341 visibility: Visibility,
342 name: &QualifiedName,
343 id: Option<Identifier>,
344 within: &QualifiedName,
345 ) -> EvalResult<Symbol> {
346 log::debug!("Using symbol {name:?}");
347
348 let symbol = self.lookup(name)?;
349 if self.is_module() {
350 let id = id.clone().unwrap_or(symbol.id());
351 let symbol = symbol.clone_with_visibility(visibility);
352 if within.is_empty() {
353 self.symbols.insert(id, symbol);
354 } else {
355 self.symbols
356 .search(within)?
357 .borrow_mut()
358 .children
359 .insert(id, symbol);
360 }
361 log::trace!("Symbol Table:\n{}", self.symbols);
362 }
363
364 if self.is_code() {
365 self.stack.put_local(id, symbol.clone())?;
366 log::trace!("Local Stack:\n{}", self.stack);
367 }
368
369 Ok(symbol)
370 }
371
372 fn use_symbols_of(
373 &mut self,
374 visibility: Visibility,
375 name: &QualifiedName,
376 within: &QualifiedName,
377 ) -> EvalResult<Symbol> {
378 log::debug!("Using all symbols in {name:?}");
379
380 let symbol = self.lookup(name)?;
381 if symbol.is_empty() {
382 Err(EvalError::NoSymbolsToUse(symbol.full_name()))
383 } else {
384 if self.is_module() {
385 for (id, symbol) in symbol.borrow().children.iter() {
386 let symbol = symbol.clone_with_visibility(visibility);
387 if within.is_empty() {
388 self.symbols.insert(id.clone(), symbol);
389 } else {
390 self.symbols
391 .search(within)?
392 .borrow_mut()
393 .children
394 .insert(id.clone(), symbol);
395 }
396 }
397 log::trace!("Symbol Table:\n{}", self.symbols);
398 }
399
400 if self.is_code() {
401 for (id, symbol) in symbol.borrow().children.iter() {
402 self.stack.put_local(Some(id.clone()), symbol.clone())?;
403 }
404 log::trace!("Local Stack:\n{}", self.stack);
405 }
406 Ok(symbol)
407 }
408 }
409}
410
411impl std::fmt::Display for SymbolTable {
412 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
413 write!(f, "\nLoaded files:\n{}", self.sources)?;
414 writeln!(f, "\nCurrent: {}", self.stack.current_name())?;
415 writeln!(f, "\nModule: {}", self.stack.current_module_name())?;
416 write!(f, "\nLocals Stack:\n{}", self.stack)?;
417 writeln!(f, "\nCall Stack:")?;
418 self.stack.pretty_print_call_trace(f, &self.sources)?;
419 writeln!(f, "\nSymbols:\n{}", self.symbols)
420 }
421}
422
423impl GetSourceByHash for SymbolTable {
424 fn get_by_hash(&self, hash: u64) -> ResolveResult<std::rc::Rc<SourceFile>> {
425 self.sources.get_by_hash(hash)
426 }
427}