microcad_lang/resolve/
lookup.rs

1// Copyright © 2025 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4use crate::{resolve::*, syntax::*};
5
6/// Trait to lookup symbols by *qualified name*.
7pub trait Lookup<E: std::error::Error = ResolveError> {
8    /// Search a *symbol* by it's *qualified name*.
9    /// # Arguments
10    /// - `name`: *Qualified name* to search for.
11    fn lookup(&self, name: &QualifiedName) -> Result<Symbol, E>;
12
13    /// Return an ambiguity error.
14    fn ambiguity_error(ambiguous: QualifiedName, others: QualifiedNames) -> E;
15
16    /// Search a *symbol* by it's *qualified name* **and** within the given *symbol*.
17    ///
18    /// # Arguments
19    /// - `name`: *Qualified name* to search for.
20    /// - `within`: Searches within this *symbol* too.
21    /// # Return
22    /// If both are found and one is an *alias* returns the other one.
23    fn lookup_within(&self, name: &QualifiedName, within: &Symbol) -> Result<Symbol, E> {
24        log::trace!(
25            "{lookup} for symbol '{name:?}' within '{within}'",
26            within = within.full_name(),
27            lookup = crate::mark!(LOOKUP)
28        );
29        match (self.lookup(name), within.search(name, true)) {
30            // found both
31            (Ok(global), Ok(relative)) => {
32                // check if one is an alias of the other
33                match (global.is_alias(), relative.is_alias()) {
34                    (true, false) => Ok(relative),
35                    (false, true) => Ok(global),
36                    (true, true) => unreachable!("found two aliases"),
37                    (false, false) => {
38                        if relative == global {
39                            Ok(global)
40                        } else {
41                            Err(Self::ambiguity_error(
42                                relative.full_name(),
43                                [global.full_name()].into_iter().collect(),
44                            ))
45                        }
46                    }
47                }
48            }
49            // found one
50            (Ok(symbol), Err(_)) | (Err(_), Ok(symbol)) => {
51                log::trace!(
52                    "{found} symbol '{name:?}' within '{within}'",
53                    within = within.full_name(),
54                    found = crate::mark!(FOUND_INTERIM)
55                );
56                Ok(symbol)
57            }
58            // found nothing
59            (Err(err), Err(_)) => {
60                log::trace!(
61                    "{not_found} symbol '{name:?}' within '{within}'",
62                    within = within.full_name(),
63                    not_found = crate::mark!(NOT_FOUND_INTERIM)
64                );
65                Err(err)
66            }
67        }
68    }
69
70    /// Search a *symbol* by it's *qualified name* **and** within a given *symbol*
71    ///
72    /// # Arguments
73    /// - `name`: *qualified name* to search for
74    /// - `within`: If some, searches within this *symbol* too.
75    /// # Return
76    /// If both are found and one is an *alias* returns the other one.
77    fn lookup_within_opt(
78        &self,
79        name: &QualifiedName,
80        within: &Option<Symbol>,
81    ) -> Result<Symbol, E> {
82        if let Some(within) = within {
83            self.lookup_within(name, within)
84        } else {
85            self.lookup(name)
86        }
87    }
88
89    /// Search a *symbol* by it's *qualified name* **and** within a *symbol* given by name.
90    ///
91    /// If both are found
92    /// # Arguments
93    /// - `name`: *qualified name* to search for.
94    /// - `within`: Searches in the *symbol* with this name too.
95    fn lookup_within_name(
96        &self,
97        name: &QualifiedName,
98        within: &QualifiedName,
99    ) -> Result<Symbol, E> {
100        self.lookup_within(name, &self.lookup(within)?)
101    }
102
103    /// Returns an error if name starts with `super::`.
104    fn check_super(&self, name: &QualifiedName) -> ResolveResult<()> {
105        if name.count_super() > 0 {
106            log::trace!(
107                "{not_found} '{name:?}' is not canonical",
108                not_found = crate::mark!(NOT_FOUND_INTERIM),
109            );
110            return Err(ResolveError::SymbolNotFound(name.clone()));
111        }
112        Ok(())
113    }
114}