aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJoel Kronqvist <joel.kronqvist@iki.fi>2025-08-10 22:33:48 +0300
committerJoel Kronqvist <joel.kronqvist@iki.fi>2025-08-10 22:33:48 +0300
commit2f101372e3311f1ccf7f8ec093c7fde9f4373439 (patch)
tree209cac3d19bc9ee714eedd17766bdc939a1feefa /src
parentd6d1ec80ffcc0b13234b170a91b920371078a027 (diff)
downloadmyslip-2f101372e3311f1ccf7f8ec093c7fde9f4373439.tar.gz
myslip-2f101372e3311f1ccf7f8ec093c7fde9f4373439.zip
Implemented functions
Diffstat (limited to 'src')
-rw-r--r--src/sexp/step.rs70
-rw-r--r--src/type/check.rs16
-rw-r--r--src/type/display.rs39
-rw-r--r--src/type/mod.rs14
-rw-r--r--src/type/util.rs61
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))
+ }
+}