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