microcad_lang/resolve/
symbol_map.rs

1// Copyright © 2025 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4use crate::{diag::*, resolve::*, syntax::*, value::*};
5use derive_more::{Deref, DerefMut};
6use std::collections::btree_map::BTreeMap;
7
8/// Map Id to SymbolNode reference
9#[derive(Debug, Default, Clone, Deref, DerefMut)]
10pub struct SymbolMap(BTreeMap<Identifier, Symbol>);
11
12impl From<Tuple> for SymbolMap {
13    fn from(tuple: Tuple) -> Self {
14        let mut symbol_map = SymbolMap::default();
15        for (id, value) in tuple.named.iter() {
16            symbol_map.add_node(Symbol::new_call_argument(id.clone(), value.clone()))
17        }
18        symbol_map
19    }
20}
21
22impl FromIterator<(Identifier, Value)> for SymbolMap {
23    fn from_iter<T: IntoIterator<Item = (Identifier, Value)>>(iter: T) -> Self {
24        let mut symbol_map = SymbolMap::default();
25        for (id, value) in iter {
26            symbol_map.add_node(Symbol::new_call_argument(id.clone(), value.clone()))
27        }
28        symbol_map
29    }
30}
31
32impl WriteToFile for SymbolMap {}
33
34impl SymbolMap {
35    /// Create symbol new map
36    pub fn new() -> Self {
37        Self(Default::default())
38    }
39
40    /// Insert a not by it's own id.
41    pub fn add_node(&mut self, symbol: Symbol) {
42        let id = symbol.id();
43        self.0.insert(id, symbol);
44    }
45
46    /// Insert a not by it's own id.
47    pub fn insert_node(&mut self, id: Identifier, symbol: Symbol) {
48        self.0.insert(id, symbol);
49    }
50
51    /// Search for a symbol in symbol map.
52    pub fn search(&self, name: &QualifiedName) -> ResolveResult<Symbol> {
53        if name.is_empty() {
54            if let Some(symbol) = self.get(&Identifier::none()) {
55                symbol.borrow_mut().used = true;
56                return Ok(symbol.clone());
57            }
58        } else {
59            let (id, leftover) = name.split_first();
60            if let Some(symbol) = self.get(&id) {
61                symbol.borrow_mut().used = true;
62                if leftover.is_empty() {
63                    log::trace!("Fetched {name:?} from globals (symbol map)");
64                    return Ok(symbol.clone());
65                } else if let Some(symbol) = symbol.search(&leftover) {
66                    return Ok(symbol);
67                }
68            }
69        }
70
71        Err(ResolveError::SymbolNotFound(name.clone()))
72    }
73
74    /// detach children from their parent
75    pub fn detach_from_parent(mut self) -> Self {
76        for child in self.iter_mut() {
77            child.1.borrow_mut().parent = None;
78        }
79        self
80    }
81
82    /// Print contained symbols with indention.
83    pub fn print(&self, f: &mut std::fmt::Formatter<'_>, depth: usize) -> std::fmt::Result {
84        for (id, symbol) in self.0.iter() {
85            symbol.print_symbol(f, Some(id), depth, true)?;
86        }
87
88        Ok(())
89    }
90
91    /// Move all children from another symbol into this map.
92    /// # Arguments
93    /// - `from`: Append this symbol's children
94    ///
95    /// Technically, nothing will be moved here because of the `Rc<RefCell<>>`,
96    /// but by resetting the parent of all moved  children, those will see
97    /// themselves as root symbols.
98    pub fn move_children(&mut self, from: &Symbol) {
99        // copy children
100        from.borrow().children.iter().for_each(|(id, child)| {
101            child.borrow_mut().parent = None;
102            self.insert(id.clone(), child.clone());
103        });
104    }
105
106    /// Collect all symbols engaged in that name.
107    ///
108    /// Example: `what`=`a::b::c` will return the symbols: `a`,`a::b` and `a::b::c`
109    pub fn path_to(&self, what: &QualifiedName) -> ResolveResult<Symbols> {
110        (1..(what.len() + 1))
111            .map(|n| what[0..n].iter().cloned().collect())
112            .map(|what| self.search(&what))
113            .collect()
114    }
115}
116
117impl std::fmt::Display for SymbolMap {
118    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119        for (id, symbol) in self.0.iter() {
120            symbol.print_symbol(f, Some(id), 0, true)?;
121        }
122
123        Ok(())
124    }
125}
126
127#[test]
128fn symbol_map_path_to() {
129    let mut symbols = SymbolMap::new();
130    let a = Symbol::new(SymbolDefinition::Tester("a".into()), None);
131    let b = Symbol::new(SymbolDefinition::Tester("b".into()), Some(a.clone()));
132    let c = Symbol::new(SymbolDefinition::Tester("c".into()), Some(b.clone()));
133    Symbol::add_child(&b, c);
134    Symbol::add_child(&a, b);
135    symbols.add_node(a);
136
137    let name: QualifiedName = "a::b::c".into();
138
139    log::trace!("symbols:\n{}", symbols);
140    let symbols = symbols.path_to(&name).expect("test error");
141    log::trace!("parents of {name}: {}", symbols.full_names());
142    assert_eq!(
143        symbols.full_names().to_string(),
144        "a, a::b, a::b::c".to_string(),
145    );
146}