1use std::collections::HashMap;
18
19use crate::{
20 analysis::Kind,
21 logic::{Clause, IRTerm, Literal, Predicate},
22};
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
25pub enum SelectBuiltinResult {
26 Match,
27 GroundnessMismatch,
28 NoMatch,
29}
30
31impl SelectBuiltinResult {
32 pub fn is_match(&self) -> bool {
33 match self {
34 SelectBuiltinResult::Match => true,
35 _ => false,
36 }
37 }
38}
39
40pub trait BuiltinPredicate {
41 fn name(&self) -> &'static str;
42
43 fn kind(&self) -> Kind;
46
47 fn arg_groundness(&self) -> &'static [bool];
49
50 fn select(&self, lit: &Literal) -> SelectBuiltinResult {
51 let Literal {
52 ref predicate,
53 ref args,
54 ..
55 } = lit;
56 if &predicate.0 != self.name() {
57 return SelectBuiltinResult::NoMatch;
58 }
59 if args.len() == self.arg_groundness().len()
60 && args.iter().zip(self.arg_groundness().into_iter()).all(
61 |(term, allows_ungrounded)| {
62 *allows_ungrounded || term.is_constant_or_compound_constant()
63 },
64 )
65 {
66 SelectBuiltinResult::Match
67 } else {
68 SelectBuiltinResult::GroundnessMismatch
69 }
70 }
71
72 fn apply(&self, lit: &Literal) -> Option<Literal>;
86}
87
88mod string_concat {
89 use super::BuiltinPredicate;
90 use crate::logic::{IRTerm, Literal, Predicate, SpannedPosition};
91
92 fn string_concat_result(
93 a: &str,
94 b: &str,
95 c: &str,
96 pos: &Option<SpannedPosition>,
97 ) -> Option<Literal> {
98 Some(Literal {
99 positive: true,
100 position: pos.clone(),
101 predicate: Predicate("string_concat".to_owned()),
102 args: vec![
103 IRTerm::Constant(a.to_owned()),
104 IRTerm::Constant(b.to_owned()),
105 IRTerm::Constant(c.to_owned()),
106 ],
107 })
108 }
109
110 pub struct StringConcat1;
111 impl BuiltinPredicate for StringConcat1 {
112 fn name(&self) -> &'static str {
113 "string_concat"
114 }
115
116 fn kind(&self) -> crate::analysis::Kind {
117 crate::analysis::Kind::Logic
118 }
119
120 fn arg_groundness(&self) -> &'static [bool] {
121 &[false, false, true]
122 }
123
124 fn apply(&self, lit: &Literal) -> Option<Literal> {
125 let a = lit.args[0].as_constant()?;
126 let b = lit.args[1].as_constant()?;
127 let c = a.to_owned() + b;
128 string_concat_result(a, b, &c, &lit.position)
129 }
130 }
131
132 pub struct StringConcat2;
133 impl BuiltinPredicate for StringConcat2 {
134 fn name(&self) -> &'static str {
135 "string_concat"
136 }
137
138 fn kind(&self) -> crate::analysis::Kind {
139 crate::analysis::Kind::Logic
140 }
141
142 fn arg_groundness(&self) -> &'static [bool] {
143 &[true, false, false]
144 }
145
146 fn apply(&self, lit: &Literal) -> Option<Literal> {
147 let b = lit.args[1].as_constant()?;
148 let c = lit.args[2].as_constant()?;
149 if let Some(a) = c.strip_suffix(&b) {
150 string_concat_result(a, b, c, &lit.position)
151 } else {
152 None
153 }
154 }
155 }
156
157 pub struct StringConcat3;
158 impl BuiltinPredicate for StringConcat3 {
159 fn name(&self) -> &'static str {
160 "string_concat"
161 }
162
163 fn kind(&self) -> crate::analysis::Kind {
164 crate::analysis::Kind::Logic
165 }
166
167 fn arg_groundness(&self) -> &'static [bool] {
168 &[false, true, false]
169 }
170
171 fn apply(&self, lit: &Literal) -> Option<Literal> {
172 let a = lit.args[0].as_constant()?;
173 let c = lit.args[2].as_constant()?;
174 if let Some(b) = c.strip_prefix(&a) {
175 string_concat_result(a, b, c, &lit.position)
176 } else {
177 None
178 }
179 }
180 }
181}
182
183mod equality {
184 use crate::logic::{IRTerm, Literal, Predicate};
185
186 use super::BuiltinPredicate;
187
188 pub struct StringEq1;
189 impl BuiltinPredicate for StringEq1 {
190 fn name(&self) -> &'static str {
191 "string_eq"
192 }
193
194 fn kind(&self) -> crate::analysis::Kind {
195 crate::analysis::Kind::Logic
196 }
197
198 fn arg_groundness(&self) -> &'static [bool] {
199 &[false, true]
200 }
201
202 fn apply(&self, lit: &crate::logic::Literal) -> Option<crate::logic::Literal> {
203 let a = lit.args[0].as_constant()?;
204 Some(Literal {
205 positive: true,
206 position: lit.position.clone(),
207 predicate: Predicate("string_eq".to_owned()),
208 args: vec![
209 IRTerm::Constant(a.to_owned()),
210 IRTerm::Constant(a.to_owned()),
211 ],
212 })
213 }
214 }
215
216 pub struct StringEq2;
217 impl BuiltinPredicate for StringEq2 {
218 fn name(&self) -> &'static str {
219 "string_eq"
220 }
221
222 fn kind(&self) -> crate::analysis::Kind {
223 crate::analysis::Kind::Logic
224 }
225
226 fn arg_groundness(&self) -> &'static [bool] {
227 &[true, false]
228 }
229
230 fn apply(&self, lit: &crate::logic::Literal) -> Option<crate::logic::Literal> {
231 let b = lit.args[1].as_constant()?;
232 Some(Literal {
233 positive: true,
234 position: lit.position.clone(),
235 predicate: Predicate("string_eq".to_owned()),
236 args: vec![
237 IRTerm::Constant(b.to_owned()),
238 IRTerm::Constant(b.to_owned()),
239 ],
240 })
241 }
242 }
243}
244
245mod number {
246 use super::BuiltinPredicate;
247
248 macro_rules! define_number_comparison {
249 ($name:ident, $cond:expr) => {
250 #[allow(non_camel_case_types)]
251 pub struct $name;
252 impl BuiltinPredicate for $name {
253 fn name(&self) -> &'static str {
254 stringify!($name)
255 }
256
257 fn kind(&self) -> crate::analysis::Kind {
258 crate::analysis::Kind::Logic
259 }
260
261 fn arg_groundness(&self) -> &'static [bool] {
262 &[false, false]
263 }
264
265 fn apply(&self, lit: &crate::logic::Literal) -> Option<crate::logic::Literal> {
267 let a: f64 = lit.args[0].as_constant().and_then(|s| s.parse().ok())?;
268 let b: f64 = lit.args[1].as_constant().and_then(|s| s.parse().ok())?;
269 if $cond(a, b) {
270 Some(lit.clone())
271 } else {
272 None
273 }
274 }
275 }
276 };
277 }
278
279 define_number_comparison!(number_eq, |a, b| a == b);
280 define_number_comparison!(number_gt, |a, b| a > b);
281 define_number_comparison!(number_lt, |a, b| a < b);
282 define_number_comparison!(number_geq, |a, b| a >= b);
283 define_number_comparison!(number_leq, |a, b| a <= b);
284}
285
286mod semver {
287 use super::BuiltinPredicate;
288 use semver::{Comparator, Op, Version};
289
290 fn parse_partial_version(s: &str) -> Option<Version> {
291 if let Ok(v) = Version::parse(s) {
292 return Some(v);
293 }
294 let mut s = String::from(s);
295 s.push_str(".0");
296 if let Ok(v) = Version::parse(&s) {
297 return Some(v);
298 }
299 s.push_str(".0");
300 if let Ok(v) = Version::parse(&s) {
301 return Some(v);
302 }
303 return None;
304 }
305
306 macro_rules! define_semver_comparison {
307 ($name:ident, $cond:expr) => {
308 #[allow(non_camel_case_types)]
309 pub struct $name;
310 impl BuiltinPredicate for $name {
311 fn name(&self) -> &'static str {
312 stringify!($name)
313 }
314
315 fn kind(&self) -> crate::analysis::Kind {
316 crate::analysis::Kind::Logic
317 }
318
319 fn arg_groundness(&self) -> &'static [bool] {
320 &[false, false]
321 }
322
323 fn apply(&self, lit: &crate::logic::Literal) -> Option<crate::logic::Literal> {
325 let a: Version = lit.args[0]
326 .as_constant()
327 .and_then(|s| parse_partial_version(s))?;
328 let b: Comparator = lit.args[1]
329 .as_constant()
330 .and_then(|s| Comparator::parse(&format!("{}{}", $cond, s)).ok())?;
331 if b.matches(&a) {
332 Some(lit.clone())
333 } else {
334 None
335 }
336 }
337 }
338 };
339 }
340
341 define_semver_comparison!(semver_exact, "=");
342 define_semver_comparison!(semver_gt, ">");
343 define_semver_comparison!(semver_lt, "<");
344 define_semver_comparison!(semver_geq, ">=");
345 define_semver_comparison!(semver_leq, "<=");
346}
347
348macro_rules! intrinsic_predicate {
349 ($name:ident, $kind:expr, $($arg_groundness:expr),*) => {
350 #[allow(non_camel_case_types)]
351 pub struct $name;
352 impl BuiltinPredicate for $name {
353 fn name(&self) -> &'static str {
354 stringify!($name)
355 }
356
357 fn kind(&self) -> Kind {
358 $kind
359 }
360
361 fn arg_groundness(&self) -> &'static [bool] {
362 &[$($arg_groundness),*]
363 }
364
365 fn apply(&self, lit: &Literal) -> Option<Literal> {
366 Some(lit.clone())
367 }
368 }
369 };
370}
371
372intrinsic_predicate!(run, crate::analysis::Kind::Layer, false);
373intrinsic_predicate!(from, crate::analysis::Kind::Image, false);
374intrinsic_predicate!(
375 _operator_copy_begin,
376 crate::analysis::Kind::Image,
377 false,
378 false,
379 false
380);
381intrinsic_predicate!(
382 _operator_copy_end,
383 crate::analysis::Kind::Image,
384 false,
385 false,
386 false
387);
388intrinsic_predicate!(
389 _operator_in_workdir_begin,
390 crate::analysis::Kind::Layer,
391 false,
392 false
393);
394intrinsic_predicate!(
395 _operator_in_workdir_end,
396 crate::analysis::Kind::Layer,
397 false,
398 false
399);
400intrinsic_predicate!(
401 _operator_set_workdir_begin,
402 crate::analysis::Kind::Image,
403 false,
404 false
405);
406intrinsic_predicate!(
407 _operator_set_workdir_end,
408 crate::analysis::Kind::Image,
409 false,
410 false
411);
412intrinsic_predicate!(
413 _operator_set_entrypoint_begin,
414 crate::analysis::Kind::Image,
415 false,
416 false
417);
418intrinsic_predicate!(
419 _operator_set_entrypoint_end,
420 crate::analysis::Kind::Image,
421 false,
422 false
423);
424intrinsic_predicate!(
425 _operator_set_cmd_begin,
426 crate::analysis::Kind::Image,
427 false,
428 false
429);
430intrinsic_predicate!(
431 _operator_set_cmd_end,
432 crate::analysis::Kind::Image,
433 false,
434 false
435);
436intrinsic_predicate!(
437 _operator_set_label_begin,
438 crate::analysis::Kind::Image,
439 false,
440 false,
441 false
442);
443intrinsic_predicate!(
444 _operator_set_label_end,
445 crate::analysis::Kind::Image,
446 false,
447 false,
448 false
449);
450intrinsic_predicate!(
451 _operator_set_env_begin,
452 crate::analysis::Kind::Image,
453 false,
454 false,
455 false
456);
457intrinsic_predicate!(
458 _operator_set_env_end,
459 crate::analysis::Kind::Image,
460 false,
461 false,
462 false
463);
464intrinsic_predicate!(
465 _operator_in_env_begin,
466 crate::analysis::Kind::Layer,
467 false,
468 false,
469 false
470);
471intrinsic_predicate!(
472 _operator_in_env_end,
473 crate::analysis::Kind::Layer,
474 false,
475 false,
476 false
477);
478intrinsic_predicate!(
479 _operator_append_path_begin,
480 crate::analysis::Kind::Image,
481 false,
482 false
483);
484intrinsic_predicate!(
485 _operator_append_path_end,
486 crate::analysis::Kind::Image,
487 false,
488 false
489);
490intrinsic_predicate!(
491 _operator_set_user_begin,
492 crate::analysis::Kind::Image,
493 false,
494 false
495);
496intrinsic_predicate!(
497 _operator_set_user_end,
498 crate::analysis::Kind::Image,
499 false,
500 false
501);
502intrinsic_predicate!(copy, crate::analysis::Kind::Layer, false, false);
503intrinsic_predicate!(_operator_merge_begin, crate::analysis::Kind::Layer, false);
504intrinsic_predicate!(_operator_merge_end, crate::analysis::Kind::Layer, false);
505
506macro_rules! select_builtins {
508 ( $lit:expr, $( $x:expr ),+, ) => {{
509 let mut has_ground_mismatch = false;
510 $(
511 match $x.select($lit) {
512 SelectBuiltinResult::Match => return (SelectBuiltinResult::Match, Some(&$x)),
513 SelectBuiltinResult::GroundnessMismatch => {
514 has_ground_mismatch = true;
515 },
516 _ => {}
517 }
518 );+
519 if has_ground_mismatch {
520 return (SelectBuiltinResult::GroundnessMismatch, None);
521 } else {
522 return (SelectBuiltinResult::NoMatch, None);
523 }
524 }};
525}
526
527pub fn select_builtin<'a>(
528 lit: &Literal,
529) -> (SelectBuiltinResult, Option<&'a dyn BuiltinPredicate>) {
530 select_builtins!(
531 lit,
532 string_concat::StringConcat1,
533 string_concat::StringConcat2,
534 string_concat::StringConcat3,
535 run,
536 from,
537 _operator_copy_begin,
538 _operator_copy_end,
539 _operator_in_workdir_begin,
540 _operator_in_workdir_end,
541 _operator_set_workdir_begin,
542 _operator_set_workdir_end,
543 _operator_set_entrypoint_begin,
544 _operator_set_entrypoint_end,
545 _operator_set_cmd_begin,
546 _operator_set_cmd_end,
547 _operator_set_label_begin,
548 _operator_set_label_end,
549 _operator_set_env_begin,
550 _operator_set_env_end,
551 _operator_in_env_begin,
552 _operator_in_env_end,
553 _operator_append_path_begin,
554 _operator_append_path_end,
555 _operator_set_user_begin,
556 _operator_set_user_end,
557 copy,
558 equality::StringEq1,
559 equality::StringEq2,
560 _operator_merge_begin,
561 _operator_merge_end,
562 number::number_eq,
563 number::number_gt,
564 number::number_lt,
565 number::number_geq,
566 number::number_leq,
567 semver::semver_exact,
568 semver::semver_gt,
569 semver::semver_lt,
570 semver::semver_geq,
571 semver::semver_leq,
572 )
573}
574
575lazy_static! {
576 pub static ref OPERATOR_KIND_MAP: HashMap<&'static str, (Kind, Kind)> = {
578 let mut m = HashMap::new();
579 m.insert("copy", (Kind::Image, Kind::Layer));
580 m.insert("set_env", (Kind::Image, Kind::Image));
581 m.insert("set_entrypoint", (Kind::Image, Kind::Image));
582 m.insert("set_cmd", (Kind::Image, Kind::Image));
583 m.insert("set_workdir", (Kind::Image, Kind::Image));
584 m.insert("set_label", (Kind::Image, Kind::Image));
585 m.insert("set_user", (Kind::Image, Kind::Image));
586 m.insert("append_path", (Kind::Image, Kind::Image));
587 m.insert("in_workdir", (Kind::Layer, Kind::Layer));
588 m.insert("in_env", (Kind::Layer, Kind::Layer));
589 m.insert("merge", (Kind::Layer, Kind::Layer));
590 m
591 };
592}
593
594#[cfg(test)]
595mod test {
596 use crate::{analysis::Kind, builtin::SelectBuiltinResult, logic::IRTerm};
597
598 #[test]
599 pub fn test_select() {
600 use crate::logic::{Literal, Predicate};
601
602 let lit = Literal {
603 positive: true,
604 position: None,
605 predicate: Predicate("run".to_owned()),
606 args: vec![IRTerm::Constant("hello".to_owned())],
607 };
608 let b = super::select_builtin(&lit);
609 assert!(b.0.is_match());
610 let b = b.1.unwrap();
611 assert_eq!(b.name(), "run");
612 assert_eq!(b.kind(), Kind::Layer);
613 assert_eq!(b.apply(&lit), Some(lit));
614
615 let lit = Literal {
616 positive: true,
617 position: None,
618 predicate: Predicate("string_concat".to_owned()),
619 args: vec![
620 IRTerm::Constant("hello".to_owned()),
621 IRTerm::Constant("world".to_owned()),
622 IRTerm::UserVariable("X".to_owned()),
623 ],
624 };
625 let b = super::select_builtin(&lit);
626 assert!(b.0.is_match());
627 let b = b.1.unwrap();
628 assert_eq!(b.name(), "string_concat");
629 assert_eq!(b.kind(), Kind::Logic);
630 assert_eq!(
631 b.apply(&lit),
632 Some(Literal {
633 positive: true,
634 position: None,
635 predicate: Predicate("string_concat".to_owned()),
636 args: vec![
637 IRTerm::Constant("hello".to_owned()),
638 IRTerm::Constant("world".to_owned()),
639 IRTerm::Constant("helloworld".to_owned()),
640 ]
641 })
642 );
643
644 let lit = Literal {
645 positive: true,
646 position: None,
647 predicate: Predicate("xxx".to_owned()),
648 args: vec![IRTerm::Constant("hello".to_owned())],
649 };
650 let b = super::select_builtin(&lit);
651 assert_eq!(b.0, SelectBuiltinResult::NoMatch);
652 }
653
654 #[test]
655 pub fn test_from_run() {
656 use crate::logic::{Clause, Literal, Predicate};
657
658 let rules = vec![Clause {
659 head: Literal {
660 positive: true,
661 position: None,
662 predicate: Predicate("a".to_owned()),
663 args: vec![],
664 },
665 body: vec![
666 Literal {
667 positive: true,
668 position: None,
669 predicate: Predicate("from".to_owned()),
670 args: vec![IRTerm::Constant("ubuntu".to_owned())],
671 },
672 Literal {
673 positive: true,
674 position: None,
675 predicate: Predicate("run".to_owned()),
676 args: vec![IRTerm::Constant("rm -rf /".to_owned())],
677 },
678 ],
679 }];
680 let goals = vec![Literal {
681 positive: true,
682 position: None,
683 predicate: Predicate("a".to_owned()),
684 args: vec![],
685 }];
686 let tree = crate::sld::sld(&rules, &goals, 100, true).tree;
687 let solutions = crate::sld::solutions(&tree);
688 assert_eq!(solutions.len(), 1);
689 assert!(solutions.contains(&goals));
690 let proof = crate::sld::proofs(&tree, &rules, &goals);
691 assert_eq!(proof.len(), 1);
692 }
693
694 #[test]
695 pub fn test_number_and_semver_compare() {
696 use crate::logic::{Literal, Predicate};
697
698 let tests = vec![
699 (
700 "number_eq",
701 vec![
702 ("1", "1"),
703 ("1.0", "1"),
704 ("0.0", "0.0"),
705 ("0", "-0"),
706 ("0.2", "0.2"),
707 ("1e-10", "1e-10"),
708 ("1e100", "1e100"),
709 ("42.0", "42.0"),
710 ],
711 vec![
712 ("0", "1"),
713 ("0", "0.01"),
714 ("1", "-1"),
715 ("1e-10", "0"),
716 ("42.0", "-273.15"),
717 ("NaN", "NaN"),
718 ],
719 ),
720 (
721 "number_gt",
722 vec![
723 ("1", "0"),
724 ("1e-10", "0"),
725 ("42.0", "-273.15"),
726 ("1e100", "0"),
727 ],
728 vec![
729 ("42.0", "42.0"),
730 ("42.0", "42.1"),
731 ("0", "1e-10"),
732 ("NaN", "NaN"),
733 ],
734 ),
735 (
736 "number_lt",
737 vec![
738 ("0", "1"),
739 ("0", "1e-10"),
740 ("-273.15", "42.0"),
741 ("0", "1e100"),
742 ],
743 vec![
744 ("42.0", "42.0"),
745 ("42.1", "42.0"),
746 ("1e-10", "0"),
747 ("NaN", "NaN"),
748 ],
749 ),
750 (
751 "number_geq",
752 vec![
753 ("1", "0"),
754 ("1e-10", "0"),
755 ("42.0", "-273.15"),
756 ("1e100", "0"),
757 ("42.0", "42.0"),
758 ("42", "42.0"),
759 ],
760 vec![("42.0", "42.1"), ("0", "1e-10"), ("NaN", "NaN")],
761 ),
762 (
763 "number_leq",
764 vec![
765 ("0", "1"),
766 ("0", "1e-10"),
767 ("-273.15", "42.0"),
768 ("0", "1e100"),
769 ("42.0", "42.0"),
770 ],
771 vec![("42.1", "42.0"), ("1e-10", "0"), ("NaN", "NaN")],
772 ),
773 (
774 "semver_exact",
775 vec![
776 ("1.0.0", "1.0.0"),
777 ("1.0.0", "1.0"),
778 ("1.0.0", "1"),
779 ("0.0.0", "0.0.0"),
780 ("0.1.0-alpha", "0.1.0-alpha"),
781 ("1", "1"),
783 ("1.0", "1"),
784 ("1", "1.0"),
785 ("0.2", "0.2"),
786 ("1.0.1", "1.0"),
787 ],
788 vec![
789 ("1.0.0", "1.0.1"),
790 ("1.0.0", "1.1"),
791 ("1.0.0", "2"),
792 ("1.0.0", "0"),
793 ("0.0.0", "0.0.1"),
794 ("1", "1.2"),
795 ("0", "-0"),
796 ("0.1.0-beta", "0.1.0-alpha"),
797 ],
798 ),
799 (
800 "semver_gt",
801 vec![
802 ("3.2.1", "1.2.3"),
803 ("1.2.3", "1.2.1"),
804 ("0.1.0-beta", "0.1.0-alpha"),
805 ("1", "0"),
806 ("1.1", "1.0"),
807 ("1.0.1", "1.0.0"),
808 ],
809 vec![
810 ("1.1", "1"),
811 ("1.1", "1.2"),
812 ("1.1", "1.1"),
813 ("3.2.1", "3.4.1"),
814 ],
815 ),
816 (
817 "semver_lt",
818 vec![
819 ("1.2.3", "3.2.1"),
820 ("3.2.1", "3.4.1"),
821 ("1.1", "1.2"),
822 ("1.1", "1.1.1"),
823 ],
824 vec![
825 ("1", "0"),
826 ("1.1", "1.0"),
827 ("1.1", "1"),
828 ("1.1", "1"),
829 ("1.1", "1.1"),
830 ("1.1.0", "1.1.0"),
831 ("1.1", "1.1.0"),
832 ("1.1.1", "1.1.1"),
833 ],
834 ),
835 (
836 "semver_geq",
837 vec![
838 ("1.0.1", "1.0.0"),
839 ("1.0.1", "1.0"),
840 ("1.2.3", "1.2.1"),
841 ("1.1", "1.0"),
842 ("1.1", "1"),
843 ("1.1", "1.1"),
844 ("1.1.0", "1.1.0"),
845 ("1.1", "1.1.0"),
846 ("1.1.1", "1.1.1"),
847 ],
848 vec![("1.2.3", "3.2.1"), ("1.1", "1.2"), ("1.1", "1.1.1")],
849 ),
850 (
851 "semver_leq",
852 vec![
853 ("1.2.3", "3.2.1"),
854 ("1.1", "1.2"),
855 ("1.1", "1.1.1"),
856 ("1.1", "1"),
857 ("1.1", "1.1"),
858 ("1.1.0", "1.1.0"),
859 ("1.1", "1.1.0"),
860 ("1.1.1", "1.1.1"),
861 ],
862 vec![("1", "0"), ("1.1", "1.0"), ("1.2.3", "1.2.1")],
863 ),
864 ];
865
866 for (name, true_cases, false_cases) in tests.into_iter() {
867 for (left, right) in true_cases.into_iter() {
868 let lit = Literal {
869 positive: true,
870 position: None,
871 predicate: Predicate(name.to_owned()),
872 args: vec![
873 IRTerm::Constant(left.to_owned()),
874 IRTerm::Constant(right.to_owned()),
875 ],
876 };
877 let b = super::select_builtin(&lit);
878 assert!(b.0.is_match());
879 let b = b.1.unwrap();
880 assert_eq!(b.name(), name);
881 assert_eq!(b.kind(), Kind::Logic);
882 if b.apply(&lit).as_ref() != Some(&lit) {
883 panic!("Expected {} to resolve (got false)", lit);
884 }
885 }
886 for (left, right) in false_cases.into_iter() {
887 let lit = Literal {
888 positive: true,
889 position: None,
890 predicate: Predicate(name.to_owned()),
891 args: vec![
892 IRTerm::Constant(left.to_owned()),
893 IRTerm::Constant(right.to_owned()),
894 ],
895 };
896 let b = super::select_builtin(&lit);
897 assert!(b.0.is_match());
898 let b = b.1.unwrap();
899 assert_eq!(b.name(), name);
900 assert_eq!(b.kind(), Kind::Logic);
901 if b.apply(&lit) != None {
902 panic!("Expected {} to fail (but resolved)", lit);
903 }
904 }
905 }
906 }
907}