From cd68a2880db1400ae09ce0df64994b2bae33a3c1 Mon Sep 17 00:00:00 2001 From: Joel Kronqvist Date: Wed, 30 Jul 2025 17:27:15 +0300 Subject: 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 --- src/sexp/step.rs | 111 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 96 insertions(+), 15 deletions(-) (limited to 'src/sexp/step.rs') 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 { 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 { + 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)), } } -- cgit v1.2.3