From c061d2970910c6bfb5be2d8b950a44d8c80428ca Mon Sep 17 00:00:00 2001 From: Joel Kronqvist Date: Mon, 18 Aug 2025 17:31:26 +0300 Subject: test: added failing tests for type pattern matching, as VecType and QuoteTy must be accounted --- src/type/case.rs | 211 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 112 insertions(+), 99 deletions(-) diff --git a/src/type/case.rs b/src/type/case.rs index 70597b6..5669e74 100644 --- a/src/type/case.rs +++ b/src/type/case.rs @@ -9,105 +9,7 @@ impl SExp { /// Checks if this expression matches the given type, /// returns all variable names bound by a respective case. /// - /// Two equal values match: - /// ```rust - /// use myslip::sexp::{SExp::*, SLeaf::*, util::*}; - /// use myslip::r#type::{Type::*, util::*, TypeError::*, PatFail::*}; - /// assert_eq!( - /// scons(1, scons(2, Nil)) - /// .matches_type(&List(vec![Integer, Integer])), - /// Ok(vec![]) - /// ); - /// ``` - /// - /// On the other hand, if values don't match, an invalid pattern - /// error should be thrown: - /// ```rust - /// use myslip::sexp::{SExp::*, SLeaf::*, util::*}; - /// use myslip::r#type::{Type::*, util::*, TypeError::*, PatFail::*}; - /// assert_eq!( - /// scons(1, scons(2, Nil)) - /// .matches_type(&List(vec![Integer])), - /// Err(InvalidPattern(TypeMismatch { - /// pattern: scons(1, scons(2, Nil)), - /// expected: List(vec![Integer]), - /// found: List(vec![Integer, Integer]) - /// })) - /// ); - /// ``` - /// - /// A wildcard variable matches too and gets the correct type: - /// ```rust - /// use myslip::sexp::{SExp::*, SLeaf::*, util::*}; - /// use myslip::r#type::{Type::*, util::*, TypeError::*, PatFail::*}; - /// assert_eq!( - /// var("x").matches_type(&List(vec![Integer, Integer])), - /// Ok(vec![("x".to_string(), List(vec![Integer, Integer]))]) - /// ); - /// ``` - /// - /// Obviously that should also work for sub-expressions: - /// ```rust - /// use myslip::sexp::{SExp::*, SLeaf::*, util::*}; - /// use myslip::r#type::{Type::*, util::*, TypeError::*, PatFail::*}; - /// assert_eq!( - /// scons(var("x"), scons(2, Nil)).matches_type(&List(vec![arr(Integer, Integer), Integer])), - /// Ok(vec![("x".to_string(), arr(Integer, Integer))]) - /// ); - /// ``` - /// - /// If the wildcard variable is encountered multiple times, - /// an invalid operator error should be returned: - /// ```rust - /// use myslip::sexp::{SExp::*, SLeaf::*, util::*}; - /// use myslip::r#type::{Type::*, util::*, TypeError::*, PatFail::*}; - /// assert_eq!( - /// scons(var("x"), scons(var("x"), Nil)).matches_type(&List(vec![Integer, Integer])), - /// Err(InvalidPattern(RepeatedVariable( - /// "x".to_string(), - /// scons(var("x"), scons(var("x"), Nil)) - /// ))) - /// ); - /// ``` - /// - /// Then there's also rest patterns which should be appropriately typed: - /// ```rust - /// use myslip::sexp::{SExp::*, SLeaf::*, util::*}; - /// use myslip::r#type::{Type::*, util::*, TypeError::*, PatFail::*}; - /// assert_eq!( - /// scons(var("h"), scons(RestPat("t".to_string()), Nil)) - /// .matches_type(&List(vec![arr(Integer, Integer), Integer, Integer, Integer])), - /// Ok(vec![ - /// ("h".to_string(), arr(Integer, Integer)), - /// ("t".to_string(), List(vec![Integer, Integer, Integer])) - /// ]) - /// ); - /// ``` - /// - /// They should also work with vectors: - /// ```rust - /// use myslip::sexp::{SExp::*, SLeaf::*, util::*}; - /// use myslip::r#type::{Type::*, util::*, TypeError::*, PatFail::*}; - /// assert_eq!( - /// scons(var("h"), scons(RestPat("t".to_string()), Nil)) - /// .matches_type(&vecof(Integer)), - /// Ok(vec![ - /// ("h".to_string(), Integer), - /// ("t".to_string(), vecof(Integer)) - /// ]) - /// ); - /// ``` - /// - /// Vector matching should work without the rest pattern too: - /// ```rust - /// use myslip::sexp::{SExp::*, SLeaf::*, util::*}; - /// use myslip::r#type::{Type::*, util::*, TypeError::*, PatFail::*}; - /// assert_eq!( - /// scons(var("h"), scons(2, Nil)).matches_type(&vecof(Integer)), - /// Ok(vec![("h".to_string(), Integer)]) - /// ); - /// ``` - /// TODO: Nil / empty list + /// TODO? Nil / empty list pub fn matches_type( &self, ty: &Type @@ -134,6 +36,8 @@ impl SExp { ctx: HashMap ) -> Result, TypeError> { + println!("checking if {} matches pattern {}", &ty, &self); + match (self, ty) { (a, b) if a.infer_list_type(ctx.clone()) == Ok(b.clone()) => @@ -226,3 +130,112 @@ impl SExp { } } + +#[cfg(test)] +mod tests { + use crate::sexp::{SExp::*, SLeaf::*, util::*}; + use crate::r#type::{Type::*, util::*, TypeError::*, PatFail::*}; + + #[test] + fn test_type_atoms() { + assert_eq!(Atom(Int(1)).matches_type(&Integer), Ok(vec![])); + assert_eq!(Atom(True).matches_type(&Boolean), Ok(vec![])); + assert_eq!(Atom(Ty(Integer)).matches_type(&TypeLit), Ok(vec![])); + assert_eq!(Atom(Nil).matches_type(&NilType), Ok(vec![])); + } + + #[test] + fn test_vector_match() { + assert_eq!( + scons(1, scons(2, Nil)) + .matches_type(&List(vec![VecType, vecof(Integer)])), + Ok(vec![]) + ); + assert_eq!( + scons(var("a"), scons(var("b"), Nil)) + .matches_type(&List(vec![VecType, vecof(Integer)])), + Ok(vec![ + ("a".to_string(), Integer), + ("b".to_string(), Integer) + ]) + ); + assert_eq!( + scons(var("a"), scons(RestPat("b".to_string()), Nil)) + .matches_type(&List(vec![VecType, vecof(Integer)])), + Ok(vec![ + ("a".to_string(), Integer), + ("b".to_string(), List(vec![VecType, vecof(Integer)])) + ]) + ); + } + + #[test] + fn test_list_match() { + assert_eq!( + scons(1, scons(2, Nil)) + .matches_type(&List(vec![QuoteTy, List(vec![Integer, Integer])])), + Ok(vec![]) + ); + assert_eq!( + scons(var("a"), scons(var("b"), Nil)) + .matches_type(&List(vec![QuoteTy, List(vec![Integer, Integer])])), + Ok(vec![ + ("a".to_string(), Integer), + ("b".to_string(), Integer) + ]) + ); + assert_eq!( + scons(var("a"), scons(RestPat("b".to_string()), Nil)) + .matches_type(&List(vec![QuoteTy, List(vec![Integer, Integer, Integer])])), + Ok(vec![ + ("a".to_string(), Integer), + ("b".to_string(), List(vec![QuoteTy, List(vec![Integer, Integer])])) + ]) + ); + } + + #[test] + fn test_mismatch_match() { + assert_eq!( + scons(1, scons(2, Nil)) + .matches_type(&List(vec![QuoteTy, List(vec![Integer])])), + Err(InvalidPattern(TypeMismatch { + pattern: scons(1, scons(2, Nil)), + expected: List(vec![QuoteTy, List(vec![Integer])]), + found: List(vec![QuoteTy, List(vec![Integer, Integer])]) + })) + ); + } + + #[test] + fn test_simple_wildcard_match() { + assert_eq!( + var("x").matches_type(&List(vec![QuoteTy, List(vec![Integer, Integer])])), + Ok(vec![("x".to_string(), List(vec![QuoteTy, List(vec![Integer, Integer])]))]) + ); + } + + #[test] + fn test_invalid_pattern_match() { + assert_eq!( + scons(var("x"), scons(var("x"), Nil)) + .matches_type(&List(vec![QuoteTy, List(vec![Integer, Integer])])), + Err(InvalidPattern(RepeatedVariable( + "x".to_string(), + scons(var("x"), scons(var("x"), Nil)) + ))) + ); + } + + #[test] + fn test_nil_match() { + assert_eq!( + Atom(Nil).matches_type(&List(vec![VecType, vecof(Boolean)])), + Ok(vec![]) + ); + assert_eq!( + Atom(Nil).matches_type(&List(vec![QuoteTy, List(vec![])])), + Ok(vec![]) + ); + } +} -- cgit v1.2.3