aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Kronqvist <joel.kronqvist@iki.fi>2025-08-10 19:16:51 +0300
committerJoel Kronqvist <joel.kronqvist@iki.fi>2025-08-10 19:16:51 +0300
commitd6d1ec80ffcc0b13234b170a91b920371078a027 (patch)
treea8347b04ad354e3f2cb265b5d506a4891853cf7f
parent06798d622327707ca3f3b42d65fc3d1a25ae3c57 (diff)
downloadmyslip-d6d1ec80ffcc0b13234b170a91b920371078a027.tar.gz
myslip-d6d1ec80ffcc0b13234b170a91b920371078a027.zip
Added tests for functions
-rw-r--r--src/parse/parsetree.rs1
-rw-r--r--src/sexp/display.rs1
-rw-r--r--src/sexp/mod.rs10
-rw-r--r--src/sexp/step.rs16
-rw-r--r--src/type/check.rs50
-rw-r--r--src/type/conversion.rs12
-rw-r--r--src/type/display.rs1
-rw-r--r--src/type/mod.rs2
8 files changed, 91 insertions, 2 deletions
diff --git a/src/parse/parsetree.rs b/src/parse/parsetree.rs
index 6dd4e01..46e636c 100644
--- a/src/parse/parsetree.rs
+++ b/src/parse/parsetree.rs
@@ -116,6 +116,7 @@ fn tokens_to_ast_inner(
Some(Sym(s)) if s == "vector" => Ok(Atom(Vector)),
Some(Sym(s)) if s == "print" => Ok(Atom(Print)),
Some(Sym(s)) if s == "let" => Ok(Atom(Let)),
+ Some(Sym(s)) if s == "fn" => Ok(Atom(Fun)),
Some(Sym(s)) if s == "->" => Ok(Atom(Arr)),
Some(Sym(s)) if s == "int" => Ok(Atom(Ty(Integer))),
Some(Sym(s)) if s == "bool" => Ok(Atom(Ty(Boolean))),
diff --git a/src/sexp/display.rs b/src/sexp/display.rs
index 5d78e93..eff3904 100644
--- a/src/sexp/display.rs
+++ b/src/sexp/display.rs
@@ -29,6 +29,7 @@ impl fmt::Display for SLeaf {
Vector => "vector".to_string(),
Print => "print".to_string(),
Let => "let".to_string(),
+ Fun => "fn".to_string(),
Ty(t) => t.to_string(),
Arr => "->".to_string(),
Nil => "()".to_string(),
diff --git a/src/sexp/mod.rs b/src/sexp/mod.rs
index 11c185f..4bb4fa2 100644
--- a/src/sexp/mod.rs
+++ b/src/sexp/mod.rs
@@ -35,6 +35,8 @@ pub enum SLeaf {
Let,
+ Fun,
+
Ty(Type),
Arr,
@@ -75,6 +77,7 @@ impl SExp {
pub fn is_value(&self) -> bool {
match self {
SCons(a, b) =>
+ 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())
@@ -102,4 +105,11 @@ impl SExp {
_ => false
}
}
+
+ pub fn is_fun(&self) -> bool {
+ match self {
+ SCons(a, _) if **a == Atom(Fun) => true,
+ _ => false
+ }
+ }
}
diff --git a/src/sexp/step.rs b/src/sexp/step.rs
index c26dce9..8cc1e64 100644
--- a/src/sexp/step.rs
+++ b/src/sexp/step.rs
@@ -270,6 +270,22 @@ impl SExp {
/// );
/// ```
///
+ /// **Functions**
+ /// ```rust
+ /// use myslip::{sexp::{SExp::*, SLeaf::*, util::*}, r#type::{Type::*, util::*}};
+ ///
+ /// let varlist = scons(var("a"), scons(var("b"), Nil));
+ /// let typelist = scons(Ty(List(vec![Integer])), Nil);
+ /// let ret = Atom(Ty(Boolean));
+ /// let body = scons(Gt, varlist.clone());
+ /// let fun = scons(Fun, scons(varlist, scons(typelist, scons(ret, scons(body, Nil)))));
+ /// let args = scons(2, scons(1, Nil));
+ /// assert_eq!(
+ /// scons(fun, scons(args, Nil)).multistep(),
+ /// Ok(Atom(True))
+ /// );
+ /// ```
+ ///
/// Shadowing:
/// ```rust
/// use myslip::sexp::{SExp::*, SLeaf::*, util::*};
diff --git a/src/type/check.rs b/src/type/check.rs
index 24c255d..1712446 100644
--- a/src/type/check.rs
+++ b/src/type/check.rs
@@ -78,7 +78,7 @@ impl SExp {
/// assert_eq!(Atom(And).type_check(), Ok(arr(List(vec![Boolean, Boolean]), Boolean)));
/// assert_eq!(Atom(Or) .type_check(), Ok(arr(List(vec![Boolean, Boolean]), Boolean)));
/// assert_eq!(Atom(Xor).type_check(), Ok(arr(List(vec![Boolean, Boolean]), Boolean)));
- /// assert_eq!(Atom(Not).type_check(), Ok(arr(List(vec![Boolean]), Boolean)));
+ /// assert_eq!(Atom(Not).type_check(), Ok(arr(Boolean, Boolean)));
/// ```
///
///
@@ -101,6 +101,50 @@ impl SExp {
/// );
/// ```
///
+ /// **Functions**
+ ///
+ /// One variable:
+ /// ```rust
+ /// use myslip::{
+ /// r#type::{*, Type::*, TypeError::*, util::*},
+ /// sexp::{SExp::*, SLeaf::*, util::*},
+ /// };
+ ///
+ /// let varlist = scons(var("a"), Nil);
+ /// let typelist = scons(Ty(Integer), Nil);
+ /// let ret = Atom(Ty(Integer));
+ /// let body = scons(var("a"), Nil);
+ /// assert_eq!(
+ /// scons(Fun, scons(varlist, scons(typelist, scons(ret, scons(body, Nil))))).type_check(),
+ /// Ok(arr(List(vec![Integer]), Integer))
+ /// );
+ /// ```
+ /// Two-variable:
+ /// ```rust
+ /// use myslip::{
+ /// r#type::{*, Type::*, TypeError::*, util::*},
+ /// sexp::{SExp::*, SLeaf::*, util::*},
+ /// };
+ ///
+ /// let varlist = scons(var("a"), scons(var("b"), Nil));
+ /// let typelist = scons(Ty(List(vec![Integer])), Nil);
+ /// let ret = Atom(Ty(Boolean));
+ /// let body = scons(Eq, varlist.clone());
+ /// assert_eq!(
+ /// scons(Fun, scons(varlist, scons(typelist, scons(ret, scons(body, Nil))))).type_check(),
+ /// Ok(arr(List(vec![Integer, Integer]), Boolean))
+ /// );
+ /// ```
+ /// Only keyword shouldnt panic:
+ /// ```rust
+ /// use myslip::{
+ /// r#type::{*, Type::*, TypeError::*, util::*},
+ /// sexp::{SExp::*, SLeaf::*, util::*},
+ /// };
+ /// Atom(Fun).type_check();
+ /// ```
+ ///
+ ///
/// Though perhaps the most important task of the type system
/// is to increase safety by being able to warn about errors
/// before evaluation. Here are some failing examples:
@@ -174,7 +218,7 @@ impl SExp {
Atom(Eq | Neq | Lt | Gt | Le | Ge) =>
Ok(arr(List(vec![Integer, Integer]), Boolean)),
Atom(Or | And | Xor) => Ok(arr(List(vec![Boolean, Boolean]), Boolean)),
- Atom(Not) => Ok(arr(List(vec!(Boolean)), Boolean)),
+ Atom(Not) => Ok(arr(Boolean, Boolean)),
Atom(Nil) => Ok(NilType),
Atom(Quote) => Ok(arr(
vt("T"),
@@ -188,6 +232,7 @@ impl SExp {
Atom(Print) => Ok(arr(vt("_"), List(vec![]))),
Atom(Ty(_)) => Ok(TypeLit),
Atom(Arr) => Ok(arr(List(vec![TypeLit, TypeLit]), TypeLit)),
+ Atom(Fun) => todo!(),
SCons(op, l) => {
@@ -204,6 +249,7 @@ impl SExp {
return Err(LetAsOperator(scons(op.clone(), l.clone())));
}
+
// Normal operation
let opertype = (*op).infer_type(ctx.clone())?;
let argstype = (*l).infer_list_type(ctx)?;
diff --git a/src/type/conversion.rs b/src/type/conversion.rs
index 3be2874..0a96790 100644
--- a/src/type/conversion.rs
+++ b/src/type/conversion.rs
@@ -36,6 +36,8 @@ impl Type {
(a, b) if a == b => a.clone(),
+ (List(v), b) if v.len() == 1 && &v[0] == b => b.clone(),
+
(VecOf(a), VecOf(b)) => vecof(a.least_general_supertype(b)),
(List(v1), List(v2)) if v1.len() == v2.len() => {
@@ -73,6 +75,7 @@ impl Type {
/// Tries to convert this type into a subtype of the other.
///
+ /// Currently the most important conversion is from list to vec.
/// ```rust
/// use myslip::r#type::{Type::*, util::*};
///
@@ -92,6 +95,13 @@ impl Type {
/// Ok(vecof(vt("T")))
/// );
/// ```
+ ///
+ /// Though the conversion from (a) to a is also convenient:
+ /// ```rust
+ /// use myslip::r#type::{Type::*, util::*};
+ ///
+ /// assert_eq!(List(vec![Integer]).into_type(&Integer), Ok(Integer));
+ /// ```
pub fn into_type(self, other: &Type) -> Result<Type, ()> {
if !self.aka(other) {
return Err(());
@@ -103,6 +113,8 @@ impl Type {
(_, VarType(_)) => Ok(self),
+ (List(x), b) if x.len() == 1 && &x[0] == b => Ok(x[0].clone()),
+
(List(v), VecOf(b)) => match v.get(0) {
Some(first) => {
let cand = v.into_iter()
diff --git a/src/type/display.rs b/src/type/display.rs
index cd60673..ded97a0 100644
--- a/src/type/display.rs
+++ b/src/type/display.rs
@@ -112,6 +112,7 @@ impl fmt::Display for TypeError {
letexp
)
},
+ FunAsAtom => write!(f, "'fn' used as atom doesn't make sense"),
OtherError => write!(f, "uncategorized error"),
}
}
diff --git a/src/type/mod.rs b/src/type/mod.rs
index c138bba..d020ab3 100644
--- a/src/type/mod.rs
+++ b/src/type/mod.rs
@@ -66,6 +66,8 @@ pub enum TypeError {
LetAsOperator(SExp),
+ FunAsAtom,
+
OtherError
}