diff options
author | Joel Kronqvist <joel.kronqvist@iki.fi> | 2025-08-21 12:35:37 +0300 |
---|---|---|
committer | Joel Kronqvist <joel.kronqvist@iki.fi> | 2025-08-21 12:35:37 +0300 |
commit | d1c97e405230b6616ef834cf38be351e566a228e (patch) | |
tree | 7c75d9353b0b3cc9f08400c5d09e0b7552afc9c8 | |
parent | a70dcaa949f41c585f9aea5e79f2550053d8e857 (diff) | |
download | myslip-d1c97e405230b6616ef834cf38be351e566a228e.tar.gz myslip-d1c97e405230b6616ef834cf38be351e566a228e.zip |
feat: implemented coproducts according to tests
-rw-r--r-- | src/parse/parsetree.rs | 9 | ||||
-rw-r--r-- | src/sexp/case.rs | 30 | ||||
-rw-r--r-- | src/sexp/mod.rs | 12 | ||||
-rw-r--r-- | src/type/case.rs | 97 | ||||
-rw-r--r-- | src/type/check.rs | 25 | ||||
-rw-r--r-- | src/type/display.rs | 13 | ||||
-rw-r--r-- | src/type/mod.rs | 2 |
7 files changed, 90 insertions, 98 deletions
diff --git a/src/parse/parsetree.rs b/src/parse/parsetree.rs index 081489d..575afc9 100644 --- a/src/parse/parsetree.rs +++ b/src/parse/parsetree.rs @@ -202,6 +202,14 @@ fn parse_type(s: &str) -> IResult<&str, Type> { let absp = take_while1(|c| "ABCDEFGHIJKLMNOPQRSTUVWXYZ".contains(c)) .map(|s: &str| VarType(s.to_string())); + let sump = ( + tag("("), multispace0, + tag("Sum"), multispace1, + parse_type, multispace1, + parse_type, multispace0, + tag(")") + ).map(|tup| sumtype(tup.4, tup.6)); + alt(( tag("Int") .map(|_| Integer), tag("Bool") .map(|_| Boolean), @@ -210,6 +218,7 @@ fn parse_type(s: &str) -> IResult<&str, Type> { tag("Let") .map(|_| LetType), tag("Type") .map(|_| TypeLit), tag("Nil") .map(|_| NilType), + sump, arrp, vecp, listp, diff --git a/src/sexp/case.rs b/src/sexp/case.rs index cc643f3..fd39922 100644 --- a/src/sexp/case.rs +++ b/src/sexp/case.rs @@ -64,6 +64,17 @@ impl SExp { * Some(vec![(Var("x".to_string()), Atom(Int(2)))]) * ); * ``` + * But they shouldn't match the wrong hand: + * ```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(Inl, scons(var("x"), Nil))), + * None + * ); + * ``` */ pub fn matches_pat(&self, pat: &SExp) -> Option<Vec<(SLeaf, SExp)>> { match (self, pat) { @@ -72,6 +83,25 @@ impl SExp { (a, Atom(Var(name))) => Some(vec![(Var(name.to_string()), a.clone())]), + (SCons(cp, ls), SCons(inx, var)) if **cp == Atom(Coprod) => { + let var = match (**var).clone() { + SCons(a, n) if *n == Atom(Nil) => *a, + t => t, + }; + let mut ls = (**ls).clone().parts().into_iter(); + match (ls.next(), ls.next(), (**inx).clone(), var) { + (Some(l), _, Atom(Inl), Atom(Var(name))) if !l.is_type_atom() + => Some(vec![(Var(name), l)]), + (_, Some(r), Atom(Inr), Atom(Var(name))) if !r.is_type_atom() + => Some(vec![(Var(name), r)]), + (Some(l), _, Atom(Inl), pat) if !l.is_type_atom() + => l.matches_pat(&pat), + (_, Some(r), Atom(Inr), pat) if !r.is_type_atom() + => r.matches_pat(&pat), + _ => None, + } + }, + (SCons(a1, a2), SCons(b1, b2)) => { let lefthand = a1.matches_pat(b1); let checkres = (*b2).check_res_pat(); diff --git a/src/sexp/mod.rs b/src/sexp/mod.rs index 14731dd..c1a5a8e 100644 --- a/src/sexp/mod.rs +++ b/src/sexp/mod.rs @@ -89,7 +89,10 @@ impl SExp { scons(a.clone(), b.clone()).is_fun() || SCons(a.clone(), b.clone()).check_let().is_some() || ( - (**a == Atom(Quote) || **a == Atom(Vector) || (**a).is_type_lit()) + (**a == Atom(Quote) + || **a == Atom(Vector) + || (**a).is_type_lit() + || **a == Atom(Coprod)) && b.consists_of_values() ), Atom(Var(_)) => false, @@ -122,4 +125,11 @@ impl SExp { _ => false } } + + pub fn is_type_atom(&self) -> bool { + match self { + Atom(Ty(_)) => true, + _ => false, + } + } } diff --git a/src/type/case.rs b/src/type/case.rs index d8871af..ae4eb77 100644 --- a/src/type/case.rs +++ b/src/type/case.rs @@ -120,99 +120,20 @@ impl SExp { _ => Err(OtherError) }, - _ => Err(OtherError), // maybe add nicer error? - - } - - // match (self, ty) { - - // (a, b) if a.infer_list_type(ctx.clone()) == Ok(b.clone()) => - // Ok(ctx.into_iter().collect()), - // (a, b) if a.infer_type(ctx.clone()) == Ok(b.clone()) => - // Ok(ctx.into_iter().collect()), - - // (Atom(Var(name)), t) => - // Ok(ctx.into_iter() - // .chain(iter::once((name, t))) - // .collect()), + (se, SumType(l, r)) => match (&(se.parts())[..], (*l, *r)) { + ([op, exp], (l, _)) if *op == Atom(Inl) => + exp.clone().matches_type_ctx(l, ctx.clone()), + ([op, exp], (_, r)) if *op == Atom(Inr) => + exp.clone().matches_type_ctx(r, ctx.clone()), + _ => Err(OtherError), + }, - // (exp, VecOf(ty)) => { - // let mut res: Vec<(String, Type)> = - // ctx.clone().into_iter().collect(); - // let mut exps = exp.clone().parts(); - // // TODO: Nil or empty exp - // let restpat = exps.remove(exps.len() - 1); - // for exp in exps { - // for et in exp.matches_type_ctx(*ty.clone(), ctx.clone())? { - // res.push(et); - // } - // } - // match restpat { - // Atom(RestPat(name)) => { - // res.push((name, vecof(ty))); - // Ok(res) - // }, - // t => { - // for et in t.matches_type_ctx(*ty.clone(), ctx.clone())? { - // res.push(et); - // } - // Ok(res) - // } - // } - // }, - // (SCons(e1, e2), List(typelist)) => { - // let explist = scons(e1.clone(), e2.clone()).parts(); - // let mut res: Vec<(String, Type)> = - // ctx.clone().into_iter().collect(); - // if explist.len() == typelist.len() { - // for (exp, ty) in explist.into_iter().zip(typelist) { - // for (e, t) in exp.matches_type_ctx(ty, ctx.clone())? { - // res.push((e, t)); - // } - // } - // Ok(res) - // } else { - // match explist.last().cloned() { - // Some(Atom(RestPat(name))) => { - // for (exp, ty) in explist.clone() - // .into_iter() - // .rev().skip(1).rev() - // .zip(typelist.clone()) - // { - // for (e, t) in exp.matches_type_ctx(ty, ctx.clone())? { - // res.push((e, t)); - // } - // } - // res.push(( - // name, - // List(typelist - // .into_iter() - // .skip(explist.len() - 1) - // .collect()) - // )); - // Ok(res) - // }, - // _ => Err(InvalidPattern(TypeMismatch { - // pattern: scons(e1.clone(), e2.clone()), - // expected: List(typelist), - // found: scons(e1, e2).infer_list_type(ctx)?, - // })), - // } - // } - // }, - // (e, t) => { - // let found_ty = e.infer_list_type(ctx)?; - // Err(InvalidPattern(TypeMismatch { - // pattern: e, - // expected: t, - // found: found_ty - // })) - // }, + _ => Err(OtherError), // maybe add nicer error? - // } + } } } diff --git a/src/type/check.rs b/src/type/check.rs index 96a4408..5ccd0e7 100644 --- a/src/type/check.rs +++ b/src/type/check.rs @@ -193,8 +193,9 @@ impl SExp { /// let exp = parse_to_ast( /// "(let mysum (coprod Bool 7)) \ /// (case mysum \ - /// ((inl b) (if b 0 1)) \ - /// ((inr x) x))" + /// ((inl b) (case b (true 0) (_ 1))) \ + /// ((inr x) x) \ + /// (_ 0))" /// ).unwrap(); /// assert_eq!( /// exp.type_check(), @@ -300,9 +301,9 @@ impl SExp { vecof(vt("T")), List(vec![VecType, vecof(vt("T"))]) )), - Atom(Coprod) => todo!(), - Atom(Inl) => todo!(), - Atom(Inr) => todo!(), + Atom(Coprod) => Err(OtherError), // TODO + Atom(Inl) => Err(OtherError), // TODO + Atom(Inr) => Err(OtherError), // TODO Atom(Let) => Ok(LetType), Atom(Print) => Ok(arr(vt("_"), NilType)), Atom(Ty(_)) => Ok(TypeLit), @@ -379,6 +380,20 @@ impl SExp { } } + // Coproducts + if **op == Atom(Coprod) { + let mut parts = (**l).clone().parts().into_iter(); + return match (parts.next(), parts.next(), parts.next()) { + (Some(Atom(Ty(ty))), Some(r), None) + => Ok(sumtype(ty, r.infer_type(ctx)?)), + (Some(l), Some(Atom(Ty(ty))), None) + => Ok(sumtype(l.infer_type(ctx)?, ty)), + (Some(l), Some(r), None) + => Err(SumDefNoTypeLiteral(scons(l, r))), + _ => Err(OtherError) + }; + } + // Normal operation let opertype = (*op).infer_type(ctx.clone())?; let argstype = (*l).infer_list_type(ctx)?; diff --git a/src/type/display.rs b/src/type/display.rs index 99394df..586483c 100644 --- a/src/type/display.rs +++ b/src/type/display.rs @@ -119,12 +119,17 @@ impl fmt::Display for TypeError { letexp ) }, - DifferentTypeCaseArms(ty1, ty2) => write!(f, "arms of match have different types, '{ty1}' and '{ty2}'"), - FunAsAtom => write!(f, "'fn' used as atom doesn't make sense"), + DifferentTypeCaseArms(ty1, ty2) + => write!(f, "arms of match have different types, '{ty1}' and '{ty2}'"), + FunAsAtom => write!(f, "'fn' used as atom doesn't make sense"), CaseAsAtom => write!(f, "'case' used as atom doesn't make sense"), RestAsAtom => write!(f, "'..[name]' used as atom doesn't make sense"), - InvalidFunDef(exp, err) => write!(f, "invalid function definition '{exp}': {err}"), - NoWildcardInCase(exp) => write!(f, "no wildcard in cases: '{exp}'"), + InvalidFunDef(exp, err) + => write!(f, "invalid function definition '{exp}': {err}"), + NoWildcardInCase(exp) + => write!(f, "no wildcard in cases: '{exp}'"), + SumDefNoTypeLiteral(exp) + => write!(f, "other arm of coproduct should've been a type in {exp}"), OtherError => write!(f, "uncategorized error"), } } diff --git a/src/type/mod.rs b/src/type/mod.rs index c726001..e1ca798 100644 --- a/src/type/mod.rs +++ b/src/type/mod.rs @@ -83,6 +83,8 @@ pub enum TypeError { DifferentTypeCaseArms(Type, Type), + SumDefNoTypeLiteral(SExp), + OtherError } |