diff options
author | Joel Kronqvist <joel.kronqvist@iki.fi> | 2025-08-10 22:33:48 +0300 |
---|---|---|
committer | Joel Kronqvist <joel.kronqvist@iki.fi> | 2025-08-10 22:33:48 +0300 |
commit | 2f101372e3311f1ccf7f8ec093c7fde9f4373439 (patch) | |
tree | 209cac3d19bc9ee714eedd17766bdc939a1feefa /src | |
parent | d6d1ec80ffcc0b13234b170a91b920371078a027 (diff) | |
download | myslip-2f101372e3311f1ccf7f8ec093c7fde9f4373439.tar.gz myslip-2f101372e3311f1ccf7f8ec093c7fde9f4373439.zip |
Implemented functions
Diffstat (limited to 'src')
-rw-r--r-- | src/sexp/step.rs | 70 | ||||
-rw-r--r-- | src/type/check.rs | 16 | ||||
-rw-r--r-- | src/type/display.rs | 39 | ||||
-rw-r--r-- | src/type/mod.rs | 14 | ||||
-rw-r--r-- | src/type/util.rs | 61 |
5 files changed, 173 insertions, 27 deletions
diff --git a/src/sexp/step.rs b/src/sexp/step.rs index 8cc1e64..fd6be9e 100644 --- a/src/sexp/step.rs +++ b/src/sexp/step.rs @@ -275,13 +275,13 @@ impl SExp { /// 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 typelist = scons(Ty(List(vec![Integer, 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(), + /// scons(fun, args).multistep(), /// Ok(Atom(True)) /// ); /// ``` @@ -309,6 +309,29 @@ impl SExp { pub fn step(self) -> Result<Self, String> { match self { + + // Type list (maybe these should just be parsed...) + SCons(op, l) if (*op).is_type_lit() => { + if *l == Atom(Nil) { + return Ok(*op); + } + let mut res = vec![]; + for exp in std::iter::once(*op).chain(l.parts()) { + res.push(match exp.multistep()? { + Atom(Ty(t)) => Ok(t), + e => Err(format!("not a type: {e}")), + }?); + } + Ok(Atom(Ty(List(res)))) + }, + + // t is value + // ---------- + // t -> t + t if t.is_value() => Ok(t), + + + // List processing // op not a value @@ -342,6 +365,32 @@ impl SExp { }, + // Custom anonymous functions + SCons(op, l) if (*op).is_fun() => { + + // Get function parts + let ls = op.parts(); + let argnames = ls.get(1).unwrap() + .clone().parts(); + let mut argnamevec = vec![]; + for name in argnames.clone() { + argnamevec.push(match name { + Atom(Var(s)) => Ok(s), + _ => Err("invalid entry in argument list (should be unreachable after type checker)".to_string()), + }?); + } + let mut body = ls.get(4).unwrap().clone(); + + // Get argument parts + let suppliedargs = l.parts(); + for (name, value) in argnamevec.into_iter().zip(suppliedargs) { + body = body.subst(&name, &value); + } + + Ok(body) + }, + + // Arithmetic @@ -549,29 +598,12 @@ impl SExp { } }, - // Type list - SCons(op, l) if (*op).is_type_lit() => { - let mut res = vec![]; - for exp in std::iter::once(*op).chain(l.parts()) { - res.push(match exp.multistep()? { - Atom(Ty(t)) => Ok(t), - e => Err(format!("not a type: {e}")), - }?); - } - Ok(Atom(Ty(List(res)))) - }, - // Print SCons(op, l) if *op == Atom(Print) => { println!("{}", *l); Ok(Atom(Nil)) }, - // t is value - // ---------- - // t -> t - t if t.is_value() => Ok(t), - t => Err(format!("unimplemented: {:?}.step()", t)), } } diff --git a/src/type/check.rs b/src/type/check.rs index 1712446..d815c85 100644 --- a/src/type/check.rs +++ b/src/type/check.rs @@ -112,11 +112,11 @@ impl SExp { /// /// let varlist = scons(var("a"), Nil); /// let typelist = scons(Ty(Integer), Nil); - /// let ret = Atom(Ty(Integer)); - /// let body = scons(var("a"), Nil); + /// let ret = scons(Ty(Integer), Nil); + /// let body = scons(Add, scons(var("a"), scons(1, Nil))); /// assert_eq!( /// scons(Fun, scons(varlist, scons(typelist, scons(ret, scons(body, Nil))))).type_check(), - /// Ok(arr(List(vec![Integer]), Integer)) + /// Ok(arr(Integer, Integer)) /// ); /// ``` /// Two-variable: @@ -127,7 +127,7 @@ impl SExp { /// }; /// /// let varlist = scons(var("a"), scons(var("b"), Nil)); - /// let typelist = scons(Ty(List(vec![Integer])), Nil); + /// let typelist = scons(Ty(Integer), scons(Ty(Integer), Nil)); /// let ret = Atom(Ty(Boolean)); /// let body = scons(Eq, varlist.clone()); /// assert_eq!( @@ -202,7 +202,7 @@ impl SExp { } - fn infer_type(&self, mut ctx: HashMap<String, Type>) -> Result<Type, TypeError> { + pub fn infer_type(&self, mut ctx: HashMap<String, Type>) -> Result<Type, TypeError> { match self { @@ -232,7 +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!(), + Atom(Fun) => Err(FunAsAtom), SCons(op, l) => { @@ -249,6 +249,10 @@ impl SExp { return Err(LetAsOperator(scons(op.clone(), l.clone()))); } + // Anonymous functions + if scons(op.clone(), l.clone()).is_fun() { + return scons(op.clone(), l.clone()).get_fun_type(ctx); + } // Normal operation let opertype = (*op).infer_type(ctx.clone())?; diff --git a/src/type/display.rs b/src/type/display.rs index ded97a0..758fee4 100644 --- a/src/type/display.rs +++ b/src/type/display.rs @@ -1,6 +1,6 @@ use std::fmt; -use crate::r#type::{Type, TypeError, Type::*, TypeError::*}; +use crate::r#type::{Type, TypeError, FunDefError, Type::*, TypeError::*, FunDefError::*}; impl fmt::Display for Type { @@ -113,8 +113,45 @@ impl fmt::Display for TypeError { ) }, FunAsAtom => write!(f, "'fn' used as atom doesn't make sense"), + InvalidFunDef(exp, err) => write!(f, "invalid function definition '{exp}': {err}"), OtherError => write!(f, "uncategorized error"), } } } + +impl fmt::Display for FunDefError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + NoFunToken => + write!( + f, + "no '{}'-token at start of definition", + crate::sexp::SLeaf::Fun + ), + NoArgumentList => + write!( + f, + "no list of argument names" + ), + NoTypeList => + write!( + f, + "no list of argument types" + ), + NoReturnType => + write!( + f, + "no return type supplied" + ), + NoFunctionBody => + write!( + f, + "function has no body" + ), + InvalidArgumentList => write!(f, "invalid argument list"), + InvalidTypeList => write!(f, "invalid argument type list"), + InvalidReturnType => write!(f, "invalid return type"), + } + } +} diff --git a/src/type/mod.rs b/src/type/mod.rs index d020ab3..f08dc1a 100644 --- a/src/type/mod.rs +++ b/src/type/mod.rs @@ -68,9 +68,23 @@ pub enum TypeError { FunAsAtom, + InvalidFunDef(SExp, FunDefError), + OtherError } +#[derive(Debug,PartialEq)] +pub enum FunDefError { + NoFunToken, + NoArgumentList, + NoTypeList, + NoReturnType, + NoFunctionBody, + InvalidArgumentList, + InvalidTypeList, + InvalidReturnType, +} + use Type::*; impl Type { diff --git a/src/type/util.rs b/src/type/util.rs index 85e64f1..23f8d1f 100644 --- a/src/type/util.rs +++ b/src/type/util.rs @@ -1,5 +1,6 @@ -use crate::r#type::{*, Type::*}; +use crate::r#type::{*, Type::*, TypeError::*, FunDefError::*}; +use std::collections::HashMap; pub fn arr(a: impl Into<Box<Type>>, b: impl Into<Box<Type>>) -> Type { Arrow(a.into(), b.into()) @@ -12,3 +13,61 @@ pub fn vt(name: &str) -> Type { pub fn vecof(ty: impl Into<Box<Type>>) -> Type { VecOf(ty.into()) } + +use crate::sexp::{SExp::*, SLeaf::*}; +impl SExp { + pub fn get_fun_type(self, mut ctx: HashMap<String, Type>) -> Result<Type, TypeError> { + let ls = self.clone().parts(); + ls.get(0) + .filter(|t| **t == Atom(Fun)) + .ok_or(InvalidFunDef(self.clone(), NoFunToken))?; + let argnames = ls.get(1) + .ok_or(InvalidFunDef(self.clone(), NoArgumentList))? + .clone().parts(); + let argtypes = ls.get(2) + .ok_or(InvalidFunDef(self.clone(), NoTypeList))? + .clone(); + let rettype = ls.get(3) + .ok_or(InvalidFunDef(self.clone(), NoReturnType))?; + let funbody = ls.get(4) + .ok_or(InvalidFunDef(self.clone(), NoFunctionBody))?; + + let mut argnamevec = vec![]; + for name in argnames { + argnamevec.push(match name { + Atom(Var(s)) => Ok(s), + _ => Err(InvalidFunDef(self.clone(), InvalidArgumentList)), + }?); + } + + let argtypes = match argtypes.clone().multistep() { + Ok(Atom(Ty(List(v)))) => Ok(v), + Ok(Atom(Ty(t))) => Ok(vec![t]), + _ => { + Err(InvalidFunDef(self.clone(), InvalidArgumentList)) + }, + }?; + + let rettype = match rettype.clone().multistep() { + Ok(Atom(Ty(t))) => Ok(t), + _ => Err(InvalidFunDef(self.clone(), InvalidReturnType)) + }?; + + let additional_ctx = argnamevec.into_iter().zip(argtypes.clone()); + for (name, ty) in additional_ctx { + ctx.insert(name, ty); + } + + let argtype = if argtypes.len() == 0 { + NilType + } else if argtypes.len() == 1 { + argtypes[0].clone() + } else { + List(argtypes) + }; + + funbody.infer_type(ctx)?; + + Ok(arr(argtype, rettype)) + } +} |