diff options
Diffstat (limited to 'src/sexp')
-rw-r--r-- | src/sexp/mod.rs | 12 | ||||
-rw-r--r-- | src/sexp/step.rs | 34 | ||||
-rw-r--r-- | src/sexp/subst.rs | 9 | ||||
-rw-r--r-- | src/sexp/util.rs | 10 |
4 files changed, 61 insertions, 4 deletions
diff --git a/src/sexp/mod.rs b/src/sexp/mod.rs index 4e3c11c..7aa517b 100644 --- a/src/sexp/mod.rs +++ b/src/sexp/mod.rs @@ -59,11 +59,19 @@ use SLeaf::*; impl SExp { + /// Quick test that lets are values + /// ```rust + /// use myslip::sexp::{SExp::*, SLeaf::*, util::*}; + /// assert!(scons(Let, scons(var("a"), scons(5, Nil))).is_value()) + /// ``` pub fn is_value(&self) -> bool { match self { SCons(a, b) => - (**a == Atom(Quote) || **a == Atom(Vector)) - && b.consists_of_values(), + SCons(a.clone(), b.clone()).check_let().is_some() || + ( + (**a == Atom(Quote) || **a == Atom(Vector)) + && b.consists_of_values() + ), Atom(Var(_)) => false, Atom(_) => true, } diff --git a/src/sexp/step.rs b/src/sexp/step.rs index e031fca..816582c 100644 --- a/src/sexp/step.rs +++ b/src/sexp/step.rs @@ -240,6 +240,8 @@ impl SExp { /// ``` /// /// **Let-bindings** + /// + /// Basic operation: /// ```rust /// use myslip::sexp::{SExp::*, SLeaf::*, util::*}; /// @@ -261,7 +263,27 @@ impl SExp { /// ).step(), /// Ok(scons( /// scons(Let, scons(var("x"), scons(5, Nil))), scons( - /// scons(Add, scons(var("x"), scons(1, Nil))), Nil + /// scons(Add, scons(var("x"), scons(4, Nil))), Nil + /// ) + /// )) + /// ); + /// ``` + /// + /// Shadowing: + /// ```rust + /// use myslip::sexp::{SExp::*, SLeaf::*, util::*}; + /// + /// assert_eq!( + /// scons( + /// scons(Let, scons(var("x"), scons(4, Nil))), + /// scons( + /// scons(Let, scons(var("x"), scons(5, Nil))), + /// scons(scons(Add, scons(var("x"), scons(4, Nil))), Nil) + /// ) + /// ).step(), + /// Ok(scons( + /// scons(Let, scons(var("x"), scons(5, Nil))), scons( + /// scons(Add, scons(var("x"), scons(4, Nil))), Nil /// ) /// )) /// ); @@ -278,6 +300,16 @@ impl SExp { SCons(op, l) if !(*op).is_value() => Ok(scons(op.step()?, l)), + // let binds + SCons(op, l) if op.clone().check_let().is_some() => match (*op).check_let() { + Some((varname, val)) => Ok(match *l { + SCons(a, b) if *b == Atom(Nil) => *a, + t => t, + }.subst(&varname, &val)), + None => panic!("unreachable as per match guard arm"), + }, + + // op value and a1 .. an values, and b0, b1 .. bn not values // --------------------------------------------------------- // (op a1 ... an b) -> (op a1 ... an b0.step() b1 .. bn) diff --git a/src/sexp/subst.rs b/src/sexp/subst.rs index 87f9b50..4cfa48d 100644 --- a/src/sexp/subst.rs +++ b/src/sexp/subst.rs @@ -17,7 +17,14 @@ impl SExp { /// ``` pub fn subst(self, name: &str, value: &SExp) -> SExp { match self { - SCons(a, b) => scons((*a).subst(name, value), (*b).subst(name, value)), + SCons(a, b) => { + if let Some((varname, _)) = a.clone().check_let() { + if &varname == name { + return SCons(a, b); + } + } + scons((*a).subst(name, value), (*b).subst(name, value)) + }, Atom(Var(x)) if x == name => value.clone(), t => t, } diff --git a/src/sexp/util.rs b/src/sexp/util.rs index b3edf19..a28aaad 100644 --- a/src/sexp/util.rs +++ b/src/sexp/util.rs @@ -76,4 +76,14 @@ impl SExp { }, } } + + pub fn check_let(self) -> Option<(String, SExp)> { + match &(self.parts())[..] { + [l, n, v] if *l == Atom(Let) => match (n.clone(), v.clone()) { + (Atom(Var(name)), val) => Some((name, val)), + _ => None + }, + _ => None + } + } } |