diff --git a/examples/tmp.rs b/examples/tmp.rs
index bde570e67889947cf38bbd8fb21e6c4feda3c652..3a1280cf6f9b7357cc6dbf7fe88ee1db325075a6 100644
--- a/examples/tmp.rs
+++ b/examples/tmp.rs
@@ -1,8 +1,24 @@
 use crust::{
     ast::Span,
-    parse::{parse_assign, test},
+    parse::{parse_assign, parse_expr},
+    interpreter::{eval_expr}
 };
 
+fn test(s: &str, v: i32) {
+    match parse_expr(Span::new(s)) {
+        Ok((Span { fragment: "", .. }, e)) => {
+            println!("{:?}", &e);
+            println!("eval {} {}", eval_expr(&e), v);
+            assert_eq!(eval_expr(&e), v);
+        }
+        Ok((s, t)) => println!(
+            "parse incomplete, \n parsed tokens \t{:?}, \n remaining \t{:?}",
+            t, s
+        ),
+        Err(err) => println!("{:?}", err),
+    }
+}
+
 fn main() {
     // test("- -1 + + 1", - -1 + 1);  // rust does not allow + as a unary op (I do ;)
     // test("(-1-1)+(-1+3)", (-1 - 1) + (-1) + 3);
@@ -12,6 +28,6 @@ fn main() {
     // test("1*2+3", 1 * 2 + 3);
     // // just to check that we get a parse error
     // test("1*2+3+3*21-a12+2", 1 * 2 + 3 + 3 * 21 - 12 + 2);
-    test("1 + a(1, 2)", 1 + 2);
-    println!("{:?}", parse_assign(Span::new("3 = 4")));
+    test("1 + (1 - 2)", 1 + (1 - 2) );
+    println!("{:?}", parse_assign(Span::new("3 = a(1, 2+3)")));
 }
diff --git a/src/ast.rs b/src/ast.rs
index f29611295a093d976ebed08940ce46e175920432..f445e1ceeb0a4b44abe3811201afc429adb00cfa 100644
--- a/src/ast.rs
+++ b/src/ast.rs
@@ -26,7 +26,7 @@ pub enum Expr<'a> {
     Bool(bool),
     Par(Box<SpanExpr<'a>>),
     Id(String),
-    Fn(String, Vec<SpanExpr<'a>>),
+    Call(String, Vec<SpanExpr<'a>>),
     BinOp(Op, Box<SpanExpr<'a>>, Box<SpanExpr<'a>>),
     UnaryOp(Op, Box<SpanExpr<'a>>),
 }
@@ -39,8 +39,8 @@ pub enum Cmd<'a> {
     Let(SpanMut<'a>, String, SpanType<'a>, SpanExpr<'a>),
     // id = expr
     Assign(SpanExpr<'a>, SpanExpr<'a>),
-    // /// if predicate do-this, and optionally do-that)
-    // If(Expr, Block, Option<Block>),
+    // if predicate do-this, and optionally do-that)
+    If(SpanExpr<'a>, SpanBlock<'a>, Option<SpanBlock<'a>>),
     // /// while predicate do-this
     // While(Expr, Block),
     // Return(Expr),
@@ -70,6 +70,7 @@ pub type SpanMut<'a> = (Span<'a>, Mutability);
 // }
 
 pub type SpanBlock<'a> = (Span<'a>, Vec<SpanCmd<'a>>);
+pub type Block<'a> = Vec<Cmd<'a>>;
 
 #[derive(Debug, PartialEq, Clone)]
 pub enum Type<'a> {
diff --git a/src/interpreter.rs b/src/interpreter.rs
new file mode 100644
index 0000000000000000000000000000000000000000..3531dcd241e93e0319d71bc655e062e02c73c61a
--- /dev/null
+++ b/src/interpreter.rs
@@ -0,0 +1,332 @@
+// Interpreter
+use crate::ast::{Expr, Op, SpanExpr};
+
+pub fn eval_expr(e: &SpanExpr) -> i32 {
+    match e.clone().1 {
+        Expr::Num(i) => i,
+        Expr::BinOp(op, l, r) => {
+            let lv = eval_expr(&l);
+            let rv = eval_expr(&r);
+            match op {
+                Op::Add => lv + rv,
+                Op::Sub => lv - rv,
+                Op::Mul => lv * rv,
+                Op::Div => lv / rv,
+                Op::Pow => lv.pow(rv as u32),
+                _ => unimplemented!(),
+            }
+        }
+        Expr::UnaryOp(op, e) => {
+            let e = eval_expr(&e);
+            match op {
+                Op::Add => e,
+                Op::Sub => -e,
+                _ => unimplemented!(),
+            }
+        }
+        _ => unimplemented!(),
+    }
+}
+
+// use crate::ast::{Binop, Cmd, Constant, Expr, Item, Prog, TypeDecl};
+// use crate::check::{check_prog, Fenv, Tenv};
+// use crate::parse::parse_prog;
+
+//use std::collections::HashMap;
+
+// pub type Addr = u32;
+
+// #[derive(Debug, PartialEq, Clone)]
+// pub enum Data {
+//     Value(Constant),
+//     Pointer(Addr),
+// }
+
+// pub type Venv = HashMap<String, Addr>;
+// pub type Menv = HashMap<Addr, Data>;
+
+// #[derive(Debug, PartialEq, Clone)]
+// pub struct Mem {
+//     pub Addr: u32,
+//     pub Menv: Menv,
+// }
+
+// impl Mem {
+//     fn new() -> Mem {
+//         Mem {
+//             Addr: 0,
+//             Menv: Menv::new(),
+//         }
+//     }
+
+//     fn alloc(&mut self) -> Addr {
+//         // allocate a new address
+//         self.Addr += 1;
+//         self.Addr
+//     }
+// }
+
+// pub fn get_bool(d: Data) -> bool {
+//     if let Data::Value(Constant::Boolean(b)) = d {
+//         b
+//     } else {
+//         panic!("cannot evaluate into Boolean");
+//     }
+// }
+
+// pub fn get_i32(d: Data) -> i32 {
+//     if let Data::Value(Constant::Num(i)) = d {
+//         i
+//     } else {
+//         panic!("cannot evaluate into i32");
+//     }
+// }
+
+// pub fn eval_expr(exp: &Expr, mem: &mut Mem, venv: &Venv, fenv: &Fenv) -> Data {
+//     println!("\neval_expr {:?}, mem {:?}, venv {:?}", exp, mem, venv);
+//     match exp {
+//         Expr::Constant(c) => Data::Value(c.clone()),
+//         Expr::Binop(e1, op, e2) => {
+//             let ev1 = eval_expr(e1, mem, venv, fenv);
+//             let ev2 = eval_expr(e2, mem, venv, fenv);
+//             Data::Value(match op {
+//                 Binop::And => Constant::Boolean(get_bool(ev1) && get_bool(ev2)),
+//                 Binop::Or => Constant::Boolean(get_bool(ev1) && get_bool(ev2)),
+//                 Binop::Equal => Constant::Boolean(ev1 == ev2),
+//                 Binop::Less => Constant::Boolean(get_i32(ev1) < get_i32(ev2)),
+//                 Binop::LessEqual => Constant::Boolean(get_i32(ev1) <= get_i32(ev2)),
+//                 Binop::Greater => Constant::Boolean(get_i32(ev1) > get_i32(ev2)),
+//                 Binop::GreaterEqual => Constant::Boolean(get_i32(ev1) >= get_i32(ev2)),
+//                 Binop::Divide => Constant::Num(get_i32(ev1) / get_i32(ev2)),
+//                 Binop::Reminder => Constant::Num(get_i32(ev1) % get_i32(ev2)),
+//                 Binop::Minus => Constant::Num(get_i32(ev1) - get_i32(ev2)),
+//                 Binop::Plus => Constant::Num(get_i32(ev1) + get_i32(ev2)),
+//                 Binop::Times => Constant::Num(get_i32(ev1) * get_i32(ev2)),
+//             })
+//         }
+//         Expr::Id(id) => {
+//             let e = mem.Menv.get(venv.get(id).unwrap()).unwrap();
+//             println!("{:?} -> {:?}", id, e);
+//             e.to_owned()
+//         }
+//         Expr::Not(e) => {
+//             let ev = eval_expr(e, mem, venv, fenv);
+//             Data::Value(Constant::Boolean(!get_bool(ev)))
+//         }
+//         Expr::Application(id, exprs) => {
+//             // evaluate arguments
+//             println!("application {:?}", id);
+//             let args: Vec<Data> = exprs
+//                 .into_iter()
+//                 .map(|e| eval_expr(e, mem, venv, fenv))
+//                 .collect();
+//             println!("args {:?}", args);
+//             // lookup callee
+//             let f = fenv.get(id).unwrap();
+//             println!("f {:?}", &f);
+//             let parameter_names: Vec<String> =
+//                 f.sig.1.clone().into_iter().map(|idt| idt.0).collect();
+//             println!("f par_names {:?}", &parameter_names);
+//             let mut lenv = Venv::new(); // local environment for function application
+//             let arg_assign: Vec<(String, Data)> =
+//                 parameter_names.into_iter().zip(args.into_iter()).collect();
+
+//             println!("arg assignments {:?}", &arg_assign);
+
+//             for (id, val) in arg_assign {
+//                 let addr = mem.alloc(); // get new allocation slot
+//                 mem.Menv.insert(addr, val); // write the new value
+//                 lenv.insert(id, addr);
+//             }
+//             println!("local enviroment {:?}", &lenv);
+//             println!("memory {:?}", &mem);
+
+//             // execute function, unwrap the result as we need a Constant
+//             eval_body(f.body.clone(), mem, &mut lenv, fenv).unwrap()
+//         }
+//         Expr::Ref(exp) => {
+//             println!("here");
+//             match *exp.to_owned() {
+//                 Expr::Id(id) => {
+//                     println!("id {:?}", &id);
+//                     let addr = venv.get(&id).unwrap();
+//                     Data::Pointer(*addr)
+//                 }
+//                 _ => {
+//                     let val = eval_expr(exp, mem, venv, fenv);
+//                     println!("-- value {:?}", &val);
+//                     let addr = mem.alloc(); // get new allocation slot
+//                     mem.Menv.insert(addr, val.to_owned()); // write the new value
+//                     let ref_val = Data::Pointer(addr);
+//                     println!(
+//                         "Ref exp {:?} e {:?} mem {:?} venv {:?}",
+//                         exp, val, mem, venv
+//                     );
+//                     ref_val
+//                 }
+//             }
+//         }
+//         Expr::RefMut(exp) => {
+//             println!("here");
+//             match *exp.to_owned() {
+//                 Expr::Id(id) => {
+//                     println!("id {:?}", &id);
+//                     let addr = venv.get(&id).unwrap();
+//                     Data::Pointer(*addr)
+//                 }
+//                 _ => {
+//                     let val = eval_expr(exp, mem, venv, fenv);
+//                     println!("-- value {:?}", &val);
+//                     let addr = mem.alloc(); // get new allocation slot
+//                     mem.Menv.insert(addr, val.to_owned()); // write the new value
+//                     let ref_val = Data::Pointer(addr);
+//                     println!(
+//                         "Ref exp {:?} e {:?} mem {:?} venv {:?}",
+//                         exp, val, mem, venv
+//                     );
+//                     ref_val
+//                 }
+//             }
+//         }
+//         Expr::Deref(exp) => {
+//             println!("-- Deref");
+//             let e = eval_expr(exp, mem, venv, fenv);
+//             println!("-- DereRef {:?} {:?}", exp, e);
+//             if let Data::Pointer(addr) = e {
+//                 mem.Menv.get(&addr).unwrap().to_owned()
+//             } else {
+//                 panic!("cannot deref {:?}", e);
+//             }
+//         }
+//         _ => unimplemented!(),
+//     }
+// }
+// pub fn eval_lvalue(exp: &Expr, mem: &mut Mem, venv: &Venv, fenv: &Fenv) -> Addr {
+//     println!("eval_lvalue {:?},{:?},{:?},{:?} ", exp, mem, venv, fenv);
+//     match exp {
+//         Expr::Id(id) => {
+//             let addr = venv.get(id).unwrap();
+//             println!("addr {:?}", addr);
+//             addr.to_owned()
+//         }
+//         Expr::Deref(exp) => {
+//             let lv = eval_expr(exp, mem, venv, fenv);
+//             println!("lv {:?}", lv);
+//             match eval_expr(exp, mem, venv, fenv) {
+//                 Data::Pointer(addr) => addr,
+//                 _ => panic!("cannot deref {:?}", exp),
+//             }
+//         }
+//         _ => unimplemented!(),
+//     }
+// }
+
+// // commands may return with a value
+// // either directly (return) or
+// // if inside an inner block (then/else, or while)
+// pub fn menv_update(data: Data, menv: &mut Menv) {
+//     // match data {
+//     //     Pointer::
+//     // }
+// }
+
+// pub fn dump(msg: &str, mem: &Mem, venv: &Venv) {
+//     println!("{:?} {:?} {:?}", msg, mem, venv);
+// }
+
+// // A return genaretes Some(Data) else None
+// pub fn eval_cmd(cmd: &Cmd, mem: &mut Mem, venv: &mut Venv, fenv: &Fenv) -> Option<Data> {
+//     println!("{:?}", cmd);
+//     match cmd {
+//         Cmd::Assign(lexp, rexp) => {
+//             let rval = eval_expr(rexp, mem, venv, fenv);
+//             println!("val {:?}", rval);
+//             let addr = eval_lvalue(lexp, mem, venv, fenv);
+//             // println!("lval {:?}", lval);
+//             // let addr = venv.get(&lval).unwrap();
+
+//             mem.Menv.insert(addr, rval);
+//             None
+//         }
+//         Cmd::If(exp, then_block, opt_else) => {
+//             if get_bool(eval_expr(exp, mem, venv, fenv)) {
+//                 eval_body(then_block.to_vec(), mem, venv, fenv)
+//             } else {
+//                 if let Some(else_block) = opt_else {
+//                     eval_body(else_block.to_vec(), mem, venv, fenv)
+//                 } else {
+//                     None
+//                 }
+//             }
+//         }
+//         Cmd::Let(_, id, _, exp) => {
+//             let val = eval_expr(exp, mem, venv, fenv);
+//             println!("val {:?}", val);
+
+//             let addr = mem.alloc(); // get new allocation slot
+//             mem.Menv.insert(addr, val); // write the new value
+//             venv.insert(id.to_owned(), addr);
+//             dump("after Let", mem, venv);
+//             None
+//         }
+//         Cmd::Return(exp) => {
+//             let v = Some(eval_expr(exp, mem, venv, fenv));
+//             println!("return value {:?}", v);
+//             v
+//         }
+//         Cmd::While(exp, body) => {
+//             while get_bool(eval_expr(exp, mem, venv, fenv)) {
+//                 if let Some(retv) = eval_body(body.to_vec(), mem, venv, fenv) {
+//                     return Some(retv);
+//                 }
+//             }
+//             None
+//         }
+//     }
+// }
+
+// pub fn eval_body(cmds: Vec<Cmd>, mem: &mut Mem, venv: &mut Venv, fenv: &Fenv) -> Option<Data> {
+//     for c in &cmds {
+//         if let Some(ret) = eval_cmd(c, mem, venv, fenv) {
+//             return Some(ret);
+//         }
+//     }
+//     None
+// }
+
+// pub fn build_env(prog: Prog) -> (Tenv, Fenv) {
+//     let mut tenv = Tenv::new();
+//     let mut fenv = Fenv::new();
+
+//     for i in prog {
+//         match i {
+//             Item::TypeDecl(TypeDecl::Struct(id, layout)) => {
+//                 tenv.insert(id.clone(), TypeDecl::Struct(id, layout));
+//             }
+//             Item::Function(f) => {
+//                 fenv.insert(f.sig.0.to_owned(), f);
+//             }
+//         }
+//     }
+//     (tenv, fenv)
+// }
+
+// pub fn eval_prog(prog: &str) {
+//     let (unparsed, prog) = parse_prog(prog).unwrap();
+//     println!("prog: {:?}", prog);
+//     println!("unparsed: {:?}", unparsed);
+//     let (tenv, fenv) = check_prog(&prog);
+
+//     println!("envs {:?}", (tenv, &fenv));
+
+//     // assume main does not take any parameters
+//     let call_main = Expr::Application("main".to_owned(), Vec::<Expr>::new());
+
+//     let mut mem = Mem::new();
+//     let mut venv = Venv::new();
+//     let ret = eval_expr(&call_main, &mut mem, &mut venv, &fenv);
+//     println!("return from main = {:?}", ret);
+
+//     println!("venv = {:?}", venv);
+//     println!("mem = {:?}", mem);
+// }
diff --git a/src/lib.rs b/src/lib.rs
index b5afecafe3d0a66ea3b66ae31359b41646bb88aa..56c2ccd7d9cf7c59a13e95a1ffe8f49897afc74e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,3 +2,4 @@
 
 pub mod ast;
 pub mod parse;
+pub mod interpreter;
diff --git a/src/parse.rs b/src/parse.rs
index 95e21051bbf1d11dac5cc4da68476d24ace92ffd..66e982388379fdecd89370df9e0c0ef96c4da8c3 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -6,15 +6,15 @@ use std::slice::Iter;
 use nom::{
     branch::alt,
     bytes::complete::tag,
-    character::complete::{alpha1, char, digit1, multispace0},
-    combinator::{cut, map},
+    character::complete::{alpha1, char, digit1, multispace0, multispace1},
+    combinator::{cut, map, opt},
     error::ParseError,
     multi::{many1, separated_list},
-    sequence::{delimited, preceded, tuple},
+    sequence::{delimited, preceded, terminated, tuple},
     IResult,
 };
 
-use crate::ast::{Cmd, Expr, Op, Span, SpanCmd, SpanExpr};
+use crate::ast::{Cmd, Expr, Op, Span, SpanCmd, SpanExpr, Block};
 
 pub fn parse_i32(i: Span) -> IResult<Span, (Span, i32)> {
     map(digit1, |digit_str: Span| {
@@ -42,7 +42,7 @@ pub enum Token<'a> {
     Num(i32),
     Bool(bool),
     Id(String),
-    Fn(String, Vec<(Span<'a>, Vec<SpanToken<'a>>)>),
+    Call(String, Vec<(Span<'a>, Vec<SpanToken<'a>>)>),
     Par(Vec<SpanToken<'a>>),
     Op(Op),
 }
@@ -54,9 +54,10 @@ fn parse_terminal(i: Span) -> IResult<Span, SpanToken> {
         map(parse_i32, |(s, v)| (s, Token::Num(v))),
         map(tag("true"), |s| (s, Token::Bool(true))),
         map(tag("false"), |s| (s, Token::Bool(false))),
-        map(tuple((alpha1, parse_par(separated_list(char(','), parse_tokens)))), |(s, t)| {
-            (s, Token::Fn(s.to_string(), t))
-         }),
+        map(
+            tuple((alpha1, parse_par(separated_list(char(','), parse_tokens)))),
+            |(s, t)| (s, Token::Call(s.to_string(), t)),
+        ),
         map(alpha1, |s: Span| (s, Token::Id(s.to_string()))),
         map(parse_par(parse_tokens), |(s, tokens)| {
             (s, Token::Par(tokens))
@@ -82,17 +83,20 @@ fn compute_atom<'a>(t: &mut Peekable<Iter<SpanToken<'a>>>) -> SpanExpr<'a> {
         Some((s, Token::Bool(b))) => (*s, Expr::Bool(*b)),
         Some((s, Token::Id(id))) => (*s, Expr::Id(id.to_string())),
         Some((_, Token::Par(v))) => climb(&mut v.iter().peekable(), 0),
-        Some((s, Token::Op(op))) => {
-            (*s, Expr::UnaryOp(*op, Box::new(climb(t, 4))))
-        } // assume highest precedence
+        Some((s, Token::Call(id, vv))) => {
+            //
+            let v: Vec<SpanExpr> = vv
+                .iter()
+                .map(|(span, t)| climb(&mut (*t).iter().peekable(), 0))
+                .collect();
+            (*s, Expr::Call(id.to_string(), v))
+        }
+        Some((s, Token::Op(op))) => (*s, Expr::UnaryOp(*op, Box::new(climb(t, 4)))), // assume highest precedence
         _ => panic!("error in compute atom"),
     }
 }
 
-fn climb<'a>(
-    t: &mut Peekable<Iter<SpanToken<'a>>>,
-    min_prec: u8,
-) -> SpanExpr<'a> {
+fn climb<'a>(t: &mut Peekable<Iter<SpanToken<'a>>>, min_prec: u8) -> SpanExpr<'a> {
     let mut result: SpanExpr = compute_atom(t);
 
     loop {
@@ -125,35 +129,7 @@ pub fn parse_expr(i: Span) -> IResult<Span, SpanExpr> {
     })(i)
 }
 
-pub fn test(s: &str, v: i32) {
-    match parse_expr(Span::new(s)) {
-        Ok((Span { fragment: "", .. }, e)) => {
-            println!("{:?}", &e);
-            println!("eval {} {}", math_eval(&e), v);
-            assert_eq!(math_eval(&e), v);
-        }
-        Ok((s, t)) => println!(
-            "parse incomplete, \n parsed tokens \t{:?}, \n remaining \t{:?}",
-            t, s
-        ),
-        Err(err) => println!("{:?}", err),
-    }
-}
-
-// helpers
-fn parse_par<'a, O, F, E>(
-    inner: F,
-) -> impl Fn(Span<'a>) -> IResult<Span<'a>, O, E>
-where
-    F: Fn(Span<'a>) -> IResult<Span<'a>, O, E>,
-    E: ParseError<Span<'a>>,
-{
-    // delimited allows us to split up the input
-    // cut allwos us to consume the input (and prevent backtracking)
-    delimited(char('('), preceded(multispace0, inner), cut(char(')')))
-}
-
-// fn parse_if<'a>(i: Span) -> IResult<Span, SpanCmd> {
+// fn parse_if(i: Span) -> IResult<Span, SpanCmd> {
 //     map(
 //         preceded(
 //             // here to avoid ambiguity with other names starting with `if`, if we added
@@ -172,6 +148,27 @@ where
 //     )(i)
 // }
 
+// helpers
+fn parse_par<'a, O, F, E>(inner: F) -> impl Fn(Span<'a>) -> IResult<Span<'a>, O, E>
+where
+    F: Fn(Span<'a>) -> IResult<Span<'a>, O, E>,
+    E: ParseError<Span<'a>>,
+{
+    // delimited allows us to split up the input
+    // cut allwos us to consume the input (and prevent backtracking)
+    delimited(char('('), preceded(multispace0, inner), cut(char(')')))
+}
+
+fn parse_sem<'a, O, F, E>(inner: F) -> impl Fn(Span<'a>) -> IResult<Span<'a>, O, E>
+where
+    F: Fn(Span<'a>) -> IResult<Span<'a>, O, E>,
+    E: ParseError<Span<'a>>,
+{
+    // delimited allows us to split up the input
+    // cut allwos us to consume the input (and prevent backtracking)
+    delimited(char('{'), preceded(multispace0, inner), cut(char('}')))
+}
+
 // pub fn parse_let(i: &str) -> IResult<&str, Cmd, VerboseError<&str>> {
 //     context(
 //         "let expression",
@@ -243,16 +240,18 @@ pub fn parse_assign<'a>(i: Span<'a>) -> IResult<Span<'a>, Cmd<'a>> {
 //     )(i)
 // }
 
-// pub fn parse_cmd(i: &str) -> IResult<&str, Cmd, VerboseError<&str>> {
-//     preceded(
-//         multispace0,
-//         alt((parse_while, parse_let, parse_if, parse_assign, parse_return)),
-//     )(i)
-// }
+// pub fn parse_cmd<'a>(i: Span<'a>) -> IResult<Span<'a>, Cmd<'a>> {
+pub fn parse_cmd(i: Span) -> IResult<Span, Cmd> {
+    preceded(
+        multispace0,
+        parse_assign,
+        // alt((parse_while, parse_let, parse_if, parse_assign, parse_return)),
+    )(i)
+}
 
-// pub fn parse_block(i: &str) -> IResult<&str, Block, VerboseError<&str>> {
-//     preceded(multispace0, s_cmd(separated_list(tag(";"), parse_cmd)))(i)
-// }
+pub fn parse_block(i: Span) -> IResult<Span, Block> {
+    preceded(multispace0, parse_sem(separated_list(tag(";"), parse_cmd)))(i)
+}
 
 // fn s_cmd<'a, O, F>(inner: F) -> impl Fn(&'a str) -> IResult<&'a str, O, VerboseError<&'a str>>
 // where
@@ -355,34 +354,6 @@ pub fn parse_assign<'a>(i: Span<'a>) -> IResult<Span<'a>, Cmd<'a>> {
 //     )(i)
 // }
 
-// tests
-fn math_eval(e: &SpanExpr) -> i32 {
-    match e.clone().1 {
-        Expr::Num(i) => i,
-        Expr::BinOp(op, l, r) => {
-            let lv = math_eval(&l);
-            let rv = math_eval(&r);
-            match op {
-                Op::Add => lv + rv,
-                Op::Sub => lv - rv,
-                Op::Mul => lv * rv,
-                Op::Div => lv / rv,
-                Op::Pow => lv.pow(rv as u32),
-                _ => unimplemented!(),
-            }
-        }
-        Expr::UnaryOp(op, e) => {
-            let e = math_eval(&e);
-            match op {
-                Op::Add => e,
-                Op::Sub => -e,
-                _ => unimplemented!(),
-            }
-        }
-        _ => unimplemented!(),
-    }
-}
-
 #[derive(Debug, Copy, Clone, PartialEq)]
 enum Ass {
     Left,