microcad_lang/resolve/
symbol_table.rs

1// Copyright © 2024-2025 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4use derive_more::{Deref, DerefMut};
5
6use crate::{resolve::*, syntax::*};
7
8/// *Symbol table* holding global symbols.
9#[derive(Default, Deref, DerefMut)]
10pub struct SymbolTable(SymbolMap);
11
12impl SymbolTable {
13    /// Add a new symbol to the table
14    pub fn add_symbol(&mut self, symbol: Symbol) -> ResolveResult<()> {
15        self.insert_symbol(symbol.id(), symbol.clone())
16    }
17
18    /// Add a new symbol to the table
19    pub fn insert_symbol(&mut self, id: Identifier, symbol: Symbol) -> ResolveResult<()> {
20        log::trace!("insert symbol: {id}");
21        if let Some(symbol) = self.insert(id, symbol.clone()) {
22            Err(ResolveError::SymbolAlreadyDefined(symbol.full_name()))
23        } else {
24            Ok(())
25        }
26    }
27
28    pub(super) fn symbols(&self) -> Symbols {
29        self.values().cloned().collect()
30    }
31
32    /// Return a list of symbols which could not or have not been checked.
33    pub fn unchecked(&self) -> Symbols {
34        let mut unchecked = Symbols::default();
35        self.values()
36            .for_each(|symbol| symbol.unchecked(&mut unchecked));
37        unchecked
38    }
39
40    /// Return a list of unused symbols
41    ///
42    /// Use this after eval for any useful result.
43    pub fn unused(&self) -> Symbols {
44        let mut unchecked = Symbols::default();
45        self.values()
46            .for_each(|symbol| symbol.unused(&mut unchecked));
47        unchecked
48    }
49
50    /// Search all ids which require target mode (e.g. `assert_valid`)
51    pub(super) fn search_target_mode_ids(&self) -> ResolveResult<IdentifierSet> {
52        let mut ids = IdentifierSet::default();
53        self.values()
54            .try_for_each(|symbol| symbol.search_target_mode_ids(&mut ids))?;
55        Ok(ids)
56    }
57}
58
59impl WriteToFile for SymbolTable {}
60
61impl Lookup for SymbolTable {
62    /// Lookup a symbol from global symbols.
63    fn lookup(&self, name: &QualifiedName) -> ResolveResult<Symbol> {
64        log::trace!(
65            "{lookup} for global symbol '{name:?}'",
66            lookup = crate::mark!(LOOKUP)
67        );
68        self.deny_super(name)?;
69
70        let symbol = match self.search(name, true) {
71            Ok(symbol) => symbol,
72            Err(err) => {
73                log::trace!(
74                    "{not_found} global symbol: {name:?}",
75                    not_found = crate::mark!(NOT_FOUND_INTERIM),
76                );
77                return Err(err)?;
78            }
79        };
80        symbol.set_check();
81        log::trace!(
82            "{found} global symbol: {symbol:?}",
83            found = crate::mark!(FOUND_INTERIM),
84        );
85        Ok(symbol)
86    }
87
88    fn ambiguity_error(ambiguous: QualifiedName, others: QualifiedNames) -> ResolveError {
89        ResolveError::AmbiguousSymbol(ambiguous, others)
90    }
91}
92
93impl std::fmt::Display for SymbolTable {
94    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95        writeln!(
96            f,
97            "{}",
98            self.iter()
99                .map(|(_, symbol)| symbol)
100                .filter(|symbol| !symbol.is_deleted())
101                .map(|symbol| symbol.full_name().to_string())
102                .collect::<Vec<_>>()
103                .join(", ")
104        )
105    }
106}
107
108impl std::fmt::Debug for SymbolTable {
109    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110        writeln!(f, "{:?}", self.0)
111    }
112}