Skip to main content

microcad_lang/symbol/
mod.rs

1// Copyright © 2025-2026 The µcad authors <info@microcad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! µcad symbol tree.
5
6mod iterators;
7mod symbol_definition;
8mod symbol_inner;
9mod symbol_map;
10mod symbols;
11
12use indexmap::IndexSet;
13use microcad_lang_base::{RcMut, SrcRef, SrcReferrer, TreeDisplay, TreeState};
14
15pub use iterators::*;
16pub use symbol_definition::*;
17pub(crate) use symbol_map::*;
18pub(crate) use symbols::*;
19
20use symbol_inner::*;
21
22use crate::{builtin::*, lower::ir, resolve::*, value::*};
23
24/// Symbol
25#[derive(Clone)]
26pub struct Symbol {
27    visibility: std::cell::RefCell<ir::Visibility>,
28    src_ref: SrcRef,
29    inner: RcMut<SymbolInner>,
30}
31
32// creation
33impl Symbol {
34    /// Create new symbol without children.
35    /// # Arguments
36    /// - `def`: Symbol definition
37    /// - `parent`: Symbol's parent symbol or none for root
38    pub(crate) fn new(def: SymbolDef, parent: Option<Symbol>) -> Self {
39        Symbol {
40            visibility: std::cell::RefCell::new(def.visibility()),
41            inner: RcMut::new(SymbolInner {
42                def,
43                parent,
44                ..Default::default()
45            }),
46            ..Default::default()
47        }
48    }
49
50    /// Create new symbol without children.
51    /// # Arguments
52    /// - `visibility`: Visibility of the symbol
53    /// - `def`: Symbol definition
54    /// - `parent`: Symbol's parent symbol or none for root
55    pub(crate) fn new_with_visibility(
56        visibility: ir::Visibility,
57        def: SymbolDef,
58        parent: Option<Symbol>,
59    ) -> Self {
60        Symbol {
61            visibility: std::cell::RefCell::new(visibility),
62            inner: RcMut::new(SymbolInner {
63                def,
64                parent,
65                ..Default::default()
66            }),
67            ..Default::default()
68        }
69    }
70
71    /// Create a symbol node for a built-in.
72    /// # Arguments
73    /// - `id`: Name of the symbol
74    /// - `parameters`: Optional parameter list
75    /// - `f`: The builtin function
76    pub(crate) fn new_builtin(builtin: impl Into<Builtin>) -> Symbol {
77        Symbol::new(SymbolDef::Builtin(builtin.into()), None)
78    }
79
80    /// New builtin function as symbol.
81    pub fn new_builtin_fn(
82        name: &'static str,
83        parameters: impl Iterator<Item = (Identifier, ParameterValue)>,
84        f: &'static BuiltinFn,
85        doc: Option<&'static str>,
86    ) -> Symbol {
87        Self::new_builtin(BuiltinFunction {
88            id: Identifier::no_ref(name),
89            parameters: parameters.collect(),
90            f,
91            doc: doc.map(ir::DocBlock::new_builtin),
92        })
93    }
94
95    /// Get fully qualified name.
96    pub fn full_name(&self) -> ir::QualifiedName {
97        let id = self.id();
98        match &self.get_parent() {
99            Some(parent) => {
100                let mut name = parent.full_name();
101                name.push(id);
102                name
103            }
104
105            None => {
106                let src_ref = id.src_ref();
107                ir::QualifiedName::new(vec![id], src_ref)
108            }
109        }
110    }
111}
112
113impl Symbol {
114    /// Return a list of unused private symbols
115    ///
116    /// Use this after eval for any useful result.
117    pub(crate) fn unused_private(&self) -> Symbols {
118        let used_in_module = &mut IndexSet::new();
119        let mut symbols: Symbols = self
120            .riter()
121            .skip(1) // skip root
122            .filter(|symbol| {
123                if let Some(in_module) = symbol.in_module()
124                    && symbol.is_used()
125                {
126                    used_in_module.insert(in_module);
127                }
128                symbol.is_unused_private()
129            })
130            .collect();
131
132        symbols.retain(|symbol| {
133            if let Some(in_module) = symbol.in_module() {
134                !used_in_module.contains(&in_module)
135            } else {
136                true
137            }
138        });
139        symbols.sort_by_key(|s| s.full_name());
140        symbols
141    }
142
143    /// Search a *symbol* by it's *qualified name* **and** within a *symbol* given by name.
144    ///
145    /// If both are found
146    /// # Arguments
147    /// - `name`: *qualified name* to search for.
148    /// - `within`: Searches in the *symbol* with this name too.
149    /// - `target`: What to search for
150    pub(crate) fn lookup_within_name(
151        &self,
152        name: &ir::QualifiedName,
153        within: &ir::QualifiedName,
154        target: LookupTarget,
155    ) -> ResolveResult<Symbol> {
156        self.lookup_within(name, &self.search(within, false)?, target)
157    }
158}
159
160// tree structure
161impl Symbol {
162    /// Get any child with the given `id`.
163    /// # Arguments
164    /// - `id`: Anticipated *id* of the possible child.
165    pub fn get_child(&self, id: &Identifier) -> Option<Symbol> {
166        self.inner.borrow().children.get(id).cloned()
167    }
168
169    /// Add a new symbol to children.
170    pub(crate) fn add_symbol(&mut self, symbol: Symbol) -> ResolveResult<()> {
171        self.insert_symbol(symbol.id(), symbol.clone())
172    }
173
174    /// Add a new symbol to children with specific id.
175    pub(super) fn insert_symbol(&mut self, id: Identifier, symbol: Symbol) -> ResolveResult<()> {
176        log::trace!("insert symbol: {id}");
177        if let Some(symbol) = self.inner.borrow_mut().children.insert(id, symbol.clone()) {
178            Err(ResolveError::SymbolAlreadyDefined(symbol.full_name()))
179        } else {
180            Ok(())
181        }
182    }
183
184    /// Insert child and change parent of child to new parent.
185    /// # Arguments
186    /// - `parent`: New parent symbol (will be changed in child!).
187    /// - `child`: Child to insert
188    pub(crate) fn add_child(parent: &Symbol, child: Symbol) {
189        child.inner.borrow_mut().parent = Some(parent.clone());
190        let id = child.id();
191        parent.inner.borrow_mut().children.insert(id, child);
192    }
193
194    /// Initially set children.
195    ///
196    /// Panics if children already exist.
197    pub(super) fn set_children(&self, new_children: SymbolMap) {
198        assert!(self.inner.borrow().children.is_empty());
199        self.inner.borrow_mut().children = new_children;
200    }
201
202    /// Try to apply a FnMut for each child.
203    pub(crate) fn try_children<E: std::error::Error>(
204        &self,
205        f: impl FnMut((&Identifier, &Symbol)) -> Result<(), E>,
206    ) -> Result<(), E> {
207        self.inner.borrow().children.iter().try_for_each(f)
208    }
209
210    /// Try to apply a FnMut for each child.
211    pub(crate) fn try_children_sorted<E: std::error::Error>(
212        &self,
213        f: impl FnMut((&Identifier, &Symbol)) -> Result<(), E>,
214    ) -> Result<(), E> {
215        let mut children = self.inner.borrow().children.clone();
216        children.sort_by(|id1, _, id2, _| id1.cmp(id2));
217        children.iter().try_for_each(f)
218    }
219
220    /// Apply a FnMut for each child.
221    pub fn with_children(&self, f: impl FnMut((&Identifier, &Symbol))) {
222        self.inner.borrow().children.iter().for_each(f)
223    }
224
225    /// Create a vector of cloned children.
226    fn public_children(&self, visibility: ir::Visibility, src_ref: SrcRef) -> SymbolMap {
227        let inner = self.inner.borrow();
228
229        inner
230            .children
231            .values()
232            .filter(|symbol| {
233                if symbol.is_public() {
234                    true
235                } else {
236                    log::trace!("Skipping private symbol:\n{symbol:?}");
237                    false
238                }
239            })
240            .map(|symbol| symbol.clone_with(visibility.clone(), src_ref))
241            .map(|symbol| (symbol.id(), symbol))
242            .collect()
243    }
244
245    /// Get parent symbol.
246    pub(crate) fn get_parent(&self) -> Option<Symbol> {
247        self.inner.borrow().parent.clone()
248    }
249
250    /// Set new parent.
251    pub(super) fn set_parent(&mut self, parent: Symbol) {
252        self.inner.borrow_mut().parent = Some(parent);
253    }
254
255    /// Return iterator over symbol's children.
256    pub fn iter(&self) -> Children {
257        Children::new(self.clone())
258    }
259
260    /// Iterate recursively
261    pub fn riter(&self) -> RecurseChildren {
262        RecurseChildren::new(self.clone())
263    }
264
265    /// Get the `SrcRef` for the kind keyword of this symbol, if any
266    pub fn kind_ref(&self) -> Option<SrcRef> {
267        self.inner.borrow().kind_ref()
268    }
269}
270
271// visibility
272impl Symbol {
273    /// Return `true` if symbol's visibility is private
274    pub fn visibility(&self) -> ir::Visibility {
275        self.visibility.borrow().clone()
276    }
277
278    /// Return `true` if symbol's visibility set to is public.
279    pub fn is_public(&self) -> bool {
280        matches!(self.visibility(), ir::Visibility::Public)
281    }
282
283    pub(super) fn is_deleted(&self) -> bool {
284        matches!(self.visibility(), ir::Visibility::Deleted)
285    }
286
287    pub(super) fn delete(&self) {
288        self.visibility.replace(ir::Visibility::Deleted);
289    }
290
291    /// Clone this symbol but give the clone another visibility.
292    pub(crate) fn clone_with(&self, visibility: ir::Visibility, src_ref: SrcRef) -> Self {
293        Self {
294            visibility: std::cell::RefCell::new(visibility),
295            src_ref,
296            inner: self.inner.clone(),
297        }
298    }
299}
300
301// definition dependent
302impl Symbol {
303    /// Return the internal *id* of this symbol.
304    pub fn id(&self) -> Identifier {
305        self.inner.borrow().def.id()
306    }
307
308    /// check if a private symbol may be declared within this symbol
309    pub(super) fn can_const(&self) -> bool {
310        matches!(
311            self.inner.borrow().def,
312            SymbolDef::Module(..) | SymbolDef::SourceFile(..) | SymbolDef::Workbench(..)
313        )
314    }
315
316    /// check if a value on the stack may be declared within this symbol
317    pub(super) fn can_value(&self) -> bool {
318        matches!(
319            self.inner.borrow().def,
320            SymbolDef::Function(..) | SymbolDef::Workbench(..) | SymbolDef::SourceFile(..)
321        )
322    }
323
324    /// check if a property may be declared within this symbol
325    pub(super) fn can_prop(&self) -> bool {
326        matches!(self.inner.borrow().def, SymbolDef::Workbench(..))
327    }
328
329    fn is_root(&self) -> bool {
330        matches!(self.inner.borrow().def, SymbolDef::Root)
331    }
332
333    pub(crate) fn is_module(&self) -> bool {
334        matches!(
335            self.inner.borrow().def,
336            SymbolDef::SourceFile(..) | SymbolDef::Module(..)
337        )
338    }
339
340    pub(crate) fn is_workbench(&self) -> bool {
341        matches!(self.inner.borrow().def, SymbolDef::Workbench(..))
342    }
343
344    /// Overwrite any value in this symbol
345    pub(crate) fn set_value(&self, new_value: Value) -> ResolveResult<()> {
346        let is_a_value = match &mut self.inner.borrow_mut().def {
347            SymbolDef::Value(.., value) => {
348                *value = new_value;
349                true
350            }
351            _ => false,
352        };
353        match is_a_value {
354            true => Ok(()),
355            false => Err(ResolveError::NotAValue(self.full_name())),
356        }
357    }
358
359    /// Return file path of top level parent source file.
360    pub(super) fn source_path(&self) -> Option<std::path::PathBuf> {
361        if let SymbolDef::SourceFile(source_file) = &self.inner.borrow().def {
362            return source_file
363                .filename()
364                .parent()
365                .map(|path| path.to_path_buf());
366        }
367        self.get_parent().and_then(|parent| parent.source_path())
368    }
369
370    pub(super) fn is_resolvable(&self) -> bool {
371        matches!(
372            self.inner.borrow().def,
373            SymbolDef::SourceFile(..)
374                | SymbolDef::Module(..)
375                | SymbolDef::Function(..)
376                | SymbolDef::Workbench(..)
377                | SymbolDef::UseAll(..)
378                | SymbolDef::Alias(..)
379        ) && !self.is_deleted()
380    }
381
382    pub(super) fn is_link(&self) -> bool {
383        matches!(
384            self.inner.borrow().def,
385            SymbolDef::UseAll(..) | SymbolDef::Alias(..)
386        )
387    }
388
389    pub(super) fn is_alias(&self) -> bool {
390        matches!(self.inner.borrow().def, SymbolDef::Alias(..))
391    }
392
393    pub(super) fn has_links(&self) -> bool {
394        if self.is_link() {
395            true
396        } else {
397            self.inner
398                .borrow()
399                .children
400                .values()
401                .filter(|symbol| !symbol.is_deleted())
402                .any(|symbol| symbol.has_links())
403        }
404    }
405
406    /// Work with the symbol definition.
407    pub fn with_def<T>(&self, mut f: impl FnMut(&SymbolDef) -> T) -> T {
408        f(&self.inner.borrow().def)
409    }
410
411    /// Work with the mutable symbol definition.
412    pub(crate) fn with_def_mut<T>(&self, mut f: impl FnMut(&mut SymbolDef) -> T) -> T {
413        f(&mut self.inner.borrow_mut().def)
414    }
415}
416
417// check
418impl Symbol {
419    pub(super) fn source_hash(&self) -> u64 {
420        self.inner.borrow().def.source_hash()
421    }
422
423    pub(crate) fn is_used(&self) -> bool {
424        self.inner.borrow().used.get().is_some()
425    }
426
427    /// Mark this symbol as *used*.
428    pub(crate) fn set_used(&self) {
429        let _ = self.inner.borrow().used.set(());
430    }
431
432    pub(crate) fn is_unused_private(&self) -> bool {
433        !self.is_used() && !self.is_public() && !self.is_deleted()
434    }
435
436    pub(crate) fn in_module(&self) -> Option<ir::QualifiedName> {
437        if let ir::Visibility::PrivateUse(module) = self.visibility() {
438            Some(module.clone())
439        } else {
440            None
441        }
442    }
443
444    /// Resolve aliases and use statements in this symbol.
445    pub(super) fn resolve(&self, context: &mut ResolveContext) -> ResolveResult<SymbolMap> {
446        log::trace!("resolving: {self}");
447
448        // retrieve symbols from any use statements
449        let mut from_self = {
450            let inner = self.inner.borrow();
451            match &inner.def {
452                SymbolDef::Alias(visibility, id, name) => {
453                    log::trace!("resolving use (as): {self} => {visibility}{id} ({name})");
454                    let symbol = context
455                        .root
456                        .lookup_within_opt(name, &inner.parent, LookupTarget::Any)?
457                        .clone_with(visibility.clone(), name.src_ref);
458                    self.delete();
459                    [(id.clone(), symbol)].into_iter().collect()
460                }
461                SymbolDef::UseAll(visibility, name) => {
462                    let visibility = &if matches!(visibility, &ir::Visibility::Private) {
463                        ir::Visibility::PrivateUse(name.clone())
464                    } else {
465                        visibility.clone()
466                    };
467                    log::trace!("resolving use all: {self} => {visibility}{name}");
468                    let symbols = context
469                        .root
470                        .lookup_within_opt(name, &inner.parent, LookupTarget::Any)?
471                        .public_children(visibility.clone(), name.src_ref);
472                    if !symbols.is_empty() {
473                        self.delete();
474                    }
475                    symbols
476                }
477                // skip others
478                _ => SymbolMap::default(),
479            }
480        };
481
482        let resolved = from_self.resolve_all(context)?;
483        from_self.extend(resolved.iter().map(|(k, v)| (k.clone(), v.clone())));
484        // collect symbols resolved from children
485        let from_children = self.inner.borrow().children.resolve_all(context)?;
486        self.inner
487            .borrow_mut()
488            .children
489            .extend(from_children.iter().map(|(k, v)| (k.clone(), v.clone())));
490        // return symbols collected from self
491        Ok(from_self)
492    }
493
494    /// Search down the symbol tree for a qualified name.
495    /// # Arguments
496    /// - `name`: Name to search for.
497    pub(crate) fn search(&self, name: &ir::QualifiedName, respect: bool) -> ResolveResult<Symbol> {
498        log::trace!("Searching {name} in {:?}", self.full_name());
499        if let Some(id) = name.first() {
500            if id.is_super() {
501                if let Some(parent) = self.get_parent() {
502                    return parent.search(&name[1..].iter().cloned().collect(), respect);
503                }
504            }
505        }
506        self.search_inner(name, true, respect)
507    }
508
509    fn search_inner(
510        &self,
511        name: &ir::QualifiedName,
512        top_level: bool,
513        respect: bool,
514    ) -> ResolveResult<Symbol> {
515        use crate::lower::SingleIdentifier;
516
517        if let Some(first) = name.first() {
518            if let Some(child) = self.get_child(first) {
519                if respect && !top_level && !child.is_public() {
520                    log::trace!("Symbol {:?} is private", child.full_name());
521                    Err(ResolveError::SymbolIsPrivate(child.full_name().clone()))
522                } else if name.is_single_identifier() && !child.is_deleted() {
523                    log::trace!("Found {name:?} in {:?}", self.full_name());
524                    self.set_used();
525                    Ok(child.clone())
526                } else {
527                    let name = &name.remove_first();
528                    child.search_inner(name, false, respect)
529                }
530            } else {
531                log::trace!("No child in {:?} while searching for {name:?}", self.id());
532                Err(ResolveError::SymbolNotFound(name.clone()))
533            }
534        } else {
535            log::warn!("Cannot search for an anonymous name");
536            Err(ResolveError::SymbolNotFound(name.clone()))
537        }
538    }
539
540    /// Print out symbols from that point.
541    /// # Arguments
542    /// - `f`: Output formatter
543    /// - `id`: Overwrite symbol's internal `id` with this one if given (e.g. when using in a map).
544    /// - `state`: TreeState
545    pub(super) fn print_symbol(
546        &self,
547        f: &mut impl std::fmt::Write,
548        id: Option<&ir::Identifier>,
549        state: TreeState,
550        children: bool,
551    ) -> std::fmt::Result {
552        let self_id = &self.id();
553        let id = id.unwrap_or(self_id);
554        let def = &self.inner.borrow().def;
555        let full_name = self.full_name();
556        let visibility = self.visibility();
557        let hash = self.source_hash();
558        let depth = state.depth;
559        if state.debug {
560            if self.is_used() {
561                write!(
562                    f,
563                    "{:depth$}{visibility:?}{id:?} {def:?} [{full_name:?}] #{hash:#x}",
564                    "",
565                )?;
566            } else {
567                color_print::cwrite!(
568                    f,
569                    "{:depth$}<#606060>{visibility:?}{id:?} {def:?} [{full_name:?}] #{hash:#x}</>",
570                    "",
571                )?;
572            }
573        } else {
574            write!(f, "{:depth$}{id} {def} [{full_name}]", "",)?;
575        }
576        if children {
577            writeln!(f)?;
578            if state.debug {
579                self.try_children(|(id, child)| {
580                    child.print_symbol(f, Some(id), state.indented(), true)
581                })?;
582            } else {
583                self.try_children_sorted(|(id, child)| {
584                    child.print_symbol(f, Some(id), state.indented(), true)
585                })?;
586            }
587        }
588        Ok(())
589    }
590
591    pub(super) fn set_src_ref(&mut self, src_ref: SrcRef) {
592        self.src_ref = src_ref;
593    }
594}
595
596impl SrcReferrer for Symbol {
597    fn src_ref(&self) -> SrcRef {
598        if self.src_ref.is_none() {
599            self.inner.borrow().src_ref()
600        } else {
601            self.src_ref
602        }
603    }
604}
605
606impl Default for Symbol {
607    fn default() -> Self {
608        Self {
609            src_ref: SrcRef::none(),
610            visibility: std::cell::RefCell::new(ir::Visibility::default()),
611            inner: RcMut::new(Default::default()),
612        }
613    }
614}
615
616impl PartialEq for Symbol {
617    fn eq(&self, other: &Self) -> bool {
618        // just compare the pointers - not the content
619        self.inner.as_ptr() == other.inner.as_ptr()
620    }
621}
622
623impl std::fmt::Display for Symbol {
624    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
625        self.print_symbol(f, None, TreeState::new_display(), false)
626    }
627}
628
629impl std::fmt::Debug for Symbol {
630    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
631        self.tree_print(f, TreeState::new_debug(0))
632    }
633}
634
635impl TreeDisplay for Symbol {
636    fn tree_print(&self, f: &mut std::fmt::Formatter, state: TreeState) -> std::fmt::Result {
637        if self.is_root() {
638            if state.debug {
639                self.try_children(|(_, symbol)| symbol.tree_print(f, state))
640            } else {
641                self.try_children_sorted(|(_, symbol)| symbol.tree_print(f, state))
642            }
643        } else {
644            self.print_symbol(f, Some(&self.id()), state, true)
645        }
646    }
647}
648
649impl Lookup for Symbol {
650    /// Lookup a symbol from global symbols.
651    fn lookup(&self, name: &ir::QualifiedName, target: LookupTarget) -> ResolveResult<Symbol> {
652        log::trace!(
653            "{lookup} for global symbol '{name:?}'",
654            lookup = microcad_lang_base::mark!(LOOKUP)
655        );
656
657        let symbol = match self.search(name, true) {
658            Ok(symbol) => {
659                if target.matches(&symbol) {
660                    symbol
661                } else {
662                    log::trace!(
663                        "{not_found} global symbol: {name:?}",
664                        not_found = microcad_lang_base::mark!(NOT_FOUND),
665                    );
666                    return Err(ResolveError::WrongTarget);
667                }
668            }
669            Err(err) => {
670                log::trace!(
671                    "{not_found} global symbol: {name:?}",
672                    not_found = microcad_lang_base::mark!(NOT_FOUND),
673                );
674                return Err(err)?;
675            }
676        };
677        log::trace!(
678            "{found} global symbol: {symbol:?}",
679            found = microcad_lang_base::mark!(FOUND),
680        );
681        Ok(symbol)
682    }
683
684    fn ambiguity_error(ambiguous: ir::QualifiedName, others: ir::QualifiedNames) -> ResolveError {
685        ResolveError::AmbiguousSymbol(ambiguous, others)
686    }
687}