1use crate::{CheckRef, Checker, impls::solving::is_solvable_var, split::SplitStrategy};
2use dashmap::DashSet;
3use flams_math_archives::backend::{AnyBackend, LocalBackend};
4use ftml_ontology::{
5 domain::{
6 HasDeclarations, SharedDeclaration,
7 declarations::{IsDeclaration, SharedSymbolLike, symbols::Symbol},
8 modules::{Module, ModuleLike},
9 },
10 narrative::{SharedDocumentElement, documents::Document, elements::VariableDeclaration},
11 terms::{ComponentVar, Term, Variable},
12};
13use ftml_uris::{DocumentElementUri, IsDomainUri, IsNarrativeUri, LeafUri, ModuleUri, SymbolUri};
14use std::hint::unreachable_unchecked;
15
16pub fn get_variable(
17 backend: &AnyBackend,
18 documents: &DashSet<Document, rustc_hash::FxBuildHasher>,
19 current: &[LeafUri],
20 uri: &DocumentElementUri,
21 prepare: impl Fn(Term) -> Term,
22) -> Result<SharedDocumentElement<VariableDeclaration>, ()> {
23 fn get(
24 backend: &AnyBackend,
25 documents: &DashSet<Document, rustc_hash::FxBuildHasher>,
26 uri: &DocumentElementUri,
27 ) -> Result<SharedDocumentElement<VariableDeclaration>, ()> {
28 let doc = uri.document_uri();
29 if let Some(d) = documents.get(doc) {
30 return d.get_as(uri.name()).ok_or(());
31 }
32 let doc = backend.get_document(doc).map_err(|_| ())?;
33 documents.insert(doc.clone());
34 doc.get_as(uri.name()).ok_or(())
35 }
36
37 if current.iter().any(|u| u == uri) {
38 return Err(());
39 }
40 let d = get(backend, documents, uri)?;
41
42 if let Some(tp) = d.data.tp.get_parsed()
43 && !d.data.tp.has_checked()
44 {
45 let tp = prepare(tp.clone());
46
47 if d.data.is_seq && tp.as_sequence_type().is_none() {
48 if d.data.sequence_range.is_empty() {
49 d.data.tp.set_checked(tp.into_seq_type());
50 } else {
51 d.data
52 .tp
53 .set_checked(tp.into_ranged_seq_type(d.data.sequence_range.iter().cloned()));
54 }
55 } else {
56 d.data.tp.set_checked(tp);
57 }
58 }
59 if let Some(df) = d.data.df.get_parsed()
60 && !d.data.df.has_checked()
61 {
62 d.data.df.set_checked(prepare(df.clone()));
63 }
64
65 Ok(d)
66}
67
68impl<Split: SplitStrategy> Checker<Split> {
69 pub(crate) fn get_module_like(&self, uri: &ModuleUri) -> Result<ModuleLike, ()> {
70 if uri.is_top() {
71 if let Some(m) = self.modules.get(uri) {
72 return Ok(ModuleLike::Module(m.clone()));
73 }
74 let ModuleLike::Module(m) = self.backend.get_module(uri).map_err(|_| ())? else {
75 unsafe { unreachable_unchecked() }
77 };
78 m.initialize(&mut |u| self.get_module_like(u).ok())
79 .map_err(|_| ())?;
80 self.modules.insert(m.clone());
81 Ok(ModuleLike::Module(m))
82 } else {
83 let inner = unsafe { uri.clone().into_top_symbol().unwrap_unchecked() };
85 let m = self.get_module(inner.module_uri())?;
86 m.as_module_like(inner.name()).ok_or(())
87 }
88 }
89 pub(crate) fn get_module(&self, uri: &ModuleUri) -> Result<Module, ()> {
90 if uri.is_top() {
91 if let Some(m) = self.modules.get(uri) {
92 return Ok(m.clone());
93 }
94 let ModuleLike::Module(m) = self.backend.get_module(uri).map_err(|_| ())? else {
95 unsafe { unreachable_unchecked() }
97 };
98 self.modules.insert(m.clone());
99 Ok(m)
100 } else {
101 let uri = !uri.clone();
102 if let Some(m) = self.modules.get(&uri) {
103 return Ok(m.clone());
104 }
105 let ModuleLike::Module(m) = self.backend.get_module(&uri).map_err(|_| ())? else {
106 unsafe { unreachable_unchecked() }
108 };
109 self.modules.insert(m.clone());
110 Ok(m)
111 }
112 }
113 pub fn get_symbol_like(
114 &self,
115 uri: &SymbolUri,
116 prepare: impl Fn(Term) -> Term,
117 ) -> Result<SharedSymbolLike, ()> {
118 if self.current.iter().any(|u| u == uri) {
119 return Err(());
120 }
121 let uri = uri.as_simple_module();
122
123 let Some(d) = self
124 .get_module(&uri.module)
125 .map_err(|_| ())?
126 .get_symbol_like(uri.name())
127 else {
128 return Err(());
129 };
130 if let SharedSymbolLike::Symbol(d) = &d {
131 if let Some(tp) = d.data.tp.get_parsed()
132 && !d.data.tp.has_checked()
133 {
134 d.data.tp.set_checked(prepare(tp.clone()));
135 }
136 if let Some(df) = d.data.df.get_parsed()
137 && !d.data.df.has_checked()
138 {
139 d.data.df.set_checked(prepare(df.clone()));
140 }
141 }
142 Ok(d)
143 }
144
145 pub(crate) fn get_symbol(
146 &self,
147 uri: &SymbolUri,
148 prepare: impl Fn(Term) -> Term,
149 ) -> Result<SharedDeclaration<Symbol>, ()> {
150 if self.current.iter().any(|u| u == uri) {
151 return Err(());
152 }
153 let uri = uri.as_simple_module();
154 let Some(d) = self.get_module(&uri.module)?.get_as::<Symbol>(uri.name()) else {
155 return Err(());
156 };
157 if let Some(tp) = d.data.tp.get_parsed()
158 && !d.data.tp.has_checked()
159 {
160 d.data.tp.set_checked(prepare(tp.clone()));
161 }
162 if let Some(df) = d.data.df.get_parsed()
163 && !d.data.df.has_checked()
164 {
165 d.data.df.set_checked(prepare(df.clone()));
166 }
167 Ok(d)
168 }
170
171 pub(crate) fn get_variable(
172 &self,
173 uri: &DocumentElementUri,
174 ) -> Result<SharedDocumentElement<VariableDeclaration>, ()> {
175 get_variable(&self.backend, &self.documents, &self.current, uri, |t| {
176 self.prepare(None, t).1
177 })
178 }
216}
217
218impl<Split: SplitStrategy> CheckRef<'_, '_, Split> {
219 pub(crate) fn get_declaration<T: IsDeclaration>(
221 &self,
222 uri: &SymbolUri,
223 ) -> Result<SharedDeclaration<T>, ()> {
224 self.top
225 .get_module(&uri.module)?
226 .get_as::<T>(uri.name())
227 .ok_or(())
228 }
229
230 #[inline]
232 pub(crate) fn get_symbol(&self, uri: &SymbolUri) -> Result<SharedDeclaration<Symbol>, ()> {
233 self.top.get_symbol(uri, |t| self.prepare(t, None).1)
234 }
235
236 pub(crate) fn get_symbol_type(&mut self, uri: &SymbolUri) -> Option<Term> {
237 let Ok(s) = self.top.get_symbol_like(uri, |t| self.prepare(t, None).1) else {
238 self.failure(format!("Symbol not found: {uri}"));
239 return None;
240 };
241 match s {
242 SharedSymbolLike::Symbol(s) => s.data.tp.checked_or_parsed().map(|(t, _)| t),
243 SharedSymbolLike::MathStructure(_) => {
244 self.top.rules.inference().iter().find_map(|rl| {
245 rl.as_any()
246 .downcast_ref::<super::records::RecordUniverse>()
247 .map(|rl| rl.0.clone())
248 })
249 }
250 _ => None,
251 }
252
253 }
261 pub(crate) fn get_symbol_definiens(&mut self, uri: &SymbolUri) -> Option<Term> {
262 let Ok(s) = self.get_symbol(uri) else {
263 self.failure(format!("Symbol not found: {uri}"));
264 return None;
265 };
266
267 let t = s.data.df.checked_or_parsed();
282 if t.is_none() {
283 self.comment(format!("Symbol has no definiens: {uri}"));
284 }
285 t.map(|(t, _)| t)
286 }
287
288 #[inline]
290 pub fn get_variable(
291 &self,
292 uri: &DocumentElementUri,
293 ) -> Result<SharedDocumentElement<VariableDeclaration>, ()> {
294 self.top.get_variable(uri)
295 }
296
297 pub(crate) fn get_var_definiens(&mut self, var: &Variable) -> Option<Term> {
298 if let Some(id) = is_solvable_var(var) {
299 return self.get_solution(id);
300 }
301 for v in self.iter_context() {
302 match (v, var) {
303 (
304 ComponentVar {
305 var: Variable::Name { name, .. },
306 df,
307 ..
308 },
309 Variable::Name { name: n2, .. },
310 ) if *name == *n2 => {
311 return df.clone().map(|t| self.subst(t));
312 }
313 (
314 ComponentVar {
315 var: Variable::Name { name, .. },
316 df,
317 ..
318 },
319 Variable::Ref { declaration, .. },
320 ) if name.as_ref() == declaration.name().last() && df.is_some() => {
321 return df.clone().map(|t| self.subst(t));
322 }
323 (
324 ComponentVar {
325 var: Variable::Ref { declaration, .. },
326 df,
327 ..
328 },
329 Variable::Name { name, .. },
330 ) if name.as_ref() == declaration.name().last() => {
331 return if df.is_some() {
332 df.clone().map(|t| self.subst(t))
333 } else {
334 self.get_variable(declaration)
335 .ok()?
336 .data
337 .df
338 .checked_or_parsed()
339 .map(|(t, _)| t)
340 };
341 }
342 (
343 ComponentVar {
344 var: Variable::Ref { declaration, .. },
345 df,
346 ..
347 },
348 Variable::Ref {
349 declaration: d2, ..
350 },
351 ) if *declaration == *d2 => {
352 return if df.is_some() {
353 df.clone().map(|t| self.subst(t))
354 } else {
355 self.get_variable(declaration)
356 .ok()?
357 .data
358 .df
359 .checked_or_parsed()
360 .map(|(t, _)| t)
361 };
362 }
363 _ => (),
364 }
365 }
366 if let Variable::Ref { declaration, .. } = var {
367 self.get_variable(declaration)
368 .ok()?
369 .data
370 .df
371 .checked_or_parsed()
372 .map(|(t, _)| t)
373 } else {
374 None
375 }
376 }
377}