aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Kronqvist <joel.kronqvist@iki.fi>2025-08-15 11:27:52 +0300
committerJoel Kronqvist <joel.kronqvist@iki.fi>2025-08-15 11:27:52 +0300
commit0ca4c3f103acd9d1ea78bbc42b9b70161a94308d (patch)
tree7e8144434656f44e2e1f66175bb80ec6364a6ce5
parent322f541d6922422e5a4aa29f50c6534517ee85be (diff)
downloadmyslip-0ca4c3f103acd9d1ea78bbc42b9b70161a94308d.tar.gz
myslip-0ca4c3f103acd9d1ea78bbc42b9b70161a94308d.zip
feat: fixed point dynamics
-rw-r--r--TUTORIAL.md2
-rw-r--r--src/sexp/step.rs62
-rw-r--r--src/sexp/util.rs41
-rw-r--r--src/type/check.rs2
4 files changed, 81 insertions, 26 deletions
diff --git a/TUTORIAL.md b/TUTORIAL.md
index 8e5165d..d7bf266 100644
--- a/TUTORIAL.md
+++ b/TUTORIAL.md
@@ -199,7 +199,7 @@ For basic lists, the `case`-operator can be used.
It's syntax is:
```myslip
(case [condition]
- ([pattern 1] [value 1])
+ ([pattern 1] [value 1])
([pattern 2] [value 2])
...)
```
diff --git a/src/sexp/step.rs b/src/sexp/step.rs
index 5b60fec..84553e1 100644
--- a/src/sexp/step.rs
+++ b/src/sexp/step.rs
@@ -1,6 +1,5 @@
use crate::sexp::{SExp, SExp::*, SLeaf::*, util::*};
-use crate::r#type::{Type::*, util::*};
impl SExp {
@@ -346,6 +345,31 @@ impl SExp {
None => panic!("unreachable as per match guard arm"),
},
+ // 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)
+ },
+
// case expressions
SCons(op, l) if scons(op.clone(), l.clone()).check_case().is_some() => {
let (scrutinee, patarms) = scons(op, l).check_case().unwrap();
@@ -394,29 +418,19 @@ 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)
+ // Fixed point recursion
+ SCons(op, l) if *op == Atom(Fix) => {
+ let ls = match (*l).clone() {
+ SCons(a, n) if *n == Atom(Nil) => a.clone(),
+ _ => todo!()
+ }.parts();
+ let argls = ls[1].clone();
+ let argname = match argls {
+ Atom(Var(name)) => name,
+ _ => todo!()
+ };
+ let body = ls[4].clone();
+ Ok(body.subst(&argname, &scons(op, l)))
},
diff --git a/src/sexp/util.rs b/src/sexp/util.rs
index aa7ae18..db400e9 100644
--- a/src/sexp/util.rs
+++ b/src/sexp/util.rs
@@ -87,6 +87,47 @@ impl SExp {
}
}
+ pub fn get_vars_bound_by_pattern(&self) -> Vec<String> {
+ match self {
+ Atom(Var(s)) => vec![s.clone()],
+ Atom(RestPat(s)) => vec![s.clone()],
+ SCons(a, b) =>
+ (*a).get_vars_bound_by_pattern()
+ .into_iter()
+ .chain((*b).get_vars_bound_by_pattern())
+ .collect(),
+ _ => vec![],
+ }
+ }
+
+ pub fn get_case_scrut_pats_and_arms(self) ->
+ Option<(SExp, Vec<(SExp, SExp)>)> {
+ let (scrutinee, patarms) = self.check_case()?;
+
+ let scrutinee = match scrutinee {
+ SCons(q, v) if *q == Atom(Quote) || *q == Atom(Vector) => *v,
+ t => t,
+ };
+
+ let mut res = vec![];
+ for patarm in patarms {
+ let (pat, arm) = match patarm {
+ SCons(pat, arm) => Some((*pat, *arm)),
+ _ => {
+ println!("warn: invalid structure in case arms,");
+ println!(" should be unreachable after type checking");
+ None
+ },
+ }?;
+ let arm = match arm {
+ SCons(x, n) if *n == Atom(Nil) => *x,
+ t => t,
+ };
+ res.push((pat, arm));
+ }
+ Some((scrutinee, res))
+ }
+
pub fn check_case(self) -> Option<(SExp, Vec<SExp>)> {
match &(self.parts())[..] {
[casekw, scrutinee, cases @ ..] if casekw.clone() == Atom(Case) =>
diff --git a/src/type/check.rs b/src/type/check.rs
index d118871..170feec 100644
--- a/src/type/check.rs
+++ b/src/type/check.rs
@@ -483,7 +483,7 @@ impl Type {
#[cfg(test)]
mod tests {
- use super::{*, TypeError};
+ use super::*;
#[test]
fn test_failing_infer_generics() {