1use super::*;
22use hyperon_atom::matcher::{Bindings, BindingsSet, apply_bindings_to_atom_move};
23use hyperon_space::DynSpace;
24
25use std::fmt::{Display, Debug};
26use itertools::Itertools;
27use hyperon_atom::gnd::number::Number;
28
29fn typeof_query(atom: &Atom, typ: &Atom) -> Atom {
30 Atom::expr(vec![HAS_TYPE_SYMBOL, atom.clone(), typ.clone()])
31}
32
33fn isa_query(sub_type: &Atom, super_type: &Atom) -> Atom {
34 Atom::expr(vec![SUB_TYPE_SYMBOL, sub_type.clone(), super_type.clone()])
35}
36
37fn query_has_type(space: &DynSpace, sub_type: &Atom, super_type: &Atom) -> BindingsSet {
38 space.borrow().query(&typeof_query(sub_type, super_type))
39}
40
41fn query_super_types(space: &DynSpace, sub_type: &Atom) -> Vec<Atom> {
42 let var_x = VariableAtom::new("X").make_unique();
44 let super_types = space.borrow().query(&isa_query(&sub_type, &Atom::Variable(var_x.clone())));
45 let atom_x = Atom::Variable(var_x);
46 super_types.into_iter().map(|bindings| { apply_bindings_to_atom_move(atom_x.clone(), &bindings) }).collect()
47}
48
49fn add_super_types(space: &DynSpace, sub_types: &mut Vec<Atom>, from: usize) {
50 let mut types = Vec::new();
51 sub_types.iter().skip(from).for_each(|typ| {
52 for typ in query_super_types(space, typ) {
53 if !sub_types.contains(&typ) {
54 types.push(typ);
55 }
56 }
57 });
58 if !types.is_empty() {
59 let from = sub_types.len();
60 sub_types.append(&mut types.clone());
61 add_super_types(space, sub_types, from);
62 }
63}
64
65fn check_arg_types(actual: &[Vec<AtomType>], meta: &[Vec<Atom>], types: &mut Vec<AtomType>, expected: &[Atom], ret_typ: &Atom, atom: &Atom, fn_type_atom: &Atom) -> () {
66 if actual.len() != expected.len() {
67 types.push(AtomType::error(fn_type_atom.clone(), Atom::expr([ERROR_SYMBOL, atom.clone(), INCORRECT_NUMBER_OF_ARGUMENTS_SYMBOL])));
68 } else {
69 check_arg_types_internal(actual, meta, expected, Bindings::new(), types, fn_type_atom, atom, ret_typ, actual.len());
70 }
71}
72
73fn check_arg_types_internal(actual: &[Vec<AtomType>], meta: &[Vec<Atom>], expected: &[Atom], bindings: Bindings, types: &mut Vec<AtomType>, fn_type_atom: &Atom, atom: &Atom, ret_typ: &Atom, start_len: usize) -> () {
74 log::trace!("check_arg_types: actual: {}, expected: {}",
75 actual.iter().format_with(", ", |v, f| f(&format_args!("{}", v.iter().format(", ")))),
76 expected.iter().format(", "));
77
78 match (actual, meta, expected) {
79 ([actual, actual_tail @ ..], [meta, meta_tail @ ..], [expected, expected_tail @ ..]) => {
80 let undefined_or_meta = actual.is_empty()
81 || *expected == ATOM_TYPE_UNDEFINED
82 || meta.contains(expected);
83 let matches: &mut dyn Iterator<Item=Bindings> = if undefined_or_meta {
84 &mut std::iter::once(bindings)
85 } else {
86 &mut actual.into_iter()
87 .flat_map(|typ| match_reducted_types(typ.as_atom(), expected))
88 .flat_map(|b| b.merge(&bindings))
89 };
90 let mut matches = matches.peekable();
91 if matches.peek().is_none() {
92 let idx = (start_len - actual_tail.len()) as i64;
93 for typ in actual {
94 let error = Atom::expr([ERROR_SYMBOL, atom.clone(), Atom::expr([BAD_ARG_TYPE_SYMBOL, Atom::gnd(Number::Integer(idx)), expected.clone(), typ.as_atom().clone()])]);
95 types.push(AtomType::error(fn_type_atom.clone(), error));
96 }
97 } else {
98 for b in matches {
99 check_arg_types_internal(actual_tail, meta_tail, expected_tail, b, types, fn_type_atom, atom, ret_typ, start_len);
100 }
101 }
102 },
103 ([], [], []) => {
104 let typ = AtomType::application(apply_bindings_to_atom_move(ret_typ.clone(), &bindings));
105 types.push(typ);
106 },
107 _ => unreachable!(),
108 };
109 log::trace!("check_arg_types: actual: {}, expected: {}",
110 actual.iter().format_with(", ", |v, f| f(&format_args!("{}", v.iter().format(", ")))),
111 expected.iter().format(", "));
112}
113
114#[inline]
127pub fn is_func(typ: &Atom) -> bool {
128 match typ {
129 Atom::Expression(expr) => {
130 (expr.children().first() == Some(&ARROW_SYMBOL)) && (expr.children().len() > 1)
131 },
132 _ => false,
133 }
134}
135
136fn query_types(space: &DynSpace, atom: &Atom) -> Vec<Atom> {
137 let var_x = VariableAtom::new("X").make_unique();
138 let types = query_has_type(space, atom, &Atom::Variable(var_x.clone()));
139 let atom_x = Atom::Variable(var_x);
140 let mut types = types.into_iter().filter_map(|bindings| {
141 let atom = apply_bindings_to_atom_move(atom_x.clone(), &bindings);
142 if atom_x == atom || atom == ATOM_TYPE_UNDEFINED {
143 None
144 } else {
145 Some(atom)
146 }
147 }).collect();
148 add_super_types(space, &mut types, 0);
149 types
150}
151
152fn get_arg_types<'a>(fn_typ: &'a AtomType) -> (&'a [Atom], &'a Atom) {
154 match fn_typ.as_atom() {
155 Atom::Expression(expr) => {
156 let children = expr.children();
157 match children {
158 [op, args @ .., res] if *op == ARROW_SYMBOL => (args, res),
159 _ => panic!("Incorrect function type: {}", fn_typ)
160 }
161 },
162 _ => panic!("Incorrect function type: {}", fn_typ)
163 }
164}
165
166fn get_args(expr: &ExpressionAtom) -> &[Atom] {
167 &expr.children()[1..]
168}
169
170#[derive(Debug, PartialEq, Clone)]
171pub struct AtomType {
172 typ: Atom,
173 is_function: bool,
174 info: TypeInfo,
175}
176
177#[derive(Debug, PartialEq, Clone)]
178enum TypeInfo {
179 Application,
180 ApplicationError {
181 error: Atom,
182 },
183 Value,
184}
185
186impl AtomType {
187
188 #[inline]
189 pub fn undefined() -> Self {
190 Self {
191 typ: ATOM_TYPE_UNDEFINED,
192 is_function: false,
193 info: TypeInfo::Value,
194 }
195 }
196
197 #[inline]
198 pub fn value(typ: Atom) -> Self {
199 let is_function = is_func(&typ);
200 Self {
201 typ,
202 is_function,
203 info: TypeInfo::Value,
204 }
205 }
206
207 #[inline]
208 pub fn application(typ: Atom) -> Self {
209 let is_function = is_func(&typ);
210 Self {
211 typ,
212 is_function,
213 info: TypeInfo::Application,
214 }
215 }
216
217 #[inline]
218 pub fn error(typ: Atom, error: Atom) -> Self {
219 let is_function = is_func(&typ);
220 Self {
221 typ,
222 is_function,
223 info: TypeInfo::ApplicationError {
224 error,
225 }
226 }
227 }
228
229 #[inline]
230 pub fn is_error(&self) -> bool {
231 matches!(self.info, TypeInfo::ApplicationError{..})
232 }
233
234 #[inline]
235 pub fn is_valid(&self) -> bool {
236 !self.is_error()
237 }
238
239 #[inline]
240 pub fn is_function(&self) -> bool {
241 self.is_function
242 }
243 #[inline]
244 pub fn is_application(&self) -> bool {
245 matches!(self.info, TypeInfo::Application)
246 }
247
248 #[inline]
249 pub fn as_atom(&self) -> &Atom {
250 &self.typ
251 }
252
253 #[inline]
254 pub fn into_atom(self) -> Atom {
255 self.typ
256 }
257
258 #[inline]
259 pub fn get_error(&self) -> Option<&Atom> {
260 match &self.info {
261 TypeInfo::ApplicationError { error } => Some(error),
262 _ => None,
263 }
264 }
265
266 #[inline]
267 pub fn into_error(self) -> Option<Atom> {
268 match self.info {
269 TypeInfo::ApplicationError { error } => Some(error),
270 _ => None,
271 }
272 }
273
274 #[inline]
275 pub fn into_error_unchecked(self) -> Atom {
276 match self.info {
277 TypeInfo::ApplicationError { error } => error,
278 _ => panic!("Unexpected state"),
279 }
280 }
281
282 #[inline]
283 pub fn into_atom_or_error(self) -> Result<Atom, Atom> {
284 match self.info {
285 TypeInfo::ApplicationError { error } => Err(error),
286 _ => Ok(self.typ),
287 }
288 }
289}
290
291impl Display for AtomType {
292 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
293 write!(f, "{}(", self.typ)
294 .and_then(|r| if self.is_application() { write!(f, "A") } else { Ok(r) })
295 .and_then(|r| if self.is_error() { write!(f, "E") } else { Ok(r) })
296 .and_then(|_| write!(f, ")"))
297 }
298}
299
300pub fn get_atom_types(space: &DynSpace, atom: &Atom) -> Vec<AtomType> {
328 let atom_types = get_atom_types_internal(space, atom);
329 if atom_types.is_empty() {
330 vec![AtomType::undefined()]
331 } else {
332 atom_types
333 }
334}
335
336struct ExprTypeInfo {
337 op_value_types: Vec<AtomType>,
338 op_func_types: Vec<AtomType>,
339 arg_types: Vec<Vec<AtomType>>,
340}
341
342impl ExprTypeInfo {
343 fn new(space: &DynSpace, expr: &ExpressionAtom) -> Self {
344 let (op, args) = expr.children().split_first().unwrap();
345 let op_types = get_atom_types_internal(space, op);
346 let mut op_func_types = Vec::with_capacity(op_types.len());
347 let mut op_value_types = Vec::with_capacity(op_types.len());
348 op_types.into_iter().for_each(|t| {
349 if t.is_function() {
350 op_func_types.push(t);
351 } else {
352 op_value_types.push(t);
353 }
354 });
355 let arg_types: Vec<Vec<AtomType>> = args.iter()
356 .map(|a| get_atom_types_internal(space, a)).collect();
357 Self{ op_value_types, op_func_types, arg_types }
368 }
369
370 #[inline]
371 fn arity(&self) -> usize {
372 self.arg_types.len() + 1
373 }
374}
375
376fn get_atom_types_internal(space: &DynSpace, atom: &Atom) -> Vec<AtomType> {
377 log::trace!("get_atom_types_internal: atom: {}", atom);
378 let types = match atom {
379 Atom::Variable(_) => vec![],
387 Atom::Grounded(gnd) => {
388 let typ = gnd.type_();
389 if typ == ATOM_TYPE_UNDEFINED {
390 vec![]
391 } else {
392 vec![AtomType::value(make_variables_unique(gnd.type_()))]
393 }
394 },
395 Atom::Symbol(_) => query_types(space, atom).into_iter()
396 .map(AtomType::value).collect(),
397 Atom::Expression(expr) if expr.children().len() == 0 => vec![],
400 Atom::Expression(expr) => {
401 let type_info = ExprTypeInfo::new(space, expr);
402 let mut types = get_tuple_types(space, atom, &type_info);
403 let applications = get_application_types(atom, expr, type_info);
404 types.extend(applications.into_iter());
405 types
406 },
407 };
408 log::debug!("get_atom_types_internal: return atom {} types {}", atom, types.iter().format(", "));
409 types
410}
411
412struct TupleIndex<'a> {
413 type_info: &'a ExprTypeInfo,
414 index: Vec<usize>,
415 max: Vec<usize>,
416 size: usize,
417}
418
419impl<'a> TupleIndex<'a> {
420 fn new(type_info: &'a ExprTypeInfo) -> Option<Self> {
421 let n_of_types = type_info.arg_types.iter()
422 .fold(type_info.op_value_types.len(), |n, types| n * types.len());
423 if n_of_types == 0 {
424 return None
425 }
426 let arity = type_info.arity();
427 let mut max = Vec::with_capacity(arity);
428 max.push(type_info.op_value_types.len());
429 max.extend(type_info.arg_types.iter().map(Vec::len));
430 let mut index = vec![0; arity];
431 index[arity - 1] = usize::wrapping_sub(index[arity - 1], 1);
432 Some(Self{ type_info, index, max, size: n_of_types })
433 }
434
435 #[inline]
436 fn inc(&mut self) -> bool {
437 let mut i = self.index.len() - 1;
438 loop {
439 let d = usize::wrapping_add(self.index[i], 1);
440 if d < self.max[i] {
441 self.index[i] = d;
442 return true;
443 } else {
444 if i == 0 {
445 return false;
446 } else {
447 self.index[i] = 0;
448 i -= 1;
449 }
450 }
451 }
452 }
453
454 #[inline]
455 fn size(&self) -> usize {
456 self.size
457 }
458}
459
460impl<'a> Iterator for TupleIndex<'a> {
461 type Item = Vec<Atom>;
462 fn next(&mut self) -> Option<Self::Item> {
463 if self.inc() {
464 let arity = self.index.len();
465 let mut v = Vec::with_capacity(arity);
466 v.push(self.type_info.op_value_types[self.index[0]].as_atom().clone());
467 for i in 1..arity {
468 v.push(self.type_info.arg_types[i - 1][self.index[i]].as_atom().clone());
469 }
470 Some(v)
471 } else {
472 None
473 }
474 }
475}
476
477fn get_tuple_types(space: &DynSpace, atom: &Atom, type_info: &ExprTypeInfo) -> Vec<AtomType> {
478 let mut types = if let Some(index) = TupleIndex::new(type_info) {
479 let mut types = Vec::with_capacity(index.size());
480 index.for_each(|v| types.push(Atom::expr(v)));
481 types
482 } else {
483 vec![]
484 };
485
486 types.append(&mut query_types(space, atom));
487 add_super_types(space, &mut types, 0);
488 let types: Vec<AtomType> = types.into_iter().map(AtomType::value).collect();
490 log::trace!("get_tuple_types: tuple {} types {}", atom, types.iter().format(", "));
491 types
492}
493
494fn get_application_types(atom: &Atom, expr: &ExpressionAtom, type_info: ExprTypeInfo) -> Vec<AtomType> {
513 let args = get_args(expr);
514 let meta_arg_types: Vec<Vec<Atom>> = args.iter().map(|a| vec![get_meta_type(a), ATOM_TYPE_ATOM]).collect();
515 let mut types = Vec::with_capacity(type_info.op_func_types.len());
516 for fn_type in type_info.op_func_types.into_iter() {
517 let (expected, ret_typ) = get_arg_types(&fn_type);
518 let fn_type_atom = fn_type.as_atom();
519 check_arg_types(&type_info.arg_types, meta_arg_types.as_slice(), &mut types, expected, ret_typ, atom, fn_type_atom);
520 }
521 log::trace!("get_application_types: function application {} types {}", atom, types.iter().format(", "));
522 types
523}
524
525#[derive(Clone, PartialEq, Debug)]
526struct UndefinedTypeMatch { }
527
528impl Display for UndefinedTypeMatch {
529 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
530 write!(f, "{}", ATOM_TYPE_UNDEFINED)
531 }
532}
533
534impl Grounded for UndefinedTypeMatch {
535 fn type_(&self) -> Atom {
536 ATOM_TYPE_TYPE
537 }
538
539 fn as_match(&self) -> Option<&dyn CustomMatch> {
540 Some(self)
541 }
542}
543
544impl CustomMatch for UndefinedTypeMatch {
545 fn match_(&self, _other: &Atom) -> matcher::MatchResultIter {
546 Box::new(std::iter::once(hyperon_atom::matcher::Bindings::new()))
547 }
548}
549
550pub fn match_reducted_types(left: &Atom, right: &Atom) -> matcher::MatchResultIter {
564 let left = replace_undefined_types(left);
565 let right = replace_undefined_types(right);
566 matcher::match_atoms(&left, &right)
567}
568
569fn replace_undefined_types(atom: &Atom) -> Atom {
570 let mut atom = atom.clone();
571 atom.iter_mut().filter(|atom| **atom == ATOM_TYPE_UNDEFINED)
572 .for_each(|atom| *atom = Atom::gnd(UndefinedTypeMatch{}));
573 atom
574}
575
576fn get_matched_types(space: &DynSpace, atom: &Atom, typ: &Atom) -> Vec<(Atom, Bindings)> {
577 let types = get_atom_types(space, atom);
578 types.into_iter().filter(AtomType::is_valid).flat_map(|t| {
579 let t = make_variables_unique(t.into_atom());
581 match_reducted_types(&t, typ).map(move |bindings| (t.clone(), bindings))
582 }).collect()
583}
584
585pub fn check_type(space: &DynSpace, atom: &Atom, typ: &Atom) -> bool {
603 check_meta_type(atom, typ) || !get_matched_types(space, atom, typ).is_empty()
604}
605
606pub fn get_meta_type(atom: &Atom) -> Atom {
607 match atom {
608 Atom::Symbol(_) => ATOM_TYPE_SYMBOL,
609 Atom::Variable(_) => ATOM_TYPE_VARIABLE,
610 Atom::Grounded(_) => ATOM_TYPE_GROUNDED,
611 Atom::Expression(_) => ATOM_TYPE_EXPRESSION,
612 }
613}
614
615fn check_meta_type(atom: &Atom, typ: &Atom) -> bool {
616 *typ == ATOM_TYPE_ATOM || *typ == get_meta_type(atom)
617}
618
619pub fn validate_atom(space: &DynSpace, atom: &Atom) -> bool {
638 get_atom_types_internal(space, atom).iter().all(AtomType::is_valid)
639}
640
641#[cfg(test)]
642mod tests {
643 use super::*;
644 use hyperon_atom::matcher::atoms_are_equivalent;
645 use crate::metta::runner::*;
646 use crate::metta::text::SExprParser;
647 use hyperon_common::assert_eq_no_order;
648 use hyperon_macros::metta;
649
650 macro_rules! typ {
651 ($($typ:tt)*) => { AtomType::value(metta!($($typ)*)) }
652 }
653 macro_rules! typ_app {
654 ($($typ:tt)*) => { AtomType::application(metta!($($typ)*)) }
655 }
656 macro_rules! typ_err {
657 ($typ:tt , $err:tt) => { AtomType::error(metta!($typ), metta!($err)) }
658 }
659
660 fn metta_space(text: &str) -> DynSpace {
661 let metta = Metta::new(Some(EnvBuilder::test_env()));
662 let mut space = GroundingSpace::new();
663 let mut parser = SExprParser::new(text);
664 while let Some(atom) = parser.parse(&*metta.tokenizer().borrow()).unwrap() {
665 space.add(atom);
666 }
667 space.into()
668 }
669
670 fn atom(atom_str: &str) -> Atom {
671 let metta = Metta::new(Some(EnvBuilder::test_env()));
672 let mut parser = SExprParser::new(atom_str);
673 let atom = parser.parse(&*metta.tokenizer().borrow()).unwrap().expect("Single atom is expected");
674 atom
675 }
676
677 fn grammar_space() -> DynSpace {
678 let mut space = GroundingSpace::new();
679 space.add(expr!(":" "answer" ("->" "Sent" "Sent")));
680 space.add(expr!(":<" "Quest" "Sent"));
681 space.add(expr!(":<" ("Aux" "Subj" "Verb" "Obj") "Quest"));
682 space.add(expr!(":<" "Pron" "Subj"));
683 space.add(expr!(":<" "NG" "Subj"));
684 space.add(expr!(":<" "Pron" "Obj"));
685 space.add(expr!(":<" "NG" "Obj"));
686 space.add(expr!(":<" ("Det" "Noun") "NG"));
687 space.add(expr!(":" "you" "Pron"));
688 space.add(expr!(":" "do" "Aux"));
689 space.add(expr!(":" "do" "Verb"));
690 space.add(expr!(":" "like" "Verb"));
691 space.add(expr!(":" "a" "Det"));
692 space.add(expr!(":" "pizza" "Noun"));
693 space.into()
694 }
695
696 #[test]
697 fn test_check_type() {
698 let mut space = GroundingSpace::new();
699 space.add(expr!(":" "do" "Verb"));
700 space.add(expr!(":" "do" "Aux"));
701 let space = space.into();
702
703 let aux = sym!("Aux");
704 let verb = sym!("Verb");
705
706 let nonsense = sym!("nonsense");
707 assert!(check_type(&space, &nonsense, &ATOM_TYPE_UNDEFINED));
708 assert!(check_type(&space, &nonsense, &aux));
709
710 let _do = sym!("do");
711 assert!(check_type(&space, &_do, &ATOM_TYPE_UNDEFINED));
712 assert!(check_type(&space, &_do, &aux));
713 assert!(check_type(&space, &_do, &verb));
714 assert!(!check_type(&space, &_do, &sym!("Noun")));
715
716 }
717
718 #[test]
719 fn test_check_expr_type() {
720 let mut space = GroundingSpace::new();
721 space.add(expr!(":" "i" "Pron"));
722 space.add(expr!(":" "like" "Verb"));
723 space.add(expr!(":" "music" "Noun"));
724 space.add(expr!(":" ("do" "you" "like" "music") "Quest"));
725 space.add(expr!(":<" ("Pron" "Verb" "Noun") "Statement"));
726 let space = space.into();
727
728 let i_like_music = expr!("i" "like" "music");
729 assert!(check_type(&space, &i_like_music, &ATOM_TYPE_UNDEFINED));
730 assert!(check_type(&space, &i_like_music, &expr!("Pron" "Verb" "Noun")));
731 assert!(check_type(&space, &i_like_music, &sym!("Statement")));
732
733 assert!(check_type(&space, &expr!("do" "you" "like" "music"), &sym!("Quest")));
734 }
735
736 #[test]
737 fn nested_type() {
738 let space = metta_space("
739 (: a A)
740 (:< A B)
741 (:< B C)
742 (:< C D)
743 ");
744
745 assert!(check_type(&space, &atom("a"), &atom("D")));
746 }
747
748 #[test]
749 fn nested_loop_type() {
750 let space = metta_space("
751 (:< B A)
752 (: a A)
753 (:< A B)
754 (:< B C)
755 ");
756
757 assert!(check_type(&space, &atom("a"), &atom("C")));
758 }
759
760 #[test]
761 fn test_validate_atom() {
762 let space = grammar_space();
763 let expr = expr!("answer" ("do" "you" "like" ("a" "pizza")));
764
765 assert!(validate_atom(&space, &expr));
766 }
767
768 #[test]
769 fn validate_symbol() {
770 let space = DynSpace::new(GroundingSpace::new());
771 assert!(validate_atom(&space, &sym!("a")));
772 }
773
774 #[test]
775 fn simple_types() {
776 let space = metta_space("
777 (: blue Color)
778 (: balloon Object)
779 ");
780
781 assert!(check_type(&space, &atom("(blue balloon)"),
782 &atom("(Color Object)")));
783 }
784
785 #[test]
786 fn arrow_type() {
787 let space = metta_space("
788 (: a (-> B A))
789 ");
790
791 assert!(check_type(&space, &atom("a"), &atom("(-> B A)")));
792 }
793
794 #[test]
795 fn arrow_allows_specific_type() {
796 let space = metta_space("
797 (: a (-> B A))
798 (: b B)
799 (: c C)
800 ");
801
802 assert!(validate_atom(&space, &atom("(a b)")));
803 assert!(check_type(&space, &atom("(a b)"), &ATOM_TYPE_UNDEFINED));
804 assert!(!validate_atom(&space, &atom("(a c)")));
805 assert!(!check_type(&space, &atom("(a c)"), &ATOM_TYPE_UNDEFINED));
806 }
807
808 #[test]
809 fn validate_basic_expr() {
810 let space = DynSpace::new(GroundingSpace::new());
811 assert!(validate_atom(&space, &expr!({5})));
812 assert!(validate_atom(&space, &expr!("+" {3} {5})));
813 assert!(validate_atom(&space, &expr!("=" ("f" x) x)));
814 }
815
816 #[test]
817 fn simple_dep_types() {
818 let space = metta_space("
819 (: = (-> $t $t Prop))
820 (: Entity Prop)
821 (: Human (-> Entity Prop))
822 (: Socrates Entity)
823 (: (Human Socrates) Prop)
824 (: Plato Entity)
825 (: Mortal (-> Entity Prop))
826 (: HumansAreMortal (-> (Human $t) (Mortal $t)))
827 (: Time NotEntity)
828 (: SocratesIsHuman (Human Socrates))
829 (: SocratesIsMortal (Mortal Socrates))
830 ");
831 let t = &atom("Prop");
832 assert!(check_type(&space, &atom("(Human Socrates)"), t));
833 assert!(check_type(&space, &atom("(Human Plato)"), t));
834 assert!(!check_type(&space, &atom("(Human Time)"), t));
835 assert!(!validate_atom(&space, &atom("(Human Time)")));
836 assert!(!check_type(&space, &atom("(Human Time)"), &atom("((-> Entity Prop) NotEntity)")));
837 assert!(check_type(&space, &atom("(= Socrates Socrates)"), t));
838 assert!(check_type(&space, &atom("(= Socrates Plato)"), t));
839 assert!(check_type(&space, &atom("(= Socrates Untyped)"), t));
840 assert!(!check_type(&space, &atom("(= Socrates Time)"), t));
841
842 assert!(validate_atom(&space, &atom("(HumansAreMortal SocratesIsHuman)")));
843 assert!(!validate_atom(&space, &atom("(HumansAreMortal (Human Socrates))")));
844 assert!(!validate_atom(&space, &atom("(HumansAreMortal (Human Plato))")));
845 assert!(!validate_atom(&space, &atom("(HumansAreMortal (Human Time))")));
846 assert!(!validate_atom(&space, &atom("(HumansAreMortal Human)")));
847 assert!(!check_type(&space, &atom("(HumansAreMortal (Human Socrates))"),
848 &atom("(Mortal Socrates)")));
849 assert!(check_type(&space, &atom("(HumansAreMortal SocratesIsHuman)"),
850 &atom("(Mortal Socrates)")));
851
852 assert!(!validate_atom(&space, &atom("(= SocratesIsHuman (Human Socrates))")));
853 assert!(!validate_atom(&space, &atom("(= SocratesIsHuman (Human Plato))")));
854 assert!(!check_type(&space, &atom("(= SocratesIsHuman (Human Socrates))"), t));
855 assert!(!validate_atom(&space, &atom("(= SocratesIsHuman (Human Time))")));
856 assert!(validate_atom(&space, &atom("(= SocratesIsMortal (HumansAreMortal SocratesIsHuman))")));
857 assert!(validate_atom(&space, &atom("(= (Mortal Socrates) (Mortal Plato))")));
858 }
859
860 #[test]
861 fn dep_types_prop() {
862 let space = metta_space("
863 (: Sam Entity)
864 (: Frog (-> Entity Prop))
865 (: Green (-> Entity Prop))
866 (: Croaks (-> Entity Prop))
867 (: GreenAndCroaksIsFrog (-> (Green $t) (Croaks $t) (Frog $t)))
868 (: SamIsGreen (Green Sam))
869 (: SamCroaks (Croaks Sam))
870 ");
871 assert!(validate_atom(&space, &atom("(GreenAndCroaksIsFrog SamIsGreen SamCroaks)")));
872 assert!(check_type(&space, &atom("(GreenAndCroaksIsFrog SamIsGreen SamCroaks)"),
873 &atom("(Frog Sam)")));
874 }
875
876 #[test]
877 fn arrow_allows_undefined_type() {
878 let space = metta_space("
879 (: a (-> B A))
880 ");
881
882 assert!(validate_atom(&space, &atom("(a b)")));
883 }
884
885 #[test]
886 fn arrow_has_type_of_returned_value() {
887 let space = metta_space("
888 (: a (-> B A))
889 (: b B)
890 ");
891
892 assert!(check_type(&space, &atom("(a b)"), &atom("A")));
893 }
894
895 #[test]
896 fn nested_arrow_type() {
897 let space = metta_space("
898 (: a (-> B A))
899 (: h (-> (-> B A) C))
900 ");
901
902 assert!(validate_atom(&space, &atom("(h a)")));
903 }
904
905 #[test]
906 fn nested_return_type() {
907 let space = metta_space("
908 (: a (-> B A))
909 (: b B)
910 (: h (-> A C))
911 ");
912
913 assert!(validate_atom(&space, &atom("(h (a b))")));
914 }
915
916 #[test]
917 fn validate_non_functional_expression() {
918 let space = metta_space("
919 (: a A)
920 (: b B)
921 ");
922
923 assert!(validate_atom(&space, &atom("(a b)")));
924 }
925
926 #[test]
927 fn check_type_non_functional_expression() {
928 let space = metta_space("
929 (: a (-> C D))
930 (: a A)
931 (: b B)
932 ");
933
934 assert!(check_type(&space, &atom("(a b)"), &atom("(A B)")));
935 }
936
937 #[test]
938 fn get_atom_types_symbol() {
939 let space = metta_space("
940 (: a A)
941 (: a AA)
942 ");
943 assert_eq_no_order!(get_atom_types(&space, &atom("a")), vec![typ!(A), typ!(AA)]);
944 assert_eq_no_order!(get_atom_types(&space, &atom("b")), vec![AtomType::undefined()]);
945 }
946
947 #[test]
948 fn get_atom_types_variable() {
949 let space = DynSpace::new(GroundingSpace::new());
950 assert_eq!(get_atom_types(&space, &atom("$x")), vec![AtomType::undefined()]);
951 }
952
953 #[test]
954 fn get_atom_types_grounded_atom() {
955 let space = DynSpace::new(GroundingSpace::new());
956 assert_eq!(get_atom_types(&space, &Atom::value(3)), vec![typ!(i32)]);
957 }
958
959 #[derive(Debug, Clone, PartialEq)]
960 struct GroundedAtomWithParameterizedType(Atom);
961
962 impl std::fmt::Display for GroundedAtomWithParameterizedType {
963 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
964 write!(f, "GroundedAtomWithParameterizedType({})", self.0)
965 }
966 }
967
968 impl Grounded for GroundedAtomWithParameterizedType {
969 fn type_(&self) -> Atom {
970 self.0.clone()
971 }
972 }
973
974 #[test]
975 fn get_atom_types_variables_are_substituted_for_grounded_atom_type() {
976 let actual_type = Atom::var("t");
977 let gnd = GroundedAtomWithParameterizedType(actual_type.clone());
978 let resolved_type = get_atom_types(&GroundingSpace::new().into(), &Atom::gnd(gnd));
979 assert_eq!(resolved_type.len(), 1);
980 assert!(resolved_type[0].is_valid());
981 assert_ne!(resolved_type[0].as_atom(), &actual_type);
982 assert!(atoms_are_equivalent(resolved_type[0].as_atom(), &actual_type));
983 }
984
985 #[test]
986 fn parameterized_atom_types_should_not_conflict() {
987 let actual_type = Atom::expr([ARROW_SYMBOL, Atom::var("t"), Atom::var("t")]);
988 let gnd_1 = GroundedAtomWithParameterizedType(actual_type.clone());
989 let gnd_2 = GroundedAtomWithParameterizedType(actual_type.clone());
990 let resolved_type_1 = get_atom_types(&GroundingSpace::new().into(), &Atom::gnd(gnd_1));
991 let resolved_type_2 = get_atom_types(&GroundingSpace::new().into(), &Atom::gnd(gnd_2));
992
993 assert_ne!(resolved_type_1, resolved_type_2);
995
996 assert_eq!(resolved_type_1.len(), 1);
998 assert_eq!(resolved_type_2.len(), 1);
999 assert!(resolved_type_1[0].is_valid());
1000 assert!(resolved_type_2[0].is_valid());
1001 assert!(atoms_are_equivalent(resolved_type_1[0].as_atom(), &actual_type));
1002 assert!(atoms_are_equivalent(resolved_type_2[0].as_atom(), &actual_type));
1003 assert!(atoms_are_equivalent(resolved_type_1[0].as_atom(), resolved_type_2[0].as_atom()));
1004 }
1005
1006 #[test]
1007 fn get_atom_types_tuple() {
1008 let space = metta_space("
1009 (: a A)
1010 (: a AA)
1011 (: b B)
1012 (: b BB)
1013 ");
1014 assert_eq_no_order!(get_atom_types(&space, &metta!((a b))),
1015 vec![typ!((A B)), typ!((AA B)), typ!((A BB)), typ!((AA BB))]);
1016 assert_eq_no_order!(get_atom_types(&space, &metta!((a c))),
1017 vec![AtomType::undefined()]);
1018 assert_eq_no_order!(get_atom_types(&space, &metta!((c d))), vec![AtomType::undefined()]);
1019 }
1020
1021 #[test]
1022 fn get_atom_types_function_call_and_tuple() {
1023 let space = metta_space("
1024 (: a (-> B C))
1025 (: a A)
1026 (: b B)
1027 ");
1028 assert_eq!(get_atom_types(&space, &metta!((a b))), vec![typ!((A B)), typ_app!(C)]);
1029 }
1030
1031 #[test]
1032 fn get_atom_types_empty_expression() {
1033 let space = DynSpace::new(GroundingSpace::new());
1034 assert_eq!(get_atom_types(&space, &Atom::expr([])), vec![AtomType::undefined()]);
1035 }
1036
1037 #[test]
1038 fn get_atom_types_function_call_simple() {
1039 let space = metta_space("
1040 (: f (-> B C))
1041 (: b B)
1042 ");
1043 assert_eq!(get_atom_types(&space, &atom("(f b)")), vec![typ_app!(C)]);
1044 assert_eq!(get_atom_types(&space, &atom("(f a)")), vec![typ_app!(C)]);
1045 }
1046
1047 #[test]
1048 fn get_atom_types_function_call_meta_types() {
1049 let space = metta_space("
1050 (: f_atom (-> Atom D))
1051 (: f_expr (-> Expression D))
1052 (: f_var (-> Variable D))
1053 (: f_sym (-> Symbol D))
1054 (: f_gnd (-> Grounded D))
1055 (: b B)
1056 ");
1057 assert_eq!(get_atom_types(&space, &metta!((f_atom b))), vec![typ_app!(D)]);
1058 assert_eq!(get_atom_types(&space, &metta!((f_sym b))), vec![typ_app!(D)]);
1059 assert_eq!(get_atom_types(&space, &metta!((f_expr b))), vec![typ_err!((-> Expression D) , (Error (f_expr b) (BadArgType 1 Expression B)))]);
1060 assert_eq!(get_atom_types(&space, &metta!((f_var b))), vec![typ_err!((-> Variable D) , (Error (f_var b) (BadArgType 1 Variable B)))]);
1061 assert_eq!(get_atom_types(&space, &metta!((f_gnd b))), vec![typ_err!((-> Grounded D) , (Error (f_gnd b) (BadArgType 1 Grounded B)))]);
1062
1063 assert_eq!(get_atom_types(&space, &metta!((f_atom $b))), vec![typ_app!(D)]);
1064 assert_eq!(get_atom_types(&space, &metta!((f_var $b))), vec![typ_app!(D)]);
1067 assert_eq!(get_atom_types(&space, &metta!((f_atom (b)))), vec![typ_app!(D)]);
1070 assert_eq!(get_atom_types(&space, &metta!((f_sym (b)))), vec![typ_err!((-> Symbol D) , (Error (f_sym (b)) (BadArgType 1 Symbol (B))))]);
1074 assert_eq!(get_atom_types(&space, &metta!((f_expr (b)))), vec![typ_app!(D)]);
1075 assert_eq!(get_atom_types(&space, &metta!((f_var (b)))), vec![typ_err!((-> Variable D) , (Error (f_var (b)) (BadArgType 1 Variable (B))))]);
1076 assert_eq!(get_atom_types(&space, &metta!((f_gnd (b)))), vec![typ_err!((-> Grounded D) , (Error (f_gnd (b)) (BadArgType 1 Grounded (B))))]);
1077
1078 assert_eq!(get_atom_types(&space, &metta!((f_atom 1))), vec![typ_app!(D)]);
1079 assert_eq!(get_atom_types(&space, &metta!((f_sym 1))), vec![typ_err!((-> Symbol D) , (Error (f_sym 1) (BadArgType 1 Symbol Number)))]);
1080 assert_eq!(get_atom_types(&space, &metta!((f_expr 1))), vec![typ_err!((-> Expression D) , (Error (f_expr 1) (BadArgType 1 Expression Number)))]);
1081 assert_eq!(get_atom_types(&space, &metta!((f_var 1))), vec![typ_err!((-> Variable D) , (Error (f_var 1) (BadArgType 1 Variable Number)))]);
1082 assert_eq!(get_atom_types(&space, &metta!((f_gnd 1))), vec![typ_app!(D)]);
1083 }
1084
1085 #[test]
1086 fn get_atom_types_function_call_incorrect_arguments() {
1087 let space = metta_space("
1088 (: a (-> C D))
1089 (: b B)
1090 ");
1091 assert_eq!(get_atom_types(&space, &atom("(a b)")), vec![typ_err!((-> C D), (Error (a b) (BadArgType 1 C B)))]);
1092 }
1093
1094 #[test]
1095 fn get_atom_types_function_call_parameterized_types() {
1096 let space = metta_space("
1097 (: = (-> $t $t Type))
1098 (: Some (-> $p Type))
1099 (: foo (-> (Some P)))
1100 (: bar (-> $p (Some $p)))
1101 (: p X)
1102 (: p P)
1103 ");
1104 assert_eq!(get_atom_types(&space, &atom("(= (foo) (bar p))")),
1105 vec![typ_app!(Type)]);
1106 }
1107
1108 #[test]
1109 fn check_type_parameterized_type_no_variable_bindings() {
1110 let space = metta_space("
1111 (: Pair (-> $a $b Type))
1112 (: A (Pair $c $d))
1113 ");
1114
1115 assert!(check_type(&space, &atom("A"), &atom("(Pair $e $f)")));
1116 assert!(check_type(&space, &atom("A"), &atom("(Pair $f $f)")));
1117 }
1118
1119 #[test]
1120 fn check_type_dependent_type_symbol_param() {
1121 let space = metta_space("
1122 (: === (-> $a $a Type))
1123 (: Refl (-> $x (=== $x $x)))
1124
1125 (: TermSym A)
1126 ");
1127
1128 assert!(check_type(&space, &atom("(Refl TermSym)"), &atom("(=== A A)")));
1129 assert!(!check_type(&space, &atom("(Refl TermSym)"), &atom("(=== A B)")));
1130 assert!(!check_type(&space, &atom("(Refl TermSym)"), &atom("(=== 42 A)")));
1131 assert!(!check_type(&space, &atom("(Refl TermSym)"), &atom("(=== A 42)")));
1132 assert!(check_type(&space, &atom("(Refl TermSym)"), &atom("(=== $a A)")));
1133 assert!(check_type(&space, &atom("(Refl TermSym)"), &atom("(=== A $a)")));
1134 assert!(check_type(&space, &atom("(Refl TermSym)"), &atom("(=== $a $a)")));
1135 assert!(check_type(&space, &atom("(Refl TermSym)"), &atom("(=== $a $b)")));
1136 }
1137
1138 #[test]
1139 fn check_type_dependent_type_grounded_param() {
1140 let space = metta_space("
1141 (: === (-> $a $a Type))
1142 (: Refl (-> $x (=== $x $x)))
1143
1144 (: TermGnd 42)
1145 ");
1146
1147 assert!(check_type(&space, &atom("(Refl TermGnd)"), &atom("(=== 42 42)")));
1148 assert!(!check_type(&space, &atom("(Refl TermGnd)"), &atom("(=== 42 24)")));
1149 assert!(!check_type(&space, &atom("(Refl TermGnd)"), &atom("(=== 42 A)")));
1150 assert!(!check_type(&space, &atom("(Refl TermGnd)"), &atom("(=== A 42)")));
1151 assert!(check_type(&space, &atom("(Refl TermGnd)"), &atom("(=== $a 42)")));
1152 assert!(check_type(&space, &atom("(Refl TermGnd)"), &atom("(=== 42 $a)")));
1153 assert!(check_type(&space, &atom("(Refl TermGnd)"), &atom("(=== $a $a)")));
1154 assert!(check_type(&space, &atom("(Refl TermGnd)"), &atom("(=== $a $b)")));
1155 }
1156
1157 #[test]
1158 fn check_type_accept_meta_type() {
1159 let type_r = &atom("R");
1160 let space = metta_space("
1161 (: R Type)
1162 (: A Type)
1163 (: B Type)
1164 (: a A)
1165 (: b B)
1166
1167 (: aF (-> A R))
1168 (: atomF (-> Atom R))
1169 (: exprF (-> Expression R))
1170 (: gndF (-> Grounded R))
1171 (: symF (-> Symbol R))
1172 (: varF (-> Variable R))
1173 ");
1174
1175 assert!(check_type(&space, &atom("(aF a)"), type_r));
1176 assert!(!check_type(&space, &atom("(aF b)"), type_r));
1177 assert!(check_type(&space, &atom("(aF b)"), &ATOM_TYPE_ATOM));
1180 assert!(check_type(&space, &atom("(aF b)"), &ATOM_TYPE_EXPRESSION));
1181 assert!(!check_type(&space, &atom("(aF b)"), &ATOM_TYPE_SYMBOL));
1182
1183 assert!(check_type(&space, &atom("(atomF a)"), type_r));
1184 assert!(check_type(&space, &atom("(atomF ())"), type_r));
1185 assert!(check_type(&space, &atom("(exprF ())"), type_r));
1186 assert!(!check_type(&space, &atom("(exprF a)"), type_r));
1187 assert!(check_type(&space, &atom("(gndF 1)"), type_r));
1188 assert!(!check_type(&space, &atom("(gndF a)"), type_r));
1189 assert!(check_type(&space, &atom("(symF a)"), type_r));
1190 assert!(!check_type(&space, &atom("(symF 1)"), type_r));
1191 assert!(check_type(&space, &atom("(varF $a)"), type_r));
1192 assert!(!check_type(&space, &atom("(varF a)"), type_r));
1193 }
1194
1195 #[test]
1196 fn check_type_return_meta_type() {
1197 let type_r = &atom("R");
1198 let space = metta_space("
1199 (: R Type)
1200 (: A Type)
1201 (: B Type)
1202 (: a A)
1203 (: b B)
1204
1205 (: atomR (-> A Atom))
1206 (: exprR (-> A Expression))
1207 (: gndR (-> A Grounded))
1208 (: symR (-> A Symbol))
1209 (: varR (-> A Variable))
1210
1211 (: atomF (-> Atom R))
1212 (: exprF (-> Expression R))
1213 (: gndF (-> Grounded R))
1214 (: symF (-> Symbol R))
1215 (: varF (-> Variable R))
1216 ");
1217
1218 assert!(check_type(&space, &atom("(atomF (atomR a))"), type_r));
1219 assert!(check_type(&space, &atom("(atomF (exprR a))"), type_r));
1220 assert!(check_type(&space, &atom("(atomF (gndR a))"), type_r));
1221 assert!(check_type(&space, &atom("(atomF (symR a))"), type_r));
1222 assert!(check_type(&space, &atom("(atomF (varR a))"), type_r));
1223
1224 assert!(check_type(&space, &atom("(exprF (exprR a))"), type_r));
1225 assert!(check_type(&space, &atom("(exprF (atomR a))"), type_r));
1228
1229 assert!(check_type(&space, &atom("(gndF (gndR a))"), type_r));
1230 assert!(!check_type(&space, &atom("(gndF (atomR a))"), type_r));
1231 assert!(check_type(&space, &atom("(symF (symR a))"), type_r));
1232 assert!(!check_type(&space, &atom("(symF (atomR a))"), type_r));
1233 assert!(check_type(&space, &atom("(varF (varR a))"), type_r));
1234 assert!(!check_type(&space, &atom("(varF (atomR a))"), type_r));
1235 }
1236
1237 #[test]
1238 fn validate_atom_accept_meta_type() {
1239 let space = metta_space("
1240 (: R Type)
1241 (: A Type)
1242 (: B Type)
1243 (: a A)
1244 (: b B)
1245
1246 (: aF (-> A R))
1247 (: atomF (-> Atom R))
1248 (: exprF (-> Expression R))
1249 (: gndF (-> Grounded R))
1250 (: symF (-> Symbol R))
1251 (: varF (-> Variable R))
1252 ");
1253
1254 assert!(validate_atom(&space, &atom("(aF a)")));
1255 assert!(!validate_atom(&space, &atom("(aF b)")));
1256
1257 assert!(validate_atom(&space, &atom("(atomF a)")));
1258 assert!(validate_atom(&space, &atom("(atomF ())")));
1259 assert!(validate_atom(&space, &atom("(exprF ())")));
1260 assert!(!validate_atom(&space, &atom("(exprF a)")));
1261 assert!(validate_atom(&space, &atom("(gndF 1)")));
1262 assert!(!validate_atom(&space, &atom("(gndF a)")));
1263 assert!(validate_atom(&space, &atom("(symF a)")));
1264 assert!(!validate_atom(&space, &atom("(symF 1)")));
1265 assert!(validate_atom(&space, &atom("(varF $a)")));
1266 assert!(!validate_atom(&space, &atom("(varF a)")));
1267 }
1268
1269 #[test]
1270 fn validate_atom_return_meta_type() {
1271 let space = metta_space("
1272 (: R Type)
1273 (: A Type)
1274 (: B Type)
1275 (: a A)
1276 (: b B)
1277
1278 (: atomR (-> A Atom))
1279 (: exprR (-> A Expression))
1280 (: gndR (-> A Grounded))
1281 (: symR (-> A Symbol))
1282 (: varR (-> A Variable))
1283
1284 (: atomF (-> Atom R))
1285 (: exprF (-> Expression R))
1286 (: gndF (-> Grounded R))
1287 (: symF (-> Symbol R))
1288 (: varF (-> Variable R))
1289 ");
1290
1291 assert!(validate_atom(&space, &atom("(atomF (atomR a))")));
1292 assert!(validate_atom(&space, &atom("(atomF (exprR a))")));
1293 assert!(validate_atom(&space, &atom("(atomF (gndR a))")));
1294 assert!(validate_atom(&space, &atom("(atomF (symR a))")));
1295 assert!(validate_atom(&space, &atom("(atomF (varR a))")));
1296
1297 assert!(validate_atom(&space, &atom("(exprF (exprR a))")));
1298 assert!(validate_atom(&space, &atom("(exprF (atomR a))")));
1301
1302 assert!(validate_atom(&space, &atom("(gndF (gndR a))")));
1303 assert!(!validate_atom(&space, &atom("(gndF (atomR a))")));
1304 assert!(validate_atom(&space, &atom("(symF (symR a))")));
1305 assert!(!validate_atom(&space, &atom("(symF (atomR a))")));
1306 assert!(validate_atom(&space, &atom("(varF (varR a))")));
1307 assert!(!validate_atom(&space, &atom("(varF (atomR a))")));
1308 }
1309
1310 #[test]
1311 fn tuple_with_undefined_member() {
1312 let space = metta_space("(: F %Undefined%)");
1313 assert_eq!(get_atom_types_internal(&space, &atom("(F arg)")), vec![]);
1314
1315 let gnd = GroundedAtomWithParameterizedType(ATOM_TYPE_UNDEFINED);
1316 assert_eq!(get_atom_types_internal(&space, &expr!({gnd} "a")), vec![]);
1317 }
1318
1319}