aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Kronqvist <joel.kronqvist@iki.fi>2025-08-21 12:35:37 +0300
committerJoel Kronqvist <joel.kronqvist@iki.fi>2025-08-21 12:35:37 +0300
commitd1c97e405230b6616ef834cf38be351e566a228e (patch)
tree7c75d9353b0b3cc9f08400c5d09e0b7552afc9c8
parenta70dcaa949f41c585f9aea5e79f2550053d8e857 (diff)
downloadmyslip-d1c97e405230b6616ef834cf38be351e566a228e.tar.gz
myslip-d1c97e405230b6616ef834cf38be351e566a228e.zip
feat: implemented coproducts according to tests
-rw-r--r--src/parse/parsetree.rs9
-rw-r--r--src/sexp/case.rs30
-rw-r--r--src/sexp/mod.rs12
-rw-r--r--src/type/case.rs97
-rw-r--r--src/type/check.rs25
-rw-r--r--src/type/display.rs13
-rw-r--r--src/type/mod.rs2
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
}