code_moniker_core/lang/sdk/
resolve.rs1use 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) {}