From d6d1ec80ffcc0b13234b170a91b920371078a027 Mon Sep 17 00:00:00 2001 From: Joel Kronqvist Date: Sun, 10 Aug 2025 19:16:51 +0300 Subject: Added tests for functions --- src/parse/parsetree.rs | 1 + src/sexp/display.rs | 1 + src/sexp/mod.rs | 10 ++++++++++ src/sexp/step.rs | 16 ++++++++++++++++ src/type/check.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/type/conversion.rs | 12 ++++++++++++ src/type/display.rs | 1 + src/type/mod.rs | 2 ++ 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 { 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 } -- cgit v1.2.3