microcad_lang/resolve/symbol/
mod.rs

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