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}