1use crate::{
2 CheckRef,
3 impls::solving::TermExtSolvable,
4 rules::{
5 CheckingRule, InferenceRule, InhabitableRule, MarkerRule, PreparationRule,
6 SimplificationRule, SizedSolverRule, SubtypeRule, UniverseRule,
7 operators::numbers::{NumberRule, NumberType},
8 },
9 split::SplitStrategy,
10};
11use ftml_ontology::terms::{
12 ApplicationTerm, Argument, BindingTerm, BoundArgument, ComponentVar, MaybeSequence, Term,
13 Variable, sequences::SequenceType,
14};
15use ftml_solver_trace::traceref;
16use ftml_uris::SymbolUri;
17use smallvec::SmallVec;
18use std::{borrow::Cow, hint::unreachable_unchecked};
19
20#[allow(unpredictable_function_pointer_comparisons)]
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct PiExtensionRule<Split: SplitStrategy> {
23 pub extension: SymbolUri,
24 pub pi: SymbolUri,
25 pub applicable: fn(&Self, &Term, &Argument) -> bool,
26 pub infer: for<'t> fn(
27 &Self,
28 &super::pi::PiInferenceRule,
29 &mut CheckRef<'t, '_, Split>,
30 &Term,
31 &'t [Argument],
32 &mut usize,
33 ) -> Option<Term>,
34}
35impl<Split: SplitStrategy> SizedSolverRule for PiExtensionRule<Split> {
36 fn display(&self) -> Vec<crate::trace::Displayable> {
37 ftml_solver_trace::trace!(&self.extension, "is extension of", &self.pi)
38 }
39}
40impl<Split: SplitStrategy> MarkerRule<Split> for PiExtensionRule<Split> {}
41
42macro_rules! ret_i {
43 ($(;)?) => {};
44 (& $e:expr; $($tk:tt)*) => {
45 if !$e {
46 return None;
47 }
48 ret_i!($($tk)*)
49 };
50 ($p:pat = $e:expr; $($tk:tt)*) => {
51 let $p = $e else {
52 return None;
53 };
54 ret_i!($($tk)*)
55 };
56
57}
58macro_rules! ret {
59 ( $($tk:tt)* ) => {
60 ret_i!($($tk)*;)
61 };
62}
63
64pub(crate) fn destruct_binder<'t>(
65 t: &'t Term,
66 head: &SymbolUri,
67) -> Option<(either::Either<&'t ComponentVar, &'t ComponentVar>, &'t Term)> {
68 ret!(
69 Term::Bound(b) = t;
70 & b.arguments.len() == 2;
71 & matches!(&b.head,Term::Symbol { uri, .. } if *uri == *head);
72 Some(BoundArgument::Simple(body)) = b.arguments.get(1);
74 );
75 let v = if let Some(BoundArgument::Bound(v)) = b.arguments.first() {
76 either::Left(v)
77 } else if let Some(BoundArgument::BoundSeq(MaybeSequence::One(s))) = b.arguments.first() {
78 either::Right(s)
79 } else {
80 return None;
81 };
82 Some((v, body))
83}
84fn construct_binder(var: ComponentVar, body: Term, head: &SymbolUri) -> Term {
85 Term::Bound(BindingTerm::new(
86 Term::Symbol {
87 uri: head.clone(),
88 presentation: None,
89 },
90 Box::new([BoundArgument::Bound(var), BoundArgument::Simple(body)]),
91 None,
92 ))
93}
94
95#[derive(Debug, Clone, PartialEq, Eq)]
96pub struct ArrowRule {
97 pub arrow: SymbolUri,
98 pub pi: SymbolUri,
99}
100impl SizedSolverRule for ArrowRule {
101 fn display(&self) -> Vec<crate::trace::Displayable> {
102 ftml_solver_trace::trace!(&self.arrow, " is simple version of ", &self.pi)
103 }
104 fn priority(&self) -> isize {
105 100_000_000 }
107}
108impl ArrowRule {
109 fn go<Split: SplitStrategy>(
110 &self,
111 t: &Term,
112 mut path: Option<(&mut smallvec::SmallVec<u8, 16>, usize)>,
113 ) -> Option<Term> {
114 let Term::Application(app) = t else {
115 return None;
116 };
117 let Some(Argument::Simple(ret)) = app.arguments.last() else {
118 return None;
119 };
120 let args = &app.arguments[..app.arguments.len() - 1];
121 if args.is_empty() {
122 return Some(ret.clone());
123 }
124 let mut count = 0u16;
125 let args = args.iter().map(|a| match a {
126 Argument::Simple(t) => {
127 if let Some((v, i)) = path.as_mut()
128 && v.get(*i).copied() == Some(count as u8)
129 {
130 v.insert(*i + 1, 0);
131 }
132 let (v, idx) = ret.fresh_variable(&crate::DUMMY, Some(count));
133 if let Some(idx) = idx {
134 count = idx + 1;
135 } else {
136 count += 1;
137 }
138 BoundArgument::Bound(ComponentVar {
139 var: v,
140 tp: Some(t.clone()),
141 df: None,
142 })
143 }
144 Argument::Sequence(MaybeSequence::One(t)) => {
145 if let Some((v, i)) = path.as_mut()
146 && v.get(*i).copied() == Some(count as u8)
147 {
148 v.insert(*i + 1, 0);
149 }
150 let (v, idx) = ret.fresh_variable(&crate::DUMMY, Some(count));
151 if let Some(idx) = idx {
152 count = idx + 1;
153 } else {
154 count += 1;
155 }
156 BoundArgument::BoundSeq(MaybeSequence::One(ComponentVar {
157 var: v,
158 tp: Some(t.clone()),
159 df: None,
160 }))
161 }
162 Argument::Sequence(MaybeSequence::Seq(ts)) => {
163 if let Some((v, i)) = path.as_mut()
164 && v.get(*i).copied() == Some(count as u8)
165 {
166 v.insert(*i + 2, 0);
167 }
168 BoundArgument::BoundSeq(MaybeSequence::Seq(
169 ts.iter()
170 .map(|t| {
171 let (v, idx) = ret.fresh_variable(&crate::DUMMY, Some(count));
172 if let Some(idx) = idx {
173 count = idx + 1;
174 } else {
175 count += 1;
176 }
177 ComponentVar {
178 var: v,
179 tp: Some(t.clone()),
180 df: None,
181 }
182 })
183 .collect(),
184 ))
185 }
186 });
187 let ret = Term::Bound(BindingTerm::new(
188 Term::Symbol {
189 uri: self.pi.clone(),
190 presentation: None,
191 },
192 args.chain([BoundArgument::Simple(ret.clone())]).collect(),
193 None,
194 ));
195 Some(ret)
196 }
197}
198impl<Split: SplitStrategy> SimplificationRule<Split> for ArrowRule {
199 fn applicable(&self, term: &Term) -> bool {
200 if let Term::Application(app) = term {
201 matches!(&app.head,Term::Symbol{uri,..} if *uri == self.arrow)
202 } else {
203 false
204 }
205 }
206 fn apply<'t>(
207 &self,
208 _: CheckRef<'t, '_, Split>,
209 t: &'t Term,
210 ) -> Result<Term, Option<ftml_ontology::terms::termpaths::TermPath>> {
211 self.go::<Split>(t, None).ok_or(None)
212 }
213}
214impl<Split: SplitStrategy> PreparationRule<Split> for ArrowRule {
215 fn applicable(&self, _: &CheckRef<'_, '_, Split>, t: &Term) -> bool {
216 <Self as SimplificationRule<Split>>::applicable(self, t)
217 }
218 fn applicable_revert(&self, _: &CheckRef<'_, '_, Split>, t: &Term) -> bool {
219 if let Term::Bound(bind) = t {
220 matches!(&bind.head,Term::Symbol { uri, .. } if *uri == self.pi)
221 } else {
222 false
223 }
224 }
225
226 #[allow(clippy::cast_possible_truncation)]
227 fn apply(
228 &self,
229 _: &mut CheckRef<'_, '_, Split>,
230 t: Term,
231 path: Option<(&mut smallvec::SmallVec<u8, 16>, usize)>,
232 ) -> std::ops::ControlFlow<Term, Term> {
233 std::ops::ControlFlow::Continue(self.go::<Split>(&t, path).unwrap_or(t))
234 }
235
236 fn revert(&self, _: &CheckRef<'_, '_, Split>, t: Term) -> std::ops::ControlFlow<Term, Term> {
237 let Term::Bound(app) = t else {
238 return std::ops::ControlFlow::Continue(t);
239 };
240 let Some(BoundArgument::Simple(ret)) = app.arguments.last() else {
241 return std::ops::ControlFlow::Continue(Term::Bound(app));
242 };
243 let args = &app.arguments[..app.arguments.len() - 1];
244 let mut free_vars = args.iter().map(|a| a.free_variables()).collect::<Vec<_>>();
245 free_vars.push(ret.free_variables());
246
247 let args: Result<Vec<Argument>, ()> = args
248 .iter()
249 .enumerate()
250 .map(|(i, a)| match a {
251 BoundArgument::Bound(ComponentVar {
252 var,
253 tp: Some(tp),
254 df: None,
255 }) if !free_vars[i + 1..]
256 .iter()
257 .flatten()
258 .any(|v| v.name() == var.name()) =>
259 {
260 Ok(Argument::Simple(tp.clone()))
261 }
262 BoundArgument::BoundSeq(MaybeSequence::One(ComponentVar {
263 var,
264 tp: Some(tp),
265 df: None,
266 })) if !free_vars[i + 1..]
267 .iter()
268 .flatten()
269 .any(|v| v.name() == var.name()) =>
270 {
271 Ok(Argument::Sequence(MaybeSequence::One(tp.clone())))
272 }
273 BoundArgument::BoundSeq(MaybeSequence::Seq(vs)) => {
274 let seq: Result<Box<[Term]>, ()> = vs
275 .iter()
276 .map(|v| {
277 if let ComponentVar {
278 var,
279 tp: Some(tp),
280 df: None,
281 } = v
282 && !free_vars[i + 1..]
283 .iter()
284 .flatten()
285 .any(|v| v.name() == var.name())
286 {
287 Ok(tp.clone())
288 } else {
289 Err(())
290 }
291 })
292 .collect();
293 Ok(Argument::Sequence(MaybeSequence::Seq(seq?)))
294 }
295 _ => Err(()),
296 })
297 .collect();
298 drop(free_vars);
299 std::ops::ControlFlow::Continue(match args {
300 Err(()) => Term::Bound(app),
301 Ok(mut args) => {
302 args.push(Argument::Simple(ret.clone()));
303 Term::Application(ApplicationTerm::new(
304 Term::Symbol {
305 uri: self.arrow.clone(),
306 presentation: None,
307 },
308 args.into_boxed_slice(),
309 None,
310 ))
311 }
312 })
313 }
314}
315
316#[derive(Debug, Clone, PartialEq, Eq)]
317pub struct NeedsTypeRule(pub SymbolUri);
318impl SizedSolverRule for NeedsTypeRule {
319 fn display(&self) -> Vec<ftml_solver_trace::Displayable> {
320 ftml_solver_trace::trace!(&self.0, "needs typed variables")
321 }
322}
323impl<Split: SplitStrategy> PreparationRule<Split> for NeedsTypeRule {
324 fn applicable(&self, _: &CheckRef<'_, '_, Split>, t: &Term) -> bool {
325 if let Term::Bound(b) = t
326 && let Term::Symbol { uri, .. } = &b.head
327 && *uri == self.0
328 {
329 b.arguments.iter().any(|a| matches!(a,BoundArgument::Bound(ComponentVar { tp:None, .. })|BoundArgument::BoundSeq(MaybeSequence::One(ComponentVar { tp:None, .. })))
330 || matches!(a,BoundArgument::BoundSeq(MaybeSequence::Seq(seq)) if seq.iter().any(|a| matches!(a,ComponentVar { tp:None, .. }))))
331 } else {
332 false
333 }
334 }
335 fn applicable_revert(&self, _: &CheckRef<'_, '_, Split>, _: &Term) -> bool {
336 false
337 }
338 fn apply(
339 &self,
340 checker: &mut CheckRef<'_, '_, Split>,
341 t: Term,
342 path: Option<(&mut smallvec::SmallVec<u8, 16>, usize)>,
343 ) -> std::ops::ControlFlow<Term, Term> {
344 let Term::Bound(b) = t else {
345 return std::ops::ControlFlow::Continue(t);
346 };
347 let arguments = &b.arguments;
348 let ret = checker.scoped(|checker| {
349 let mut changed = false;
350 let nargs = arguments
351 .iter()
352 .map(|a| match a {
353 BoundArgument::Bound(ComponentVar { var, tp: None, df }) => {
354 if checker.infer_var_type(var).is_none() {
355 changed = true;
356 let v = checker.new_solvable();
357 Cow::Owned(BoundArgument::Bound(ComponentVar {
358 var: var.clone(),
359 tp: Some(v),
360 df: df.clone(),
361 }))
362 } else {
363 Cow::Borrowed(a)
364 }
365 }
366 BoundArgument::BoundSeq(MaybeSequence::One(ComponentVar {
367 var,
368 tp: None,
369 df,
370 })) => {
371 if checker.infer_var_type(var).is_none() {
372 changed = true;
373 let v = checker.new_solvable();
374 Cow::Owned(BoundArgument::BoundSeq(MaybeSequence::One(ComponentVar {
375 var: var.clone(),
376 tp: Some(v),
377 df: df.clone(),
378 })))
379 } else {
380 Cow::Borrowed(a)
381 }
382 }
383 BoundArgument::BoundSeq(MaybeSequence::Seq(vs)) => {
384 let nvs = vs
385 .iter()
386 .map(|cv @ ComponentVar { var, tp, df }| {
387 if tp.is_none() && checker.infer_var_type(var).is_none() {
388 changed = true;
389 let v = checker.new_solvable();
390 Cow::Owned(ComponentVar {
391 var: var.clone(),
392 tp: Some(v),
393 df: df.clone(),
394 })
395 } else {
396 Cow::Borrowed(cv)
397 }
398 })
399 .collect::<Vec<_>>();
400 if changed {
401 Cow::Owned(BoundArgument::BoundSeq(MaybeSequence::Seq(
402 nvs.into_iter().map(Cow::into_owned).collect(),
403 )))
404 } else {
405 Cow::Borrowed(a)
406 }
407 }
408 _ => Cow::Borrowed(a),
409 })
410 .collect::<Vec<_>>();
411 if changed {
412 Some(Term::Bound(BindingTerm::new(
413 b.head.clone(),
414 nargs.into_iter().map(Cow::into_owned).collect(),
415 b.presentation.clone(),
416 )))
417 } else {
418 None
419 }
420 });
421 std::ops::ControlFlow::Continue(ret.unwrap_or(Term::Bound(b)))
422 }
423
424 fn revert(&self, _: &CheckRef<'_, '_, Split>, t: Term) -> std::ops::ControlFlow<Term, Term> {
425 std::ops::ControlFlow::Continue(t)
426 }
427}
428
429#[derive(Debug, Clone, PartialEq, Eq)]
430pub struct PiVarianceRule(pub SymbolUri);
431
432impl SizedSolverRule for PiVarianceRule {
433 fn display(&self) -> Vec<crate::trace::Displayable> {
434 ftml_solver_trace::trace!(
435 &self.0,
436 " is contravariant in arguments and covariant in its return type"
437 )
438 }
439}
440impl<Split: SplitStrategy> SubtypeRule<Split> for PiVarianceRule {
441 fn applicable(&self, _: &CheckRef<'_, '_, Split>, sub: &Term, sup: &Term) -> bool {
442 if let Term::Bound(b) = sub
443 && let Term::Bound(b2) = sup
444 && let Term::Symbol { uri, .. } = &b.head
445 && let Term::Symbol { uri: uri2, .. } = &b2.head
446 {
447 *uri == self.0 && *uri2 == self.0 && b.arguments.len() == b2.arguments.len()
448 } else {
449 false
450 }
451 }
452 fn apply<'t>(
453 &self,
454 mut checker: CheckRef<'t, '_, Split>,
455 sub: &'t Term,
456 sup: &'t Term,
457 ) -> Option<bool> {
458 let Term::Bound(sub) = sub else { return None };
459 let Term::Bound(sup) = sup else { return None };
460 let Some(BoundArgument::Simple(subret)) = sub.arguments.last() else {
461 return None;
462 };
463 let Some(BoundArgument::Simple(supret)) = sup.arguments.last() else {
464 return None;
465 };
466 let mut currsub = Vec::<(&str, Term)>::new();
467 for (sub, sup) in sub.arguments[..sub.arguments.len() - 1]
468 .iter()
469 .zip(sup.arguments[..sup.arguments.len() - 1].iter())
470 {
471 match (sub, sup) {
472 (
473 BoundArgument::Bound(ComponentVar {
474 var: varsub,
475 tp: Some(sub),
476 ..
477 }),
478 BoundArgument::Bound(ComponentVar {
479 var: varsup,
480 tp: Some(sup),
481 ..
482 }),
483 ) => {
484 let sup = sup / &*currsub;
485 if !checker.scoped(|checker| checker.check_subtype(&sup, sub))? {
486 return None;
487 }
488 currsub.push((varsup.name(), varsub.clone().into()));
489 checker.extend_context(ComponentVar {
490 var: varsub.clone(),
491 tp: Some(sub.clone()),
492 df: None,
493 });
494 }
495 (
496 BoundArgument::BoundSeq(MaybeSequence::One(ComponentVar {
497 var: varsub,
498 tp: Some(sub),
499 ..
500 })),
501 BoundArgument::BoundSeq(MaybeSequence::One(ComponentVar {
502 var: varsup,
503 tp: Some(sup),
504 ..
505 }))
506 | BoundArgument::Bound(ComponentVar {
507 var: varsup,
508 tp: Some(sup),
509 ..
510 }),
511 ) => {
512 let sup = sup / &*currsub;
513 if !checker.scoped(|checker| checker.check_subtype(&sup, sub))? {
514 return None;
515 }
516 currsub.push((varsup.name(), varsub.clone().into()));
517 checker.extend_context(ComponentVar {
518 var: varsub.clone(),
519 tp: Some(sub.clone()),
520 df: None,
521 });
522 }
523 _ => {
524 checker.failure("argument not bound single variable or variable sequence");
525 return None;
527 }
528 }
529 }
530 let supret = supret / &*currsub;
531 checker.scoped(|checker| checker.check_subtype(subret, &supret))
532 }
533}
534
535#[derive(Debug, Clone, PartialEq, Eq)]
536pub struct LambdaPiInferenceRule {
537 pub lambda: SymbolUri,
538 pub pi: SymbolUri,
539}
540impl SizedSolverRule for LambdaPiInferenceRule {
541 fn display(&self) -> Vec<crate::trace::Displayable> {
542 ftml_solver_trace::trace!(
543 "{ x:A, B(x):T } ⊢ (",
544 &self.lambda,
545 " x:A. B) :=> ",
546 &self.pi,
547 " x:A. T"
548 )
549 }
550}
551impl LambdaPiInferenceRule {
552 fn infer_simple<'t, Split: SplitStrategy>(
553 &self,
554 mut checker: CheckRef<'t, '_, Split>,
555 var: &'t ComponentVar,
556 body: &'t Term,
557 ) -> Option<Term> {
558 let btp = match &var.tp {
559 None => {
560 if checker.infer_var_type(&var.var).is_some() {
561 checker.scoped(|checker| {
562 checker.extend_context(var);
563 checker.infer_type(body)
564 })?
565 } else {
566 let nvar = ComponentVar {
567 var: var.var.clone(),
568 tp: Some(checker.new_solvable()),
569 df: var.df.clone(),
570 };
571 checker.scoped(|checker| {
572 checker.extend_context(nvar);
573 checker.infer_type(body)
574 })?
575 }
576 }
577 Some(tp) if tp.is_solvable().is_some() => {
578 let inf = checker.scoped(|checker| {
579 checker.extend_context(var);
580 checker.infer_type(body)
581 })?;
582 ret!(&checker.check_inhabitable(tp) == Some(true));
583 inf
584 }
585 Some(tp) => {
586 ret!(&checker.check_inhabitable(tp) == Some(true));
587 checker.scoped(|checker| {
588 checker.extend_context(var);
589 checker.infer_type(body)
590 })?
591 }
592 };
593 Some(construct_binder(var.clone(), btp, &self.pi))
594 }
595
596 fn infer_sequence<'t, Split: SplitStrategy>(
597 &self,
598 mut checker: CheckRef<'t, '_, Split>,
599 var: &'t ComponentVar,
600 body: &'t Term,
601 ) -> Option<Term> {
602 let btp = match &var.tp {
603 None => {
604 if let Some(tp) = checker.infer_var_type(&var.var) {
605 checker.scoped(|checker| {
606 checker.extend_context(var);
607 checker.infer_type(body)
608 })?
609 } else {
610 let nvar = ComponentVar {
611 var: var.var.clone(),
612 tp: Some(checker.new_solvable()),
613 df: var.df.clone(),
614 };
615 checker.scoped(|checker| {
616 checker.extend_context(nvar);
617 checker.infer_type(body)
618 })?
619 }
620 }
621 Some(tp) if tp.is_solvable().is_some() => {
622 let inf = checker.scoped(|checker| {
623 checker.extend_context(var);
624 checker.infer_type(body)
625 })?;
626 ret!(&checker.check_inhabitable(tp) == Some(true));
627 inf
628 }
629 Some(tp) => {
630 ret!(&checker.check_inhabitable(tp) == Some(true));
631 checker.scoped(|checker| {
632 checker.extend_context(var);
633 checker.infer_type(body)
634 })?
635 }
636 };
637 let r = Term::Bound(BindingTerm::new(
638 Term::Symbol {
639 uri: self.pi.clone(),
640 presentation: None,
641 },
642 Box::new([
643 BoundArgument::BoundSeq(MaybeSequence::One(var.clone())),
644 BoundArgument::Simple(btp),
645 ]),
646 None,
647 ));
648 Some(r)
649 }
650}
651impl<Split: SplitStrategy> InferenceRule<Split> for LambdaPiInferenceRule {
652 fn applicable(&self, term: &Term) -> bool {
653 destruct_binder(term, &self.lambda).is_some()
654 }
655 fn infer<'t>(&self, checker: CheckRef<'t, '_, Split>, term: &'t Term) -> Option<Term> {
656 let (var, body) = destruct_binder(term, &self.lambda)?;
657 match var {
658 either::Left(var) => self.infer_simple(checker, var, body),
659 either::Right(var) => self.infer_sequence(checker, var, body),
660 }
661 }
662}
663
664#[derive(Debug, Clone, PartialEq, Eq)]
665pub struct LambdaPiCheckingRule {
666 pub lambda: SymbolUri,
667 pub pi: SymbolUri,
668}
669impl SizedSolverRule for LambdaPiCheckingRule {
670 fn display(&self) -> Vec<crate::trace::Displayable> {
671 ftml_solver_trace::trace!(
672 "{ x:A, B(x):T } ⊢ (",
673 &self.lambda,
674 " x:A. B) : ",
675 &self.pi,
676 " y:A. T"
677 )
678 }
679}
680impl<Split: SplitStrategy> CheckingRule<Split> for LambdaPiCheckingRule {
681 fn applicable(&self, _: &CheckRef<'_, '_, Split>, term: &Term, tp: &Term) -> bool {
682 destruct_binder(term, &self.lambda).is_some_and(|(v, _)| v.is_left())
683 && destruct_binder(tp, &self.pi).is_some_and(|(v, _)| v.is_left())
684 }
685 fn apply<'t>(
686 &self,
687 mut checker: CheckRef<'t, '_, Split>,
688 term: &'t Term,
689 tp: &'t Term,
690 ) -> Option<bool> {
691 let (var, lambda_body) = destruct_binder(term, &self.lambda)?;
692 let var = var.expect_left("applicable");
693 let (pivar, pi_body) = destruct_binder(tp, &self.pi)?;
694 let pivar = pivar.expect_left("applicable");
695 let pi_tp = match &pivar.tp {
696 None => Cow::Owned(checker.infer_var_type(&var.var)?),
697 Some(tp) => {
698 Cow::Borrowed(tp)
700 }
701 };
702 let lam_tp = match &var.tp {
703 None => Cow::Owned(checker.infer_var_type(&var.var)?),
704 Some(tp) => {
705 Cow::Borrowed(tp)
707 }
708 };
709 ret!(&checker.scoped(|checker| { checker.check_subtype(&pi_tp, &lam_tp) }) == Some(true));
710 let ntp = pi_body
711 / (
712 pivar.var.name(),
713 &Term::Var {
714 variable: var.var.clone(),
715 presentation: None,
716 },
717 );
718 checker.scoped(|checker| {
719 checker.extend_context(var);
720 checker.check_type(lambda_body, &ntp)
721 })
722 }
723}
724
725#[derive(Debug, Clone, PartialEq, Eq)]
726pub struct PiInhabitableRule(pub SymbolUri);
727impl SizedSolverRule for PiInhabitableRule {
728 fn display(&self) -> Vec<crate::trace::Displayable> {
729 ftml_solver_trace::trace!("{ INH A, x:A, INH B(x) } ⊢ INH ", &self.0, " x:A. B")
730 }
731}
732
733impl<Split: SplitStrategy> InhabitableRule<Split> for PiInhabitableRule {
734 fn applicable(&self, term: &Term) -> bool {
735 matches!(term,Term::Bound(b) if matches!(&b.head,Term::Symbol { uri, .. } if *uri == self.0))
736 }
737 fn apply<'t>(&self, mut checker: CheckRef<'t, '_, Split>, term: &'t Term) -> Option<bool> {
738 let Term::Bound(b) = term else { return None };
739 let Some(BoundArgument::Simple(body)) = b.arguments.last() else {
740 return None;
741 };
742 let previous = &b.arguments[..&b.arguments.len() - 1];
743 let mut deferred = Vec::new();
744 for arg in previous {
746 match arg {
747 BoundArgument::Simple(t) | BoundArgument::Sequence(MaybeSequence::One(t)) => {
748 let _ = checker.infer_type(t)?;
749 }
750 BoundArgument::Sequence(MaybeSequence::Seq(ts)) => {
751 for t in ts {
752 let _ = checker.infer_type(t)?;
753 }
754 }
755 BoundArgument::Bound(cv @ ComponentVar { var, tp, .. })
756 | BoundArgument::BoundSeq(MaybeSequence::One(cv @ ComponentVar { var, tp, .. })) => {
757 if let Some(tp) = tp {
758 if tp.has_solvable() {
759 deferred.push(tp);
760 } else if !checker.check_inhabitable(tp)? {
761 return Some(false);
762 }
763 } else {
764 let _ = checker.infer_var_type(var)?;
765 }
766 checker.extend_context(cv);
767 }
768 BoundArgument::BoundSeq(MaybeSequence::Seq(vars)) => {
769 for cv @ ComponentVar { var, tp, .. } in vars {
770 if let Some(tp) = tp {
771 if tp.has_solvable() {
772 deferred.push(tp);
773 } else if !checker.check_inhabitable(tp)? {
774 return Some(false);
775 }
776 } else {
777 let _ = checker.infer_var_type(var)?;
778 }
779 checker.extend_context(cv);
780 }
781 }
782 }
783 }
784 if !checker.check_inhabitable(body)? {
785 return None;
786 }
787 for d in deferred {
788 if !checker.check_inhabitable(d)? {
789 return None;
790 }
791 }
792 Some(true)
793 }
794}
795
796#[derive(Debug, Clone, PartialEq, Eq)]
797pub struct PiUniverseRule(pub SymbolUri);
798impl SizedSolverRule for PiUniverseRule {
799 fn display(&self) -> Vec<crate::trace::Displayable> {
800 ftml_solver_trace::trace!("{ INH A, x:A, UNIV B(x) } ⊢ UNIV ", &self.0, " x:A. B")
801 }
802}
803
804impl<Split: SplitStrategy> UniverseRule<Split> for PiUniverseRule {
805 fn applicable(&self, term: &Term) -> bool {
806 matches!(term,Term::Bound(b) if matches!(&b.head,Term::Symbol { uri, .. } if *uri == self.0))
807 }
808 fn apply<'t>(&self, mut checker: CheckRef<'t, '_, Split>, term: &'t Term) -> Option<bool> {
809 let Term::Bound(b) = term else { return None };
810 let Some(BoundArgument::Simple(body)) = b.arguments.last() else {
811 return None;
812 };
813 let previous = &b.arguments[..&b.arguments.len() - 1];
814 let mut deferred = Vec::new();
815 for arg in previous {
817 match arg {
818 BoundArgument::Simple(t) | BoundArgument::Sequence(MaybeSequence::One(t)) => {
819 let _ = checker.infer_type(t)?;
820 }
821 BoundArgument::Sequence(MaybeSequence::Seq(ts)) => {
822 for t in ts {
823 let _ = checker.infer_type(t)?;
824 }
825 }
826 BoundArgument::Bound(cv @ ComponentVar { var, tp, .. })
827 | BoundArgument::BoundSeq(MaybeSequence::One(cv @ ComponentVar { var, tp, .. })) => {
828 if let Some(tp) = tp {
829 if tp.has_solvable() {
830 deferred.push(tp);
831 } else if !checker.check_inhabitable(tp)? {
832 return Some(false);
833 }
834 } else {
835 let _ = checker.infer_var_type(var)?;
836 }
837 checker.extend_context(cv);
838 }
839 BoundArgument::BoundSeq(MaybeSequence::Seq(vars)) => {
840 for cv @ ComponentVar { var, tp, .. } in vars {
841 if let Some(tp) = tp {
842 if tp.has_solvable() {
843 deferred.push(tp);
844 } else if !checker.check_inhabitable(tp)? {
845 return Some(false);
846 }
847 } else {
848 let _ = checker.infer_var_type(var)?;
849 }
850 checker.extend_context(cv);
851 }
852 }
853 }
854 }
855 if !checker.check_universe(body)? {
856 return None;
857 }
858 for d in deferred {
859 if !checker.check_inhabitable(d)? {
860 return None;
861 }
862 }
863 Some(true)
864 }
865}
866
867#[derive(Debug, Clone, PartialEq, Eq)]
868pub struct PiInferenceRule(pub SymbolUri);
869impl SizedSolverRule for PiInferenceRule {
870 fn display(&self) -> Vec<crate::trace::Displayable> {
871 ftml_solver_trace::trace!("{ f: ", &self.0, " x:A.B(x), t:A } ⊢ f(t) :=> B(t)")
872 }
873}
874
875impl<Split: SplitStrategy> InferenceRule<Split> for PiInferenceRule {
876 fn applicable(&self, term: &Term) -> bool {
877 matches!(term, Term::Application(_)) }
879 fn infer<'t>(&self, mut checker: CheckRef<'t, '_, Split>, term: &'t Term) -> Option<Term> {
880 if let Term::Application(app) = term {
881 if app.arguments.is_empty() {
882 return None;
883 }
884 let tp = checker.infer_type(&app.head)?;
885 if let Some(SequenceType::SeqType(tp, _)) = tp.as_sequence_type()
886 && let [Argument::Simple(idx)] = &*app.arguments
887 {
888 checker.comment("is sequence type");
889 let nat = checker.rules().marker().iter().rev().find_map(|rl| {
890 rl.as_any().downcast_ref::<NumberRule>().and_then(|rl| {
891 if rl.typ == NumberType::Naturals {
892 Some(rl.sym.clone())
893 } else {
894 None
895 }
896 })
897 })?;
898 let ntp: Term = nat.into();
899 if checker.scoped(|checker| checker.check_type(idx, &ntp)) != Some(true) {
900 return None;
901 }
902 Some(tp.clone())
903 } else {
904 self.type_apply(&mut checker, tp, &app.arguments)
905 }
906 } else {
907 checker.failure("Not an application");
908 None
909 }
910 }
911}
912
913impl PiInferenceRule {
914 pub fn deconstruct_tp<Split: SplitStrategy>(
916 bind_uri: &SymbolUri,
917 checker: &mut CheckRef<'_, '_, Split>,
918 tp: Term,
919 ) -> Result<BindingTerm, Term> {
920 let Some(nret) = checker.scoped(|checker| {
921 match checker.simplify_until(&tp, |_, t| matches!(t, Term::Bound(_)))? {
922 Cow::Borrowed(_) => Some(None),
923 Cow::Owned(tp) => Some(Some(tp)),
924 }
925 }) else {
926 checker.add_msg(traceref!(FAIL "type is not a binder: ",tp.clone()).into());
927 return Err(tp);
928 };
929 let Term::Bound(b) = nret.unwrap_or(tp) else {
930 unsafe { unreachable_unchecked() }
932 };
933 if !matches!(&b.head,Term::Symbol { uri, .. } if *uri == *bind_uri)
934 || b.arguments.len() != 2
935 || !matches!(b.arguments.get(1), Some(BoundArgument::Simple(_)))
936 {
937 checker.failure("Type is not a Î anymore");
938 return Err(Term::Bound(b));
939 }
940 Ok(b)
941 }
942
943 pub(super) fn flatten_sequence<Split: SplitStrategy>(
945 checker: &mut CheckRef<'_, '_, Split>,
946 tp: &Term,
947 b: &BindingTerm,
948 body: Term,
949 ) -> Term {
950 let Some(new_args) = tp.as_sequence().and_then(|s| s.to_concrete()) else {
951 unsafe { unreachable_unchecked() }
953 };
954 checker.comment("Flattening concrete sequence argument");
955 new_args.into_iter().rfold(body.clone(), |body, arg| {
956 Term::Bound(BindingTerm::new(
957 b.head.clone(),
958 Box::new([
959 BoundArgument::Bound(ComponentVar {
960 var: Variable::Name {
961 name: crate::DUMMY.clone(),
962 notated: None,
963 },
964 tp: Some(arg),
965 df: None,
966 }),
967 BoundArgument::Simple(body),
968 ]),
969 b.presentation.clone(),
970 ))
971 })
972 }
973
974 pub(super) fn recurse_seq_args<'t, Split: SplitStrategy>(
976 uri: &SymbolUri,
977 checker: &mut CheckRef<'t, '_, Split>,
978 b: &BindingTerm,
979 seq: &'t [Term],
980 body: &Term,
981 ) -> Option<Term> {
982 let first = unsafe { seq.first().unwrap_unchecked() };
984 let rest = &seq[1..];
985 let mut ret = Self::simple_apply(checker, b, first, body)?;
986 for arg in rest {
987 let b = Self::deconstruct_tp(uri, checker, ret).ok()?;
988 let [_, BoundArgument::Simple(body)] = &*b.arguments else {
989 unsafe { unreachable_unchecked() }
991 };
992 ret = Self::simple_apply(checker, &b, arg, body)?;
993 }
994 Some(ret)
995 }
996
997 fn try_extension<'t, Split: SplitStrategy>(
998 &self,
999 checker: &mut CheckRef<'t, '_, Split>,
1000 tp: &'t Term,
1001 args: &'t [Argument],
1002 index: &mut usize,
1003 ) -> Option<Term> {
1004 let exts = checker
1005 .rules()
1006 .marker()
1007 .iter()
1008 .rev()
1009 .filter_map(|rl| rl.as_any().downcast_ref::<PiExtensionRule<Split>>())
1010 .cloned()
1011 .collect::<SmallVec<_, 1>>();
1012 let arg = &args[*index];
1013 let Some(ntp) =
1014 checker.simplify_until(tp, |_, t| exts.iter().any(|rl| (rl.applicable)(rl, t, arg)))
1015 else {
1016 checker.failure("type is not a pi");
1017 return None;
1018 };
1019 for rl in exts {
1020 if (rl.applicable)(&rl, &ntp, arg)
1021 && let Some(t) = (rl.infer)(&rl, self, checker, &ntp, args, index)
1022 {
1023 return Some(t);
1024 }
1025 }
1026 None
1027 }
1028
1029 pub fn type_apply<'t, Split: SplitStrategy>(
1030 &self,
1031 checker: &mut CheckRef<'t, '_, Split>,
1032 tp: Term,
1033 args: &'t [Argument],
1034 ) -> Option<Term> {
1035 let mut ret = tp;
1036 let mut i = 0;
1037 loop {
1038 let Some(arg) = args.get(i) else {
1039 return Some(ret);
1040 };
1041 let dec = Self::deconstruct_tp(&self.0, checker, ret);
1042 let b = match dec {
1043 Ok(b) => b,
1044 Err(t) => {
1045 ret =
1046 checker.scoped(|checker| self.try_extension(checker, &t, args, &mut i))?;
1047 continue;
1048 }
1049 };
1050 let [first, BoundArgument::Simple(body)] = &*b.arguments else {
1051 unsafe { unreachable_unchecked() }
1053 };
1054 match (first, arg) {
1055 (
1056 BoundArgument::Bound(ComponentVar {
1057 var,
1058 tp: Some(tp),
1059 df: None,
1060 }),
1061 Argument::Sequence(seq),
1062 ) if !body.has_free_such_that(|v| v.name() == var.name())
1063 && tp.as_sequence().is_some_and(|s| s.is_concrete()) =>
1064 {
1065 ret = Self::flatten_sequence(checker, tp, &b, body.clone());
1066 }
1067 (
1068 BoundArgument::Bound(ComponentVar {
1069 var,
1070 tp: Some(tp),
1071 df: None,
1072 }),
1073 Argument::Sequence(MaybeSequence::Seq(seq)),
1074 ) if !seq.is_empty() => {
1075 i += 1;
1076 checker.counter("(a) Checking Argument ", i);
1077 ret = Self::recurse_seq_args(&self.0, checker, &b, seq, body)?;
1078 }
1079 (_, Argument::Simple(arg)) => {
1080 i += 1;
1081 checker.counter("(b) Checking Argument ", i);
1082 ret = Self::simple_apply(checker, &b, arg, body)?;
1083 }
1084 (_, Argument::Sequence(arg)) => {
1085 i += 1;
1086 checker.counter("(c) Checking Argument ", i);
1087 ret = checker.scoped(|checker| Self::seq_apply(checker, &b, arg, body))?;
1088 }
1089 }
1090 }
1091 }
1092
1093 pub(super) fn simple_apply<'t, Split: SplitStrategy>(
1094 checker: &mut CheckRef<'t, '_, Split>,
1095 b: &BindingTerm,
1096 arg: &'t Term,
1097 body: &Term,
1098 ) -> Option<Term> {
1099 let headvar = match b.arguments.first() {
1100 Some(BoundArgument::Bound(headvar)) => headvar,
1101 Some(BoundArgument::BoundSeq(MaybeSequence::Seq(vs))) => {
1102 if let [headvar] = &**vs {
1103 headvar
1104 } else {
1105 checker.failure("First argument is not a bound variable");
1106 return None;
1107 }
1108 }
1109 Some(BoundArgument::BoundSeq(MaybeSequence::One(v))) if arg.is_sequence() => v,
1110 Some(BoundArgument::BoundSeq(MaybeSequence::One(_))) => {
1111 let seq = Term::into_seq(std::iter::once(arg.clone()));
1112 return checker.scoped(|slf| Self::simple_apply(slf, b, &seq, body));
1113 }
1114 _ => {
1115 checker.failure("First argument is not a bound variable");
1116 return None;
1123 }
1124 };
1125
1126 let (varname, vartp) = match headvar {
1127 ComponentVar {
1128 var, tp: Some(tp), ..
1129 } => (var.name(), tp.clone()),
1130 ComponentVar { var, .. } => (
1131 var.name(),
1132 checker.scoped(|checker| {
1133 checker
1134 .infer_var_type(var)
1135 .unwrap_or_else(|| checker.new_solvable())
1136 }),
1137 ),
1138 };
1139
1140 if checker
1141 .scoped(|checker| checker.check_type(arg, &vartp))
1142 .is_none_or(|b| !b)
1143 {
1144 return None;
1145 }
1146 Some((body / (varname, arg)).into_owned())
1147 }
1148
1149 pub(super) fn seq_apply<'t, Split: SplitStrategy>(
1150 checker: &mut CheckRef<'t, '_, Split>,
1151 b: &'t BindingTerm,
1152 arg: &'t MaybeSequence<Term>,
1153 body: &Term,
1154 ) -> Option<Term> {
1155 if let Some(BoundArgument::Bound(ComponentVar {
1156 var,
1157 tp: Some(tp),
1158 df: None,
1159 })) = b.arguments.first()
1160 && let Some(tpargs) = tp.as_sequence().and_then(|s| s.to_concrete())
1161 && !body
1162 .free_variables()
1163 .into_iter()
1164 .any(|v| v.name() == var.name())
1165 && let MaybeSequence::Seq(args) = arg
1166 && args.len() == tpargs.len()
1167 {
1168 for (a, t) in args.iter().zip(tpargs.iter()) {
1169 if !checker.scoped(|checker| checker.check_type(a, t))? {
1170 return None;
1171 }
1172 }
1173 return Some(body.clone());
1174 }
1175
1176 let Some(BoundArgument::BoundSeq(MaybeSequence::One(headvar))) = b.arguments.first() else {
1177 checker.failure("First argument is not a bound variable sequence");
1178 return None;
1184 };
1185
1186 let (varname, vartp) = match headvar {
1187 ComponentVar {
1188 var, tp: Some(tp), ..
1189 } => (var.name(), tp.clone()),
1190 ComponentVar { var, .. } => (
1191 var.name(),
1192 checker.scoped(|checker| {
1193 checker
1194 .infer_var_type(var)
1195 .unwrap_or_else(|| checker.new_solvable())
1196 }),
1197 ),
1198 };
1199
1200 match arg {
1201 MaybeSequence::One(arg) => {
1202 if checker
1203 .scoped(|checker| checker.check_type(arg, &vartp))
1204 .is_none_or(|b| !b)
1205 {
1206 return None;
1207 }
1208 Some((body / (varname, arg)).into_owned())
1209 }
1210 MaybeSequence::Seq(arg) => {
1211 let narg = Term::into_seq(arg.iter().cloned());
1212 if let Some(SequenceType::SeqType(vartp, _)) = vartp.as_sequence_type() {
1213 if !checker.scoped(|checker| {
1214 for a in arg {
1215 if !checker.check_type(a, vartp)? {
1216 return None;
1217 }
1218 }
1219 Some(true)
1220 })? {
1221 return None;
1222 }
1223 } else if checker
1224 .scoped(|checker| checker.check_type(&narg, &vartp))
1225 .is_none_or(|b| !b)
1226 {
1227 return None;
1228 }
1229
1230 Some((body / (varname, &narg)).into_owned())
1231 }
1232 }
1233 }
1234}
1235
1236#[derive(Debug, Clone, PartialEq, Eq)]
1237pub struct BetaRule(pub SymbolUri);
1238impl SizedSolverRule for BetaRule {
1239 fn display(&self) -> Vec<crate::trace::Displayable> {
1240 ftml_solver_trace::trace!("{ a:A } (", &self.0, " x:A. t)(a) ==> t[x/a]")
1241 }
1242}
1243impl<Split: SplitStrategy> SimplificationRule<Split> for BetaRule {
1244 fn applicable(&self, term: &Term) -> bool {
1245 if let Term::Application(app) = term
1246 && let Term::Bound(op) = &app.head
1247 && let Term::Symbol { uri, .. } = &op.head
1248 {
1249 *uri == self.0 && app.arguments.len() >= op.arguments.len() - 1
1250 } else {
1251 false
1252 }
1253 }
1254 fn apply<'t>(
1255 &self,
1256 mut checker: CheckRef<'t, '_, Split>,
1257 term: &'t Term,
1258 ) -> Result<Term, Option<ftml_ontology::terms::termpaths::TermPath>> {
1259 let Term::Application(app) = term else {
1260 return Err(None);
1261 };
1262 let Term::Bound(fun) = &app.head else {
1263 return Err(None);
1264 };
1265 let Some(BoundArgument::Simple(ret)) = fun.arguments.last() else {
1266 return Err(None);
1267 };
1268 let funargs = &fun.arguments[..fun.arguments.len() - 1];
1269 let actual_args = &app.arguments[..funargs.len()];
1270 let rest_args = &app.arguments[funargs.len()..];
1271 let mut ret = Cow::Borrowed(ret);
1272 for (i, (v, a)) in funargs.iter().zip(actual_args).enumerate() {
1273 checker.counter("Checking argument ", i + 1);
1274 match (v, a) {
1275 (
1276 BoundArgument::Bound(ComponentVar {
1277 var,
1278 tp: Some(tp),
1279 df: None,
1280 }),
1281 Argument::Simple(a),
1282 ) => {
1283 if !checker.check_type(a, tp).ok_or(None)? {
1284 return Err(None);
1285 }
1286 if let Cow::Owned(nr) = &*ret / (var.name(), a) {
1287 ret = Cow::Owned(nr);
1288 }
1289 }
1290 (
1291 BoundArgument::Bound(ComponentVar {
1292 var,
1293 tp: None,
1294 df: None,
1295 }),
1296 Argument::Simple(a),
1297 ) => {
1298 let Some(tp) = checker.infer_var_type(var) else {
1299 checker.add_msg(traceref!(FAIL "untyped variable ",var).into());
1300 return Err(None);
1301 };
1302 if checker.scoped(|checker| checker.check_type(a, &tp)) != Some(true) {
1303 return Err(None);
1304 }
1305 if let Cow::Owned(nr) = &*ret / (var.name(), a) {
1306 ret = Cow::Owned(nr);
1307 }
1308 }
1309 (
1310 BoundArgument::BoundSeq(MaybeSequence::One(ComponentVar {
1311 var,
1312 tp: Some(tp),
1313 df: None,
1314 })),
1315 Argument::Sequence(MaybeSequence::Seq(ts)),
1316 ) => {
1317 let Some(SequenceType::SeqType(tp, _)) = tp.as_sequence_type() else {
1318 checker.failure("Type of sequence variable is not a sequence type");
1319 return Err(None);
1321 };
1322 for t in ts {
1323 if !checker.check_type(t, tp).ok_or(None)? {
1324 return Err(None);
1325 }
1326 }
1327 if let Cow::Owned(nr) =
1328 &*ret / (var.name(), &Term::into_seq(ts.iter().cloned()))
1329 {
1330 ret = Cow::Owned(nr);
1331 }
1332 }
1333 (
1334 BoundArgument::BoundSeq(MaybeSequence::One(ComponentVar {
1335 var,
1336 tp: Some(tp),
1337 df: None,
1338 })),
1339 Argument::Simple(t),
1340 ) => {
1341 if tp.as_sequence_type().is_none() {
1342 checker.failure("Type of sequence variable is not a sequence type");
1343 return Err(None);
1345 }
1346 if !checker.check_type(t, tp).ok_or(None)? {
1347 return Err(None);
1348 }
1349 if let Cow::Owned(nr) = &*ret / (var.name(), t) {
1350 ret = Cow::Owned(nr);
1351 }
1352 }
1353
1354 (
1355 BoundArgument::Bound(ComponentVar {
1356 var,
1357 tp: None,
1358 df: None,
1359 }),
1360 Argument::Sequence(MaybeSequence::Seq(args)),
1361 ) => {
1362 let Some(tp) = checker.infer_var_type(var) else {
1363 checker.add_msg(traceref!(FAIL "untyped variable ",var).into());
1364 return Err(None);
1365 };
1366 let Some(tp) = checker.scoped(|checker| {
1367 checker
1368 .simplify_until(&tp, |_, t| {
1369 t.as_sequence().is_some_and(|s| s.is_concrete())
1370 })
1371 .map(Cow::into_owned)
1372 }) else {
1373 checker.failure("Not a concrete sequence");
1374 return Err(None);
1375 };
1376 let vartps = tp.as_sequence().and_then(|s| s.to_concrete()).ok_or(None)?;
1377 if vartps.len() != args.len() {
1378 checker.failure("sequence lengths don't match");
1379 return Err(None);
1380 }
1381 for (a, tp) in args.iter().zip(vartps) {
1382 if !checker
1383 .scoped(|checker| checker.check_type(a, &tp))
1384 .ok_or(None)?
1385 {
1386 return Err(None);
1387 }
1388 }
1389 }
1390 (
1391 BoundArgument::Bound(ComponentVar {
1392 var,
1393 tp: Some(tp),
1394 df: None,
1395 }),
1396 Argument::Sequence(MaybeSequence::Seq(args)),
1397 ) => {
1398 let ntp = checker
1399 .scoped(|checker| {
1400 checker
1401 .simplify_until(tp, |_, t| {
1402 t.as_sequence().is_some_and(|s| s.is_concrete())
1403 })
1404 .map(Cow::into_owned)
1405 })
1406 .ok_or(None)?;
1407 let Some(vartps) = ntp.as_sequence().and_then(|s| s.to_concrete()) else {
1408 checker.failure("type of bound variable not a concrete sequence");
1409 return Err(None);
1410 };
1411 if vartps.len() != args.len() {
1412 checker.failure("sequence lengths don't match");
1413 return Err(None);
1414 }
1415 for (a, tp) in args.iter().zip(vartps) {
1416 if !checker
1417 .scoped(|checker| checker.check_type(a, &tp))
1418 .ok_or(None)?
1419 {
1420 return Err(None);
1421 }
1422 }
1423 }
1424
1425 (a, b) => {
1426 checker.failure(format!("TODO: {a:?} <-> {b:?}"));
1427 return Err(None);
1428 }
1429 }
1430 }
1431 if rest_args.is_empty() {
1432 Ok(ret.into_owned())
1433 } else {
1434 Ok(Term::Application(ApplicationTerm::new(
1435 ret.into_owned(),
1436 rest_args.iter().cloned().collect(),
1437 None,
1438 )))
1439 }
1440 }
1441}
1442
1443#[derive(Debug, Clone, PartialEq, Eq)]
1444pub struct ApplyRule(pub SymbolUri);
1445impl SizedSolverRule for ApplyRule {
1446 fn display(&self) -> Vec<crate::trace::Displayable> {
1447 ftml_solver_trace::trace!("{ a:A } ", &self.0, "(f,a*) ==> f(a*)")
1448 }
1449 fn priority(&self) -> isize {
1450 isize::MAX
1451 }
1452}
1453impl<Split: SplitStrategy> PreparationRule<Split> for ApplyRule {
1454 fn applicable(&self, _: &CheckRef<'_, '_, Split>, t: &Term) -> bool {
1455 if let Term::Application(app) = t
1456 && let [Argument::Simple(_), Argument::Sequence(_)] = &*app.arguments
1457 && let Term::Symbol { uri, .. } = &app.head
1458 {
1459 *uri == self.0
1460 } else {
1461 false
1462 }
1463 }
1464 fn apply(
1465 &self,
1466 _: &mut CheckRef<'_, '_, Split>,
1467 t: Term,
1468 path: Option<(&mut smallvec::SmallVec<u8, 16>, usize)>,
1469 ) -> std::ops::ControlFlow<Term, Term> {
1470 let Term::Application(app) = t else {
1471 return std::ops::ControlFlow::Continue(t);
1472 };
1473 let [Argument::Simple(f), Argument::Sequence(a)] = &*app.arguments else {
1474 return std::ops::ControlFlow::Continue(Term::Application(app));
1475 };
1476 let ret = Term::Application(ApplicationTerm::new(
1477 f.clone(),
1478 match a {
1479 MaybeSequence::One(o) => {
1480 Box::new([Argument::Sequence(MaybeSequence::One(o.clone()))])
1481 }
1482 MaybeSequence::Seq(ts) => ts.iter().map(|t| Argument::Simple(t.clone())).collect(),
1483 },
1484 app.presentation.clone(),
1489 ));
1490 if let Some((p, i)) = path
1491 && let Some(j) = p.get_mut(i)
1492 {
1493 *j = j.saturating_sub(1);
1494 }
1495 std::ops::ControlFlow::Continue(ret)
1496 }
1497 fn applicable_revert(&self, _: &CheckRef<'_, '_, Split>, _: &Term) -> bool {
1498 false
1499 }
1500 fn revert(&self, _: &CheckRef<'_, '_, Split>, t: Term) -> std::ops::ControlFlow<Term, Term> {
1501 std::ops::ControlFlow::Continue(t)
1502 }
1503}