Skip to main content

code_moniker_core/lang/sdk/
resolve.rs

1use crate::core::moniker::Moniker;
2use crate::core::moniker::MonikerBuilder;
3use crate::lang::kinds;
4
5use super::model::{DefIndex, DiscoveredFile, ImportKind, ImportTable, ResolvedRef, TargetExpr};
6use super::model::{RefHints, UnresolvedRef};
7use super::scope::{Namespace, ScopeId, ScopeTree};
8
9#[derive(Clone, Debug, Eq, PartialEq)]
10pub struct Resolution {
11	pub target: Moniker,
12	pub confidence: &'static [u8],
13}
14
15pub trait LangResolverStrategy {
16	fn resolve_path(
17		&self,
18		_target: &TargetExpr,
19		_context: ResolveContext<'_>,
20	) -> Option<Resolution> {
21		None
22	}
23
24	fn resolve_builtin(
25		&self,
26		_name: &[u8],
27		_namespace: Namespace,
28		_context: ResolveContext<'_>,
29	) -> Option<Resolution> {
30		None
31	}
32
33	fn external_package_target(&self, root: &Moniker, package: &[u8], path: &[Vec<u8>]) -> Moniker {
34		let mut builder = MonikerBuilder::from_view(root.as_view());
35		builder.segment(kinds::EXTERNAL_PKG, package);
36		for segment in path {
37			builder.segment(kinds::PATH, segment);
38		}
39		builder.build()
40	}
41}
42
43#[derive(Clone, Copy)]
44pub struct ResolveContext<'a> {
45	pub root: &'a Moniker,
46	pub scope: ScopeId,
47	pub defs: &'a DefIndex,
48	pub scopes: &'a ScopeTree,
49	pub imports: &'a ImportTable,
50}
51
52pub struct LocalResolver<S> {
53	strategy: S,
54}
55
56impl<S> LocalResolver<S> {
57	pub fn new(strategy: S) -> Self {
58		Self { strategy }
59	}
60}
61
62impl<S: LangResolverStrategy> LocalResolver<S> {
63	pub fn resolve_file(
64		&self,
65		discovered: &DiscoveredFile,
66		refs: impl IntoIterator<Item = UnresolvedRef>,
67	) -> Vec<ResolvedRef> {
68		refs.into_iter()
69			.map(|reference| self.resolve_ref(discovered, reference))
70			.collect()
71	}
72
73	pub fn resolve_ref(
74		&self,
75		discovered: &DiscoveredFile,
76		reference: UnresolvedRef,
77	) -> ResolvedRef {
78		let namespace = reference.hints.namespace.unwrap_or(Namespace::Unified);
79		let context = ResolveContext {
80			root: &discovered.root,
81			scope: reference.source_scope,
82			defs: &discovered.def_index,
83			scopes: &discovered.scopes,
84			imports: &discovered.imports,
85		};
86		let resolution = match &reference.target {
87			TargetExpr::Bare(name) => self.resolve_bare(name, namespace, context),
88			TargetExpr::External { package, path } => Some(Resolution {
89				target: self
90					.strategy
91					.external_package_target(&discovered.root, package, path),
92				confidence: kinds::CONF_EXTERNAL,
93			}),
94			target => self.strategy.resolve_path(target, context),
95		}
96		.unwrap_or_else(|| Resolution {
97			target: fallback_target(&discovered.root, &reference.target),
98			confidence: kinds::CONF_UNRESOLVED,
99		});
100
101		ResolvedRef {
102			source: reference.source,
103			target: resolution.target,
104			kind: reference.kind,
105			position: reference.position,
106			confidence: resolution.confidence,
107			hints: reference.hints,
108		}
109	}
110
111	fn resolve_bare(
112		&self,
113		name: &[u8],
114		namespace: Namespace,
115		context: ResolveContext<'_>,
116	) -> Option<Resolution> {
117		context
118			.resolve_local(name, namespace)
119			.or_else(|| context.resolve_import(name, namespace))
120			.or_else(|| context.resolve_file_def(name, namespace))
121			.or_else(|| self.strategy.resolve_builtin(name, namespace, context))
122	}
123}
124
125impl ResolveContext<'_> {
126	fn resolve_local(&self, name: &[u8], namespace: Namespace) -> Option<Resolution> {
127		self.scopes
128			.resolve(self.scope, namespace, name)
129			.first()
130			.map(|target| Resolution {
131				target: target.clone(),
132				confidence: kinds::CONF_LOCAL,
133			})
134	}
135
136	fn resolve_import(&self, name: &[u8], namespace: Namespace) -> Option<Resolution> {
137		self.imports
138			.scoped(self.scope)
139			.iter()
140			.find(|import| {
141				import.namespace == namespace
142					&& matches!(import.kind, ImportKind::Symbol | ImportKind::Alias)
143					&& import.alias == name
144			})
145			.map(|import| Resolution {
146				target: import.target.clone(),
147				confidence: import.confidence,
148			})
149	}
150
151	fn resolve_file_def(&self, name: &[u8], namespace: Namespace) -> Option<Resolution> {
152		self.defs
153			.by_name(namespace, name)
154			.first()
155			.map(|target| Resolution {
156				target: target.clone(),
157				confidence: kinds::CONF_RESOLVED,
158			})
159	}
160}
161
162fn fallback_target(root: &Moniker, target: &TargetExpr) -> Moniker {
163	let mut builder = MonikerBuilder::from_view(root.as_view());
164	match target {
165		TargetExpr::Bare(name) | TargetExpr::SelfType(name) => {
166			builder.segment(kinds::PATH, name);
167		}
168		TargetExpr::Path(path) => {
169			for segment in path {
170				builder.segment(kinds::PATH, segment);
171			}
172		}
173		TargetExpr::Receiver { name, .. } => {
174			builder.segment(kinds::PATH, name);
175		}
176		TargetExpr::External { package, path } => {
177			builder.segment(kinds::EXTERNAL_PKG, package);
178			for segment in path {
179				builder.segment(kinds::PATH, segment);
180			}
181		}
182	}
183	builder.build()
184}
185
186#[allow(dead_code)]
187fn _assert_ref_hints_owned(_: RefHints) {}