diff options
author | Joel Kronqvist <joel.kronqvist@iki.fi> | 2025-07-30 17:27:15 +0300 |
---|---|---|
committer | Joel Kronqvist <joel.kronqvist@iki.fi> | 2025-07-30 17:27:15 +0300 |
commit | cd68a2880db1400ae09ce0df64994b2bae33a3c1 (patch) | |
tree | 04893ee79ed16b920e7129b4eab1298a62814d65 /src/sexp/step.rs | |
parent | 8e2ca4bbc356d87ff5a37f20bea4f5b3cc561041 (diff) | |
download | myslip-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.rs | 111 |
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)), } } |