1use crate::{
5 resolve::*,
6 symbol::{Symbol, SymbolDef},
7 syntax::*,
8};
9
10#[derive(Clone, Copy)]
12pub enum LookupTarget {
13 Any,
15
16 AnyButMethod,
18
19 Method,
21 Function,
23 Module,
25 Value,
27 Link,
29}
30
31impl LookupTarget {
32 pub(crate) fn matches(&self, symbol: &Symbol) -> bool {
33 symbol.with_def(|def| -> bool {
34 match &def {
35 SymbolDef::Root => unreachable!("<ROOT> cannot be matched"),
36 SymbolDef::SourceFile(..) | SymbolDef::Module(..) => {
37 matches!(self, Self::Any | Self::AnyButMethod | Self::Module)
38 }
39 SymbolDef::Workbench(wd) => match *wd.kind {
40 WorkbenchKind::Part | WorkbenchKind::Sketch => {
41 matches!(self, Self::Any | Self::AnyButMethod | Self::Function)
42 }
43 WorkbenchKind::Operation => matches!(self, Self::Any | Self::Method),
44 },
45 SymbolDef::Function(..) => {
46 matches!(self, Self::Any | Self::AnyButMethod | Self::Function)
47 }
48 SymbolDef::Builtin(b) => match b {
49 crate::builtin::Builtin::Function(..) => {
50 matches!(self, Self::Any | Self::AnyButMethod | Self::Function)
51 }
52 crate::builtin::Builtin::Workbench(bwk) => match bwk.kind {
53 crate::builtin::BuiltinWorkbenchKind::Primitive2D
54 | crate::builtin::BuiltinWorkbenchKind::Primitive3D => {
55 matches!(self, Self::Any | Self::AnyButMethod | Self::Function)
56 }
57 crate::builtin::BuiltinWorkbenchKind::Transform
58 | crate::builtin::BuiltinWorkbenchKind::Operation => {
59 matches!(self, Self::Any | Self::Method)
60 }
61 },
62 &crate::builtin::Builtin::Constant(_) => {
63 matches!(self, Self::Any | Self::AnyButMethod | Self::Value)
64 }
65 },
66 SymbolDef::Value(..) | SymbolDef::Assignment(..) => {
67 matches!(self, Self::Any | Self::AnyButMethod | Self::Value)
68 }
69 SymbolDef::Alias(..) | SymbolDef::UseAll(..) => {
70 matches!(self, Self::Any | Self::AnyButMethod | Self::Link)
71 }
72 #[cfg(test)]
73 SymbolDef::Tester(..) => todo!(),
74 }
75 })
76 }
77}
78
79impl std::fmt::Display for LookupTarget {
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81 match self {
82 LookupTarget::Any => write!(f, "any symbol"),
83 LookupTarget::AnyButMethod => write!(f, "any symbol but a method"),
84 LookupTarget::Method => write!(f, "method"),
85 LookupTarget::Function => write!(f, "function"),
86 LookupTarget::Module => write!(f, "module"),
87 LookupTarget::Value => write!(f, "value"),
88 LookupTarget::Link => write!(f, "link"),
89 }
90 }
91}
92
93pub trait Lookup<E: std::error::Error = ResolveError> {
95 fn lookup(&self, name: &QualifiedName, target: LookupTarget) -> Result<Symbol, E>;
100
101 fn ambiguity_error(ambiguous: QualifiedName, others: QualifiedNames) -> E;
103
104 fn lookup_within(
113 &self,
114 name: &QualifiedName,
115 within: &Symbol,
116 target: LookupTarget,
117 ) -> Result<Symbol, E> {
118 log::trace!(
119 "{lookup} for symbol '{name:?}' within '{within}'",
120 within = within.full_name(),
121 lookup = microcad_lang_base::mark!(LOOKUP)
122 );
123 match (self.lookup(name, target), within.search(name, true)) {
124 (Ok(global), Ok(relative)) => {
126 match (global.is_alias(), relative.is_alias()) {
128 (true, false) => Ok(relative),
129 (false, true) => Ok(global),
130 (true, true) => unreachable!("found two aliases"),
131 (false, false) => {
132 if relative == global {
133 Ok(global)
134 } else {
135 Err(Self::ambiguity_error(
136 relative.full_name(),
137 [global.full_name()].into_iter().collect(),
138 ))
139 }
140 }
141 }
142 }
143 (Ok(symbol), Err(_)) | (Err(_), Ok(symbol)) => {
145 log::trace!(
146 "{found} symbol '{name:?}' within '{within}'",
147 within = within.full_name(),
148 found = microcad_lang_base::mark!(FOUND)
149 );
150 Ok(symbol)
151 }
152 (Err(err), Err(_)) => {
154 log::trace!(
155 "{not_found} symbol '{name:?}' within '{within}'",
156 within = within.full_name(),
157 not_found = microcad_lang_base::mark!(NOT_FOUND)
158 );
159 Err(err)
160 }
161 }
162 }
163
164 fn lookup_within_opt(
173 &self,
174 name: &QualifiedName,
175 within: &Option<Symbol>,
176 target: LookupTarget,
177 ) -> Result<Symbol, E> {
178 if let Some(within) = within {
179 self.lookup_within(name, within, target)
180 } else {
181 self.lookup(name, target)
182 }
183 }
184
185 fn deny_super(&self, name: &QualifiedName) -> ResolveResult<()> {
187 if name.count_super() > 0 {
188 log::trace!(
189 "{not_found} '{name:?}' is not canonical",
190 not_found = microcad_lang_base::mark!(NOT_FOUND),
191 );
192 return Err(ResolveError::SymbolNotFound(name.clone()));
193 }
194 Ok(())
195 }
196}