diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/type/case.rs | 102 | ||||
-rw-r--r-- | src/type/check.rs | 18 | ||||
-rw-r--r-- | src/type/display.rs | 29 | ||||
-rw-r--r-- | src/type/mod.rs | 16 |
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 { |