aboutsummaryrefslogtreecommitdiff
path: root/src/sexp/step.rs
diff options
context:
space:
mode:
authorJoel Kronqvist <joel.kronqvist@iki.fi>2025-07-30 17:27:15 +0300
committerJoel Kronqvist <joel.kronqvist@iki.fi>2025-07-30 17:27:15 +0300
commitcd68a2880db1400ae09ce0df64994b2bae33a3c1 (patch)
tree04893ee79ed16b920e7129b4eab1298a62814d65 /src/sexp/step.rs
parent8e2ca4bbc356d87ff5a37f20bea4f5b3cc561041 (diff)
downloadmyslip-cd68a2880db1400ae09ce0df64994b2bae33a3c1.tar.gz
myslip-cd68a2880db1400ae09ce0df64994b2bae33a3c1.zip
Implemented evaluation according to tests. Quite a bit of changes were required, see rest of commit message.
SExp::Quote was added to let the interpreter know whether a list should be evaluated or treated as a literal list. It still needs code to be added for parsing it successfully. Some utility functions were needed: * SExp::is_value * SExp::consists_of_values * SExp::into_vec
Diffstat (limited to 'src/sexp/step.rs')
-rw-r--r--src/sexp/step.rs111
1 files changed, 96 insertions, 15 deletions
diff --git a/src/sexp/step.rs b/src/sexp/step.rs
index 4391bb9..0989ee3 100644
--- a/src/sexp/step.rs
+++ b/src/sexp/step.rs
@@ -86,24 +86,105 @@ impl SExp {
pub fn step(self) -> Result<Self, String> {
match self {
- SCons(op, e1) if *op == Atom(Add)
- || *op == Atom(Sub)
- || *op == Atom(Mul)
- || *op == Atom(Div) => match *e1 {
- SCons(e1, e2) => match (*e1, *e2) {
- (Atom(Int(a)), Atom(Int(b))) => match *op {
- Atom(Add) => Ok(Atom(Int(a + b))),
- Atom(Sub) => Ok(Atom(Int(a - b))),
- Atom(Mul) => Ok(Atom(Int(a * b))),
- Atom(Div) => Ok(Atom(Int(a / b))),
- _ => panic!("unreachable as per match arm"),
+ // List processing
+
+ // op not a value
+ // -----------------------
+ // (op x) -> (op.step() x)
+ SCons(op, l) if !(*op).is_value() => Ok(scons(op.step()?, l)),
+
+
+ // op value and a1 .. an values, and b0, b1 .. bn not values
+ // ---------------------------------------------------------
+ // (op a1 ... an b) -> (op a1 ... an b0.step() b1 .. bn)
+ SCons(op, l) if !(*l).consists_of_values() => {
+ fn inner(s: SExp) -> Result<SExp, String> {
+ match s {
+ SCons(a, b) if (*a).is_value() => Ok(scons(a, inner(*b)?)),
+ SCons(a, b) => Ok(scons(a.step()?, b)),
+ x => x.step()
+ }
+ }
+ Ok(scons(op, inner(*l)?))
+ },
+
+
+ // Arithmetic
+
+
+ // t1, ..., tn integers
+ // ------------------------------
+ // (+ t1 ... tn) -> t1 + ... + tn
+ SCons(op, l) if *op == Atom(Add) => l.into_vec()?
+ .iter()
+ .fold(
+ Ok(0),
+ |acc, el| {
+ match el {
+ Int(x) => acc.map(|v| x+v),
+ _ => Err(
+ "'+' should only be given integers".to_string()
+ )
}
- (Atom(Int(a)), e2) => Ok(scons(op, scons(a, e2.step()?))),
- (e1, e2) => Ok(scons(op, scons(e1.step()?, e2))),
- },
- _ => Err(format!("{:?} should be given two arguments, only one was given: {:?}", op, e1)),
+ }
+ ).map(|x| Atom(Int(x))),
+
+
+ // t1, ..., tn integers
+ // ------------------------------
+ // (* t1 ... tn) -> t1 * ... * tn
+ SCons(op, l) if *op == Atom(Mul) => l.into_vec()?
+ .iter()
+ .fold(
+ Ok(1),
+ |acc, el| {
+ match el {
+ Int(x) => acc.map(|v| x*v),
+ _ => Err(
+ format!("'*' should only be given integers, found {:?}", el)
+ )
+ }
+ }
+ ).map(|x| Atom(Int(x))),
+
+
+ // t1,t2 integers
+ // --------------------
+ // (- t1 t2) -> t1 - t2
+ SCons(op, l) if *op == Atom(Sub) => {
+ let ls = l.into_vec()?;
+ if ls.len() == 2 {
+ match (ls[0].clone(), ls[1].clone()) {
+ (Int(a), Int(b)) => Ok(Atom(Int(a - b))),
+ _ => Err("'-' should be only given integers".to_string())
+ }
+ } else {
+ Err(format!("'-' should be given 2 arguments, found {}", ls.len()))
+ }
},
+
+ // t1,t2 integers
+ // --------------------
+ // (/ t1 t2) -> t1 / t2
+ SCons(op, l) if *op == Atom(Div) => {
+ let ls = l.into_vec()?;
+ if ls.len() == 2 {
+ match (ls[0].clone(), ls[1].clone()) {
+ (_, Int(0)) => Err("division by zero".to_string()),
+ (Int(a), Int(b)) => Ok(Atom(Int(a / b))),
+ _ => Err("'/' should be only given integers".to_string())
+ }
+ } else {
+ Err(format!("'/' should be given 2 arguments, found {}", ls.len()))
+ }
+ },
+
+ // t is value
+ // ----------
+ // t -> t
+ t if t.is_value() => Ok(t),
+
t => Err(format!("unimplemented: {:?}.step()", t)),
}
}