aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJoel Kronqvist <joel.kronqvist@iki.fi>2025-08-12 12:12:39 +0300
committerJoel Kronqvist <joel.kronqvist@iki.fi>2025-08-12 12:12:39 +0300
commitdb736d795b759edd913d96195747f0881c4e950f (patch)
tree14cdf8e53810162857ff3a6ee672b9fd8e6ccbff /src
parent2e17ad5361a86d004ca48419a0f69f9c298ec1e1 (diff)
downloadmyslip-db736d795b759edd913d96195747f0881c4e950f.tar.gz
myslip-db736d795b759edd913d96195747f0881c4e950f.zip
test: added failing tests for pattern match typing and matches_type
Diffstat (limited to 'src')
-rw-r--r--src/type/case.rs102
-rw-r--r--src/type/check.rs18
-rw-r--r--src/type/display.rs29
-rw-r--r--src/type/mod.rs16
4 files changed, 164 insertions, 1 deletions
diff --git a/src/type/case.rs b/src/type/case.rs
new file mode 100644
index 0000000..2403acd
--- /dev/null
+++ b/src/type/case.rs
@@ -0,0 +1,102 @@
+
+use crate::r#type::{Type, TypeError, Type::*, TypeError::*, PatFail::*, util::*};
+use crate::sexp::{SExp, SExp::*, SLeaf::*, util::*};
+
+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))
+ /// ])
+ /// );
+ /// ```
+ ///
+ pub fn matches_type(&self, ty: &Type) -> Result<Vec<(String, Type)>, TypeError> {
+ todo!()
+ }
+}
diff --git a/src/type/check.rs b/src/type/check.rs
index b2815c7..9d7c52b 100644
--- a/src/type/check.rs
+++ b/src/type/check.rs
@@ -143,6 +143,24 @@ impl SExp {
/// };
/// Atom(Fun).type_check();
/// ```
+ ///
+ /// **Pattern matching**
+ ///
+ /// If this works, I guess it seems to work...
+ /// ```rust
+ /// use myslip::{
+ /// r#type::{*, Type::*, TypeError::*, util::*},
+ /// sexp::{SExp::*, SLeaf::*, util::*},
+ /// parse::parsetree::parse_to_ast
+ /// };
+ ///
+ /// assert_eq!(
+ /// parse_to_ast(
+ /// "(case (1 2 3 true false) ((x 2 3 false true) (+ x 1)) ((0 0 0 true true) 0) (_ 1))"
+ /// ).unwrap().type_check(),
+ /// Ok(Integer)
+ /// );
+ /// ```
///
///
/// Though perhaps the most important task of the type system
diff --git a/src/type/display.rs b/src/type/display.rs
index 758fee4..e945eba 100644
--- a/src/type/display.rs
+++ b/src/type/display.rs
@@ -1,6 +1,15 @@
use std::fmt;
-use crate::r#type::{Type, TypeError, FunDefError, Type::*, TypeError::*, FunDefError::*};
+use crate::r#type::{
+ Type,
+ TypeError,
+ FunDefError,
+ PatFail,
+ Type::*,
+ TypeError::*,
+ FunDefError::*,
+ PatFail::*
+};
impl fmt::Display for Type {
@@ -98,6 +107,7 @@ impl fmt::Display for TypeError {
arglist, expected, found
)
},
+ InvalidPattern(ty) => write!(f, "invalid pattern: {ty}"),
ArgumentsDontMatchGeneric { argtype, generictype } => {
write!(
f,
@@ -120,6 +130,23 @@ impl fmt::Display for TypeError {
}
+impl fmt::Display for PatFail {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ RepeatedVariable(name, exp_in) =>
+ write!(f, "repeated pattern variable '{name}' in '{exp_in}'"),
+ TypeMismatch { pattern, expected, found } =>
+ write!(
+ f,
+ "type mismatch in '{}' — expected type '{}', found '{}'",
+ pattern,
+ expected,
+ found,
+ ),
+ }
+ }
+}
+
impl fmt::Display for FunDefError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
diff --git a/src/type/mod.rs b/src/type/mod.rs
index f08dc1a..34506d0 100644
--- a/src/type/mod.rs
+++ b/src/type/mod.rs
@@ -5,6 +5,7 @@ pub mod display;
pub mod check;
pub mod subst;
pub mod conversion;
+pub mod case;
use crate::sexp::SExp;
@@ -59,6 +60,8 @@ pub enum TypeError {
found: Type,
},
+ InvalidPattern(PatFail),
+
ArgumentsDontMatchGeneric {
argtype: Type,
generictype: Type,
@@ -73,6 +76,7 @@ pub enum TypeError {
OtherError
}
+
#[derive(Debug,PartialEq)]
pub enum FunDefError {
NoFunToken,
@@ -85,6 +89,18 @@ pub enum FunDefError {
InvalidReturnType,
}
+
+#[derive(Debug,PartialEq)]
+pub enum PatFail {
+ RepeatedVariable(String, SExp),
+ TypeMismatch {
+ pattern: SExp,
+ expected: Type,
+ found: Type,
+ }
+}
+
+
use Type::*;
impl Type {