diff options
author | Joel Kronqvist <joel.kronqvist@iki.fi> | 2025-08-19 13:55:05 +0300 |
---|---|---|
committer | Joel Kronqvist <joel.kronqvist@iki.fi> | 2025-08-19 13:55:05 +0300 |
commit | a70dcaa949f41c585f9aea5e79f2550053d8e857 (patch) | |
tree | 9366f0b1e026c009708ebc243d006a79cbf452c9 | |
parent | d4d6e972650370d529363f7db3946e58bdbd7bca (diff) | |
download | myslip-a70dcaa949f41c585f9aea5e79f2550053d8e857.tar.gz myslip-a70dcaa949f41c585f9aea5e79f2550053d8e857.zip |
test: added boilerplate and tests for coproduct parsing, type checking and evaluation
-rw-r--r-- | src/parse/parsetree.rs | 4 | ||||
-rw-r--r-- | src/sexp/case.rs | 12 | ||||
-rw-r--r-- | src/sexp/display.rs | 3 | ||||
-rw-r--r-- | src/sexp/mod.rs | 3 | ||||
-rw-r--r-- | src/sexp/step.rs | 8 | ||||
-rw-r--r-- | src/type/case.rs | 18 | ||||
-rw-r--r-- | src/type/check.rs | 43 | ||||
-rw-r--r-- | src/type/display.rs | 1 | ||||
-rw-r--r-- | src/type/mod.rs | 5 | ||||
-rw-r--r-- | src/type/subst.rs | 2 | ||||
-rw-r--r-- | src/type/util.rs | 4 |
11 files changed, 102 insertions, 1 deletions
diff --git a/src/parse/parsetree.rs b/src/parse/parsetree.rs index c773f3b..081489d 100644 --- a/src/parse/parsetree.rs +++ b/src/parse/parsetree.rs @@ -119,6 +119,9 @@ fn tokens_to_ast_inner( Some(Sym(s)) if s == "false" => Ok(Atom(False)), Some(Sym(s)) if s == "quote" => Ok(Atom(Quote)), Some(Sym(s)) if s == "vector" => Ok(Atom(Vector)), + Some(Sym(s)) if s == "coprod" => Ok(Atom(Coprod)), + Some(Sym(s)) if s == "inl" => Ok(Atom(Inl)), + Some(Sym(s)) if s == "inr" => Ok(Atom(Inr)), Some(Sym(s)) if s == "<>" => Ok(Atom(Concat)), Some(Sym(s)) if s == "print" => Ok(Atom(Print)), Some(Sym(s)) if s == "let" => Ok(Atom(Let)), @@ -248,6 +251,7 @@ mod private_parsing_tests { Ok(("", List(vec![Integer, Boolean, arr(Boolean, Integer), Integer]))) ); assert_eq!(parse_type("T"), Ok(("", VarType("T".to_string())))); + assert_eq!(parse_type("(Sum (Int -> Bool) Int)"), Ok(("", sumtype(arr(Integer, Boolean), Integer)))); } #[test] diff --git a/src/sexp/case.rs b/src/sexp/case.rs index 4c2c7ba..cc643f3 100644 --- a/src/sexp/case.rs +++ b/src/sexp/case.rs @@ -52,6 +52,18 @@ impl SExp { * ]) * ); * ``` + * + * Coproducts must be handled individually: + * ```rust + * use myslip::r#type::Type::*; + * use myslip::sexp::{SExp::*, SLeaf::*, util::*}; + * + * assert_eq!( + * scons(Coprod, scons(Ty(Boolean), scons(2, Nil))) + * .matches_pat(&scons(Inr, scons(var("x"), Nil))), + * Some(vec![(Var("x".to_string()), Atom(Int(2)))]) + * ); + * ``` */ pub fn matches_pat(&self, pat: &SExp) -> Option<Vec<(SLeaf, SExp)>> { match (self, pat) { diff --git a/src/sexp/display.rs b/src/sexp/display.rs index ec1b421..703bda3 100644 --- a/src/sexp/display.rs +++ b/src/sexp/display.rs @@ -28,6 +28,9 @@ impl fmt::Display for SLeaf { RestPat(s) => format!("..{s}"), Quote => "quote".to_string(), Vector => "vector".to_string(), + Coprod => "coprod".to_string(), + Inl => "inl".to_string(), + Inr => "inr".to_string(), Concat => "<>".to_string(), Print => "print".to_string(), Let => "let".to_string(), diff --git a/src/sexp/mod.rs b/src/sexp/mod.rs index 1a90f52..14731dd 100644 --- a/src/sexp/mod.rs +++ b/src/sexp/mod.rs @@ -34,6 +34,9 @@ pub enum SLeaf { Quote, Vector, + Coprod, + Inl, + Inr, Let, diff --git a/src/sexp/step.rs b/src/sexp/step.rs index 59b201c..3168c5b 100644 --- a/src/sexp/step.rs +++ b/src/sexp/step.rs @@ -297,6 +297,14 @@ impl SExp { /// assert_eq!(exp, expshould); /// let exp = exp.and_then(|e| e.step()); /// assert_eq!(exp, Ok(Atom(True))); + /// + /// let exp = "case (coprod Bool (+ 1 2)) ((inl b) (if b 1 0)) ((inr x) (- x 1))"; + /// let exp = parse_to_ast(exp); + /// let exp = exp.and_then(|e| e.step()); + /// let expshould = parse_to_ast("case (coprod Bool 3) ((inl b) (if b 1 0)) ((inr x) (- x 1))"); + /// assert_eq!(exp, expshould); + /// let exp = exp.and_then(|e| e.step()); + /// assert_eq!(exp, Ok(scons(Sub, scons(3, scons(1, Nil))))); /// ``` /// /// Shadowing: diff --git a/src/type/case.rs b/src/type/case.rs index c53bbd0..d8871af 100644 --- a/src/type/case.rs +++ b/src/type/case.rs @@ -324,4 +324,22 @@ mod tests { Ok(vec![]) ); } + + #[test] + fn test_sumtype_match() { + assert_eq!( + scons(Inl, scons(var("x"), Nil)).matches_type(&sumtype(Integer, Boolean)), + Ok(vec![("x".to_string(), Integer)]) + ); + assert_eq!( + scons(Inr, scons(var("x"), Nil)).matches_type(&sumtype(Integer, Boolean)), + Ok(vec![("x".to_string(), Boolean)]) + ); + assert!( + scons(Inl, scons(True, Nil)).matches_type(&sumtype(Integer, Boolean)).is_err() + ); + assert!( + scons(Inr, scons(1, Nil)).matches_type(&sumtype(Integer, Boolean)).is_err() + ); + } } diff --git a/src/type/check.rs b/src/type/check.rs index c0f4b1f..96a4408 100644 --- a/src/type/check.rs +++ b/src/type/check.rs @@ -60,6 +60,25 @@ impl SExp { /// ``` /// ...so please don't ask what their type is. /// + /// **Sum types** + /// + /// Instantiation types: + /// ```rust + /// use myslip::{ + /// r#type::{*, Type::*, TypeError::*, util::*}, + /// sexp::{SExp::*, SLeaf::*, util::*}, + /// }; + /// assert_eq!( + /// scons(Coprod, scons(7, scons(Ty(Boolean), Nil))).type_check(), + /// Ok(sumtype(Integer, Boolean)) + /// ); + /// assert_eq!( + /// scons(Coprod, scons(Ty(Boolean), scons(False, Nil))).type_check(), + /// Ok(sumtype(Boolean, Boolean)) + /// ); + /// ``` + /// Destructuring is tested in the tests of case. + /// /// Some common operators get arrow types: /// ```rust /// use myslip::{ @@ -161,7 +180,28 @@ impl SExp { /// Ok(Integer) /// ); /// ``` + /// + /// Here's a test on pattern matching on sum types: + /// ```rust + /// use myslip::{ + /// r#type::{*, Type::*, TypeError::*, util::*}, + /// sexp::{SExp::*, SLeaf::*, util::*}, + /// parse::parsetree::parse_to_ast + /// }; /// + /// fn main() { + /// let exp = parse_to_ast( + /// "(let mysum (coprod Bool 7)) \ + /// (case mysum \ + /// ((inl b) (if b 0 1)) \ + /// ((inr x) x))" + /// ).unwrap(); + /// assert_eq!( + /// exp.type_check(), + /// Ok(Integer) + /// ); + /// } + /// ``` /// /// Though perhaps the most important task of the type system /// is to increase safety by being able to warn about errors @@ -260,6 +300,9 @@ impl SExp { vecof(vt("T")), List(vec![VecType, vecof(vt("T"))]) )), + Atom(Coprod) => todo!(), + Atom(Inl) => todo!(), + Atom(Inr) => todo!(), Atom(Let) => Ok(LetType), Atom(Print) => Ok(arr(vt("_"), NilType)), Atom(Ty(_)) => Ok(TypeLit), diff --git a/src/type/display.rs b/src/type/display.rs index a2f4b58..99394df 100644 --- a/src/type/display.rs +++ b/src/type/display.rs @@ -35,6 +35,7 @@ impl fmt::Display for Type { TypeLit => write!(f, "Type"), VecOf(ty) => write!(f, "({} ...)", *ty), Arrow(a, b) => write!(f, "({} -> {})", a, b), + SumType(a,b)=> write!(f, "(Sum {} {})", a, b), List(types) => write!( f, "({})", diff --git a/src/type/mod.rs b/src/type/mod.rs index bfe0055..c726001 100644 --- a/src/type/mod.rs +++ b/src/type/mod.rs @@ -31,6 +31,8 @@ pub enum Type { VecOf(Box<Type>), VecType, // constructor + SumType(Box<Type>, Box<Type>), + LetType, /// Type for generics @@ -156,7 +158,8 @@ impl Type { LetType => Ok(()), NilType => Ok(()), TypeLit => Ok(()), - Arrow(a, b) => b.is_concrete().and_then(|_ok| a.is_concrete()), + Arrow(a, b) => b.is_concrete().and_then(|_ok| a.is_concrete()), + SumType(a, b) => b.is_concrete().and_then(|_ok| a.is_concrete()), List(v) => { let mut res = Ok(()); for t in v { diff --git a/src/type/subst.rs b/src/type/subst.rs index 18c9926..fd45005 100644 --- a/src/type/subst.rs +++ b/src/type/subst.rs @@ -27,6 +27,8 @@ impl Type { Arrow(a, b) => arr((*a).subst(name, value), (*b).subst(name, value)), + SumType(a, b) => sumtype((*a).subst(name, value), (*b).subst(name, value)), + List(v) => List( v.into_iter() .map(|t| t.subst(name, value)) diff --git a/src/type/util.rs b/src/type/util.rs index 04b4ee6..0ea1e4d 100644 --- a/src/type/util.rs +++ b/src/type/util.rs @@ -6,6 +6,10 @@ pub fn arr(a: impl Into<Box<Type>>, b: impl Into<Box<Type>>) -> Type { Arrow(a.into(), b.into()) } +pub fn sumtype(a: impl Into<Box<Type>>, b: impl Into<Box<Type>>) -> Type { + SumType(a.into(), b.into()) +} + pub fn vt(name: &str) -> Type { VarType(name.to_string()) } |