diff --git a/Cargo.lock b/Cargo.lock
index 04a459946e737bf6b02e4d92d8aaeb08e62cc608..b11e624f90930462f943132891cb93b6fd7154b1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -387,9 +387,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.21"
+version = "1.0.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c"
+checksum = "e4b93dba1818d32e781f9d008edd577bab215e83ef50e8a1ddf1ad301b19a09f"
 dependencies = [
  "unicode-xid",
 ]
diff --git a/examples/borrow.rs b/examples/borrow.rs
index 7e40ef0b4b7602100030e39753b6e14c2f36b25a..fe386e9b3542824299748f9b7a75ec2b6f81df62 100644
--- a/examples/borrow.rs
+++ b/examples/borrow.rs
@@ -1,6 +1,11 @@
-fn f(a: &i32, b: &mut bool) {}
+fn f(x: &i32, y: &mut bool) {
+    *y = true;
+    let c = *x;
+    // println!("{}", c);
+}
 fn main() {
     let mut a = 1;
     let mut b = false;
-    f(&a, &mut b)
+    f(&a, &mut b);
+    // println!("{}", b);
 }
diff --git a/examples/borrow2.rs b/examples/borrow2.rs
index f84101eb46b865ce539d4791beae9ec10f265342..f99b6d5d921895510c27eb954e2386a448e89a95 100644
--- a/examples/borrow2.rs
+++ b/examples/borrow2.rs
@@ -1,5 +1,22 @@
-fn f(a: &i32, b: &mut i32) {}
+// parameter a is of type &i32
+fn f(a: &i32, b: &mut bool) {
+    *b = true;
+    let c = *a;
+    //println!("{}", c);
+}
 fn main() {
     let mut a = 1;
-    f(&mut a, &mut a)
+    let mut b = false;
+    // argument &a, is bound to parameter a in function
+    f(&a, &mut b);
+    // println!("{}", b);
 }
+
+// here we don't want to shadow a in main by the
+// local a in f.
+
+// this is can be solved by "alpha renaming"
+// in a function f(x, ...) = x ...
+// x can be "renamed" to a new name y 
+// by changing all appearances of x to y
+// (as long as y don't appear in f)
\ No newline at end of file
diff --git a/examples/borrow_err.rs b/examples/borrow_err.rs
index 63157b53b2008d714818be0fa0c16f510968c76c..b540df16dcc3eb97b250678e64095a29088d3b6b 100644
--- a/examples/borrow_err.rs
+++ b/examples/borrow_err.rs
@@ -1,7 +1,10 @@
+#[derive(Debug, Copy, Clone)]
+struct A {}
 fn main() {
-    let mut a = 5;
+    let mut a = A {};
     let b = &mut a;
     //let q = *b;
-    let c = a;
+    let c = *b;
     println!("b {:?}", *b);
+    let d = a;
 }
diff --git a/examples/borrow_err2.rs b/examples/borrow_err2.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a732ba2bb48fc69ee4472c548caa068db1e6e03f
--- /dev/null
+++ b/examples/borrow_err2.rs
@@ -0,0 +1,6 @@
+fn main() {
+    let mut a = 5;
+    let b = &a;
+    a = 4;
+    let d = *b;
+}
diff --git a/examples/deref.rs b/examples/deref.rs
new file mode 100644
index 0000000000000000000000000000000000000000..08e49d742defbe0e546ab2560bfe26a36d2bb5d3
--- /dev/null
+++ b/examples/deref.rs
@@ -0,0 +1,4 @@
+fn main() {
+    let a = 1;
+    let b = *&a;
+}
diff --git a/examples/deref2.rs b/examples/deref2.rs
new file mode 100644
index 0000000000000000000000000000000000000000..5a038e84f6afae86fd3bc875597092a1f83d1bc9
--- /dev/null
+++ b/examples/deref2.rs
@@ -0,0 +1,5 @@
+fn main() {
+    let a = 1;
+    let b = &a;
+    let c = *b;
+}
diff --git a/examples/deref3.rs b/examples/deref3.rs
new file mode 100644
index 0000000000000000000000000000000000000000..727fe27379039309678514e80d8c5bd31f7c55b4
--- /dev/null
+++ b/examples/deref3.rs
@@ -0,0 +1,8 @@
+fn main() {
+    let mut a = 1;
+    let mut b = &a;
+    let c = &mut b;
+
+    let d = *c;
+    let e = *d;
+}
diff --git a/examples/deref_assign3.rs b/examples/deref_assign3.rs
index db7311ea1875d4ddad5e6bef83ded2949e084d1a..a7ee5030565eca454c359a334eef0ed546f73b7e 100644
--- a/examples/deref_assign3.rs
+++ b/examples/deref_assign3.rs
@@ -1,10 +1,9 @@
-fn main() -> i32 {
+fn main() {
     let mut a = 7;
     let mut aa = 8;
-    let mut b = &a;
-    let c = &b;
+    let mut b = &mut a;
+    let c = &mut b;
 
-    *c = &aa;
+    *c = &mut aa;
     *b = 99;
-    aa
 }
diff --git a/src/borrow.rs b/src/borrow.rs
index 5b4781cb06186ff498e15eeaffaac4c9198771c6..d0e990af6b854921198d365ca8265e53084c8a73 100644
--- a/src/borrow.rs
+++ b/src/borrow.rs
@@ -1,568 +1,370 @@
-use std::io::Write;
+// boorrow
 
-use env_logger::Builder;
-use log::{trace, LevelFilter};
-use std::convert::From;
+use std::collections::{HashMap, VecDeque};
 
 use crate::ast::*;
-use crate::bc_env::{Bc, BcEnv, Error, IdBc};
-use crate::env::{new_fn_env, new_type_env, FnEnv, TypeEnv};
+use crate::env::*;
+
+type Id = String;
+
+#[derive(Debug, Clone)]
+enum Val {
+    Num(i32),
+    Bool(bool),
+    Uninitialized,
+    Unit,
+    Ref(Id),
+    RefMut(Id),
+}
 
-// type check
+type IdVal = HashMap<Id, (bool, Val)>;
 
-fn bc_expr(
-    e: &Expr,
-    fn_env: &FnEnv,
-    type_env: &TypeEnv,
-    bc_env: &mut BcEnv,
-) -> Result<Type, Error> {
-    use Expr::*;
-    println!("expr_type {}", e);
-    println!("var_env {:?}", bc_env);
+#[derive(Debug)]
+struct Mem(VecDeque<IdVal>);
 
-    match e {
-        // Num(_) => Ok(Type::I32),
-        // Bool(_) => Ok(Type::Bool),
-        // Infix(l, op, r) => {
-        //     let lt = expr_type(l, fn_env, type_env, var_env)?;
-        //     let rt = expr_type(r, fn_env, type_env, var_env)?;
-
-        //     let lt = strip_mut(lt);
-        //     let rt = strip_mut(rt);
-
-        //     use Op::*;
-        //     match op {
-        //         // Arithmetic and Boolen
-        //         Add | Mul | Div | Sub | And | Or => {
-        //             // check if op and args are compliant
-        //             let opt = op.get_type();
-        //             if lt == opt && rt == opt {
-        //                 Ok(opt)
-        //             } else {
-        //                 Err(format!(
-        //                     "Expected type {}, found, left {}: {}, {:?}  right {}: {}, {:?}",
-        //                     opt, l, lt, lt, r, rt, rt
-        //                 ))
-        //             }
-        //         }
-        //         // Equality
-        //         Eq | Neq => {
-        //             // check if args are of same type
-        //             if lt == rt {
-        //                 Ok(Type::Bool)
-        //             } else {
-        //                 Err(format!(
-        //                     "Comparison requires operands of same type, left {}, right {}",
-        //                     lt, rt
-        //                 ))
-        //             }
-        //         }
-        //         // Comparison
-        //         Less | Greater | LessEq | GreaterEq => {
-        //             if lt == Type::I32 && rt == Type::I32 {
-        //                 // check if args are of i32
-        //                 Ok(Type::Bool)
-        //             } else {
-        //                 Err(format!(
-        //                     "Comparison requires operands of same type, left {}, right {}",
-        //                     lt, rt
-        //                 ))
-        //             }
-        //         }
-        //         _ => panic!("ICE on {}", e),
-        //     }
-        // }
-
-        // Prefix(op, r) => {
-        //     let rt = expr_type(r, fn_env, type_env, var_env)?;
-        //     let opt = op.get_type();
-
-        //     // check if both of same type
-        //     if rt == opt {
-        //         Ok(opt)
-        //     } else {
-        //         Err(format!("op {} rt {}", opt, rt))
-        //     }
-        // }
-
-        // Call(s, args) => {
-        //     trace!("call {} with {}", s, args);
-
-        //     let arg_t: Vec<Type> = args
-        //         .clone()
-        //         .0
-        //         .into_iter()
-        //         .map(|e| expr_type(&*e, fn_env, type_env, var_env))
-        //         .collect::<Result<_, _>>()?;
-
-        //     trace!("arg types {:?}", arg_t);
-        //     let f = match fn_env.get(s.as_str()) {
-        //         Some(f) => f,
-        //         None => Err(format!("{} not found", s))?,
-        //     };
-
-        //     let par_t: Vec<Type> = (f.params.0.clone())
-        //         .into_iter()
-        //         .map(|p| From::from(&p))
-        //         .collect();
-
-        //     trace!(
-        //         "fn to call {} with params {} and types {:?}",
-        //         f,
-        //         f.params,
-        //         par_t
-        //     );
-
-        //     if arg_t == par_t {
-        //         Ok(f.result.clone())
-        //     } else {
-        //         Err(format!(
-        //             "arguments types {:?} does not match parameter types {:?}",
-        //             arg_t, par_t
-        //         ))
-        //     }
-        // }
-
-        // Id(id) => match var_env.get(id.to_string()) {
-        //     Some(t) => Ok(t.clone()),
-        //     None => Err(format!("variable not found {}", id)),
-        // },
-
-        // As(_e, _t) => unimplemented!("here we implement explicit type cast"),
-
-        // // Convert Expr::Ref to Type::Ref
-        // Ref(ref_e) => {
-        //     let t = expr_type(ref_e, fn_env, type_env, var_env)?;
-        //     trace!("ref_e {}, t {}", ref_e, t);
-        //     let t = match t {
-        //         Type::Mut(t) => t.clone(),
-        //         t => Box::new(t),
-        //     };
-        //     Ok(Type::Ref(t))
-        // }
-
-        // // Convert Expr::Mut to Type::Mut
-        // RefMut(ref_mut_e) => {
-        //     let t = expr_type(ref_mut_e, fn_env, type_env, var_env)?;
-        //     match t {
-        //         Type::Mut(_) => Ok(Type::Ref(Box::new(t))),
-        //         _ => Err(format!("{} is not mutable in {}", t, ref_mut_e)),
-        //     }
-        // }
-
-        // DeRef(deref_e) => {
-        //     let t = expr_type(deref_e, fn_env, type_env, var_env)?;
-        //     trace!("deref_t {}", &t);
-        //     let t = strip_mut(t);
-        //     trace!("strip deref_t {}", &t);
-        //     match t {
-        //         Type::Ref(dt) => Ok(*dt),
-        //         _ => Err(format!("cannot deref {} of type {}", e, t)),
-        //     }
-        // }
-
-        // Block(b) => check_stmts(b, fn_env, type_env, var_env),
-
-        // Stmt(s) => check_stmt(s, fn_env, type_env, var_env),
-        _ => unimplemented!(),
+impl Mem {
+    fn new() -> Self {
+        Mem(VecDeque::new())
     }
-}
 
-// fn lexpr_type<'a>(
-//     e: &Expr,
-//     fn_env: &FnEnv,
-//     type_env: &TypeEnv,
-//     var_env: &'a mut VarEnv,
-// ) -> Result<&'a mut Type, Error> {
-//     use Expr::*;
-//     trace!("expr_type {}", e);
-//     trace!("var_env {:?}", var_env);
-
-//     match e {
-//         Id(id) => match var_env.get_mut(id.to_string()) {
-//             Some(t) => Ok(t),
-//             None => Err(format!("variable not found {}", id)),
-//         },
-
-//         DeRef(deref_e) => {
-//             let t = lexpr_type(deref_e, fn_env, type_env, var_env)?;
-//             trace!("deref_t {}", &t);
-
-//             // strip mut
-//             let t = match t {
-//                 Type::Mut(t) => t,
-//                 _ => t,
-//             };
-//             trace!("strip deref_t {}", t);
-//             match t {
-//                 Type::Ref(dt) => Ok(dt),
-//                 _ => Err(format!("cannot deref {} of type {}", e, t)),
-//             }
-//         }
-//         _ => Err(format!("Illegal left hand expression {}", e)),
-//     }
-// }
-
-// strip a "mut T" to a T
-fn strip_mut(t: Type) -> Type {
-    match t {
-        Type::Mut(t) => *t,
-        _ => t,
+    fn get(&self, id: String) -> Option<&Val> {
+        self.0.iter().find_map(|hm| match hm.get(id.as_str()) {
+            Some((_, v)) => Some(v), // always ok to go from mut to non mut
+            _ => None,
+        })
     }
-}
 
-// determine if a type is declared recursively
-fn is_known(t: &Type) -> bool {
-    use Type::*;
-    match t {
-        Type::Unknown => false,
-        Unit | Bool | I32 => true,
-        Mut(t) | Ref(t) => is_known(t),
-        _ => panic!("ICE is_known"),
+    fn get_mut(&mut self, id: String) -> Option<&mut Val> {
+        self.0
+            .iter_mut()
+            .find_map(|hm| match hm.get_mut(id.as_str()) {
+                Some((true, v)) => Some(v), // a mut reference
+                Some((_, v)) => {
+                    match v {
+                        Val::Uninitialized => Some(v), // an uninitialized
+                        Val::RefMut(_) => {
+                            // a ref mut
+                            println!("get &mut {:?}", v);
+                            Some(v)
+                        }
+                        _ => panic!("cannot access as mutable"),
+                    }
+                }
+                _ => None,
+            })
     }
-}
 
-pub fn bc_stmt(
-    s: &Stmt,
-    fn_env: &FnEnv,
-    type_env: &TypeEnv,
-    bc_env: &mut BcEnv,
-) -> Result<(), Error> {
-    use Stmt::*;
-    //     trace!("stmt: {}", s);
-    //     match s {
-    //         Stmt::Block(b) => check_stmts(b, fn_env, type_env, var_env),
-
-    //         Expr(e) => expr_type(&*e, fn_env, type_env, var_env),
-
-    //         Let(var_id, is_mut, ot, oe) => {
-    //             let t: Type = match (ot, oe) {
-    //                 (Some(t), Some(e)) => {
-    //                     let e_type = expr_type(&*e, fn_env, type_env, var_env)?;
-    //                     let e_type = strip_mut(e_type);
-    //                     match strip_mut(t.clone()) == e_type {
-    //                         true => t.clone(),
-    //                         false => {
-    //                             trace!("e {}", e);
-    //                             Err(format!("incompatible types, {} <> {}", t, e_type))?
-    //                         }
-    //                     }
-    //                 }
-    //                 (None, Some(e)) => {
-    //                     let e_type = strip_mut(expr_type(&*e, fn_env, type_env, var_env)?);
-    //                     match is_known(&e_type) {
-    //                         true => e_type,
-    //                         _ => Err("reference to unknown type".to_string())?,
-    //                     }
-    //                 }
-    //                 (Some(t), None) => t.clone(),
-    //                 _ => Type::Unknown,
-    //             };
-    //             let t = match is_mut {
-    //                 true => Type::Mut(Box::new(t)),
-    //                 false => t,
-    //             };
-    //             var_env.new_id(var_id.clone(), t);
-    //             trace!("var_env {:?}", var_env);
-    //             Ok(Type::Unit)
-    //         }
-
-    //         Assign(lh, e) => {
-    //             trace!("assign");
-
-    //             let e_type = expr_type(&*e, fn_env, type_env, var_env)?;
-    //             trace!("e_type = {}", &e_type);
-    //             let e_type = strip_mut(e_type);
-    //             trace!("e_type stripped = {}", &e_type);
-
-    //             trace!("lh expr = {:?}", lh);
-
-    //             let lh_type = lexpr_type(lh, fn_env, type_env, var_env)?;
-    //             trace!("lh_type {}", lh_type);
-
-    //             if match lh_type {
-    //                 Type::Unknown => {
-    //                     trace!("assign to unknown");
-    //                     *lh_type = e_type.clone();
-    //                     true
-    //                 }
-    //                 Type::Mut(t) => match **t {
-    //                     Type::Unknown => {
-    //                         trace!("assign to `mut Unknown`");
-    //                         *t = Box::new(e_type.clone());
-    //                         true
-    //                     }
-    //                     _ => **t == e_type,
-    //                 },
-
-    //                 _ => Err(format!("assignment to immutable"))?,
-    //             } {
-    //                 Ok(Type::Unit)
-    //             } else {
-    //                 Err(format!("cannot assign {} = {}", &lh_type, e_type))
-    //             }
-    //         }
-
-    //         While(e, block) => match expr_type(&*e, fn_env, type_env, var_env) {
-    //             Ok(Type::Bool) => {
-    //                 let _ = check_stmts(&block, fn_env, type_env, var_env)?;
-    //                 Ok(Type::Unit) // a while statement is of unit type;
-    //             }
-    //             _ => Err("Condition not Boolean".to_string()),
-    //         },
-
-    //         If(e, then, o_else) => match expr_type(&*e, fn_env, type_env, var_env)? {
-    //             Type::Bool => {
-    //                 // The condition is of Bool type
-    //                 let then_type = check_stmts(&then, fn_env, type_env, var_env)?;
-    //                 trace!("then type {}", then_type);
-    //                 match o_else {
-    //                     None => Ok(then_type), // type of the arm
-    //                     Some(else_stmts) => {
-    //                         let else_type = check_stmts(&else_stmts, fn_env, type_env, var_env)?;
-    //                         trace!("else type {}", else_type);
-
-    //                         match then_type == else_type {
-    //                             true => Ok(then_type), // same type of both arms
-    //                             false => {
-    //                                 trace!("error-----");
-    //                                 Err(format!(
-    //                                     "'then' arm :{} does not match 'else' arm :{}",
-    //                                     then_type, else_type
-    //                                 ))
-    //                             }
-    //                         }
-    //                     }
-    //                 }
-    //             }
-    //             _ => Err("Condition not Boolean".to_string()),
-    //         },
-    //         Semi => Ok(Type::Unit),
-    //     }
-    Ok(())
-}
+    fn new_id(&mut self, id: String, is_mut: bool) {
+        let hm = self.0.front_mut().unwrap();
+        println!("insert id {:?}, in scope {:?}", id, hm);
 
-pub fn bc_stmts(
-    stmts: &Stmts,
-    fn_env: &FnEnv,
-    type_env: &TypeEnv,
-    bc_env: &mut BcEnv,
-) -> Result<(), Error> {
-    bc_env.push_empty_scope();
-
-    let t = stmts
-        .stmts
-        .iter()
-        .try_fold((), |_, s| bc_stmt(s, fn_env, type_env, bc_env))?;
-
-    bc_env.pop_scope();
-    // if stmts.trailing_semi {
-    //     Ok(())
-    // } else {
-    //     Ok(t.clone())
-    // }
-
-    Ok(())
-}
+        hm.insert(id, (is_mut, Val::Uninitialized));
+    }
 
-pub fn build_env(p: &Program) -> (FnEnv, TypeEnv) {
-    let fn_env = new_fn_env(&p.fn_decls);
-    let type_env = new_type_env(&p.type_decls);
+    fn update(&mut self, id: String, val: Val) {
+        // println!("before mem {:?}", self);
+        match self.get_mut(id.clone()) {
+            Some(v_ref) => {
+                println!("found");
+                *v_ref = val;
+            }
+            None => {
+                panic!("variable not found");
+            }
+        };
+        // println!("after mem {:?}", self);
+    }
 
-    (fn_env, type_env)
-}
+    fn push_empty_scope(&mut self) {
+        self.0.push_front(HashMap::new());
+    }
 
-// pub fn dump_env(fn_env: &FnEnv, type_env: &TypeEnv) {
-//     trace!("fn_env {:?}", fn_env);
-//     trace!("type_env {:?}", type_env);
-// }
+    fn push_param_scope(&mut self, args: IdVal) {
+        self.0.push_front(args)
+    }
+
+    fn pop_scope(&mut self) {
+        self.0.pop_front();
+    }
+}
 
-// check a whole Program
-pub fn bc_prog(p: &Program) -> Result<(), Error> {
-    let (fn_env, type_env) = build_env(&p);
+fn eval_infix(l: Val, op: &Op, r: Val) -> Val {
+    match (l, op, r) {
+        // Operations on Num
+        (Val::Num(l), op, Val::Num(r)) => match op {
+            // arithmetic operations on Num
+            Op::Add => Val::Num(l + r),
+            Op::Sub => Val::Num(l - r),
+            Op::Mul => Val::Num(l * r),
+            Op::Div => Val::Num(l / r),
+            // comparisons
+            Op::Eq => Val::Bool(l == r),
+            Op::Neq => Val::Bool(l != r),
+            Op::Less => Val::Bool(l < r),
+            Op::LessEq => Val::Bool(l <= r),
+            Op::Greater => Val::Bool(l > r),
+            Op::GreaterEq => Val::Bool(l >= r),
+            _ => panic!("expression evaluation error"),
+        },
+        // boolean
+        (Val::Bool(l), op, Val::Bool(r)) => Val::Bool(match op {
+            Op::And => l && r,
+            Op::Or => l || r,
+            Op::Eq => l == r,
+            _ => panic!("expression evaluation error"),
+        }),
+        _ => panic!("expression evaluation error"),
+    }
+}
 
-    println!("Input program \n{}", &p);
+fn eval_prefix(op: &Op, r: Val) -> Val {
+    match (op, r) {
+        // Operations on Num
+        (op, Val::Num(r)) => match op {
+            // arithmetic operations on Num
+            Op::Sub => Val::Num(-r),
+            Op::Add => Val::Num(r),
+            _ => panic!("expression evaluation error"),
+        },
+        // boolean
+        (op, Val::Bool(r)) => match op {
+            Op::Not => Val::Bool(!r),
+            _ => panic!("expression evaluation error"),
+        },
+        // Unint
+        _ => panic!("expression evaluation error"),
+    }
+}
 
-    for fd in p.fn_decls.iter() {
-        println!("check function\n{}", fd);
-        println!("ast\n{:?}", fd);
+fn eval_expr(e: &Expr, m: &mut Mem, fn_env: &FnEnv) -> Val {
+    println!("eval_expr {}", e);
+    match e {
+        Expr::Num(i) => Val::Num(*i),
+        Expr::Bool(b) => Val::Bool(*b),
+        // Expr::As(_, _) => {}
+        Expr::Infix(l, op, r) => {
+            let l = eval_expr(l, m, fn_env);
+            let r = eval_expr(r, m, fn_env);
+            eval_infix(l, op, r)
+        }
+        Expr::Prefix(op, r) => {
+            let r = eval_expr(r, m, fn_env);
+            eval_prefix(op, r)
+        }
+        Expr::Id(id) => match m.get(id.to_owned()) {
+            Some(v) => v.clone(),
+            None => panic!("identifier not found {:?}", id),
+        },
+        Expr::Ref(e) => match &**e {
+            Expr::Id(id) => match m.get(id.to_owned()) {
+                Some(_) => Val::Ref(id.to_owned()),
+                None => panic!("identifier not found {:?}", id),
+            },
+            _ => panic!("ref of non identifier"),
+        },
+        Expr::RefMut(e) => match &**e {
+            Expr::Id(id) => match m.get(id.to_owned()) {
+                Some(_) => Val::RefMut(id.to_owned()),
+                None => panic!("identifier not found {:?}", id),
+            },
+            _ => panic!("ref on non identifier"),
+        },
+        Expr::DeRef(e) => {
+            println!("deref e {:?}", e);
+
+            let ev = eval_expr(e, m, fn_env);
+            println!("ev {:?}", ev);
+
+            match ev {
+                Val::Ref(id) => eval_expr(&Expr::Id(id), m, fn_env),
+                Val::RefMut(id) => eval_expr(&Expr::Id(id), m, fn_env),
+                _ => panic!("cannot deref"),
+            }
+        }
+        Expr::Call(id, ee) => {
+            println!("call {}", id);
+            let args: Vec<Val> = ee.0.iter().map(|e| eval_expr(e, m, fn_env)).collect();
 
-        let mut bc_env = BcEnv::new();
+            eval_fn(id, &args, m, fn_env)
+        }
+        _ => {
+            unimplemented!();
+        }
+    }
+}
 
-        // build a scope for the arguments
-        let mut arg_ty = IdBc::new();
+fn eval_left_expr(e: &Expr, m: &Mem, fn_env: &FnEnv) -> Id {
+    println!("eval_left_expr {}", e);
+    match e {
+        Expr::Id(id) => id.to_owned(),
+
+        Expr::DeRef(e) => {
+            println!("eval_left deref e {:?}", e);
+
+            let ev = eval_left_expr(e, m, fn_env);
+            println!("eval_left {:?}", ev);
+
+            match m.get(ev) {
+                Some(Val::Ref(id)) => {
+                    println!("Ref id {}", id);
+                    id.clone()
+                }
+                Some(Val::RefMut(id)) => {
+                    println!("RefMut id {}", id);
+                    id.clone()
+                }
+                _ => panic!("deref failed"),
+            }
+        }
+        _ => panic!("illegal left hand expression"),
+    }
+}
 
-        for Param { is_mut, id, ty } in fd.params.0.iter() {
-            let ty = match *is_mut {
-                true => Bc::Unique,
-                _ => Bc::Shared,
-            };
-            arg_ty.insert(id.to_owned(), ty);
+fn eval_stmts(stmts: &Stmts, m: &mut Mem, fn_env: &FnEnv) -> Val {
+    let ss = &stmts.stmts;
+
+    m.push_empty_scope();
+
+    let mut stmt_val = Val::Unit;
+
+    for s in ss {
+        println!("s {}", s);
+        println!("m {:?}", m);
+        stmt_val = match s {
+            // Id, is_mut, Option<Type>, Option<Box<Expr>
+            Stmt::Let(id, is_mut, _, o_expr) => {
+                println!("let");
+                m.new_id(id.to_owned(), *is_mut);
+                match o_expr {
+                    Some(e) => {
+                        let r = eval_expr(e, m, fn_env);
+                        println!("r {:?}", r);
+                        m.update(id.to_owned(), r);
+                        Val::Unit
+                    }
+                    _ => Val::Unit,
+                }
+            }
+            Stmt::Assign(ev, e) => {
+                println!("assign");
+                match &**ev {
+                    Expr::Id(id) => {
+                        let v = eval_expr(e, m, fn_env);
+                        m.update(id.to_owned(), v)
+                    }
+                    _ => {
+                        let lv = eval_left_expr(ev, m, fn_env);
+                        println!("lv {:?}", lv);
+                        let v = eval_expr(e, m, fn_env);
+                        m.update(lv, v)
+                    }
+                };
+                Val::Unit
+            }
+            Stmt::While(e, while_block) => {
+                println!("while");
+                while match eval_expr(e, m, fn_env) {
+                    Val::Bool(b) => b,
+                    _ => panic!("non boolean condition"),
+                } {
+                    eval_stmts(while_block, m, fn_env);
+                }
+                Val::Unit
+            }
+            Stmt::If(e, then_block, op_else_block) => {
+                println!("if");
+                // println!("scope before {}", m);
+                match (eval_expr(e, m, fn_env), op_else_block) {
+                    (Val::Bool(true), _) => eval_stmts(then_block, m, fn_env),
+                    (Val::Bool(false), Some(else_block)) => eval_stmts(else_block, m, fn_env),
+                    (Val::Bool(_), _) => Val::Uninitialized, // this should never happen if value used
+                    _ => panic!("non boolean condition"),
+                }
+            }
+            Stmt::Expr(e) => {
+                println!("expr");
+                eval_expr(e, m, fn_env)
+            }
+            Stmt::Block(block) => {
+                println!("block");
+                eval_stmts(block, m, fn_env)
+            }
+            Stmt::Semi => Val::Unit,
         }
+    }
+    m.pop_scope();
+    if stmts.trailing_semi {
+        Val::Unit
+    } else {
+        println!("statements with return value {:?}", stmt_val);
+        stmt_val
+    }
+}
 
-        //         var_env.push_param_scope(arg_ty);
+fn eval_fn(id: &str, args: &Vec<Val>, m: &mut Mem, fn_env: &FnEnv) -> Val {
+    if let Some(fn_decl) = fn_env.get(id) {
+        println!("f id {:?}, {:?}", id, fn_decl.params);
+
+        let p_id: &Vec<(bool, String)> = &fn_decl
+            .params
+            .0
+            .iter()
+            .map(|param| (param.is_mut, param.id.to_owned()))
+            .collect();
+
+        let p_id_arg: Vec<(&(bool, String), &Val)> = p_id.iter().zip(args).collect();
+        let p_id_arg: IdVal = p_id_arg
+            .into_iter()
+            .map(|(s, v)| (s.1.clone(), (s.0, v.clone())))
+            .collect();
+
+        println!("args {:?}", args);
+        println!("p_id_arg {:?}", &p_id_arg);
+
+        m.push_param_scope(p_id_arg);
+        let ret = eval_stmts(&fn_decl.body, m, fn_env);
+        println!("mem {:?}, ret {:?}", m, ret);
+        m.pop_scope();
+        ret
+    } else {
+        panic!("function not found");
+    }
+}
 
-        //         let stmt_type = check_stmts(&fd.body, &fn_env, &type_env, &mut var_env);
-        //         trace!("result {}: {} {:?}", &fd.id, &fd.result, &stmt_type);
+pub fn eval_prog(file_name: &str) {
+    let p = crate::read_file::parse(file_name);
+    println!("prog \n{}", p);
 
-        //         let stmt_type = strip_mut(stmt_type?);
+    let fn_env = new_fn_env(&p.fn_decls);
+    let args: Vec<Val> = Vec::new();
+    let mut m = Mem::new();
 
-        //         if stmt_type != fd.result {
-        //             Err(format!(
-        //                 "return type {} does not match statements type {}",
-        //                 fd.result, stmt_type
-        //             ))?;
-        //         }
-    }
-    Ok(())
+    eval_fn("main", &args, &mut m, &fn_env);
 }
 
-// unit test
-
-// #[test]
-// fn test_stmts() {
-//     use crate::grammar::*;
-//     use crate::*;
-//     // use Type::*;
-
-//     // setup environment
-//     let p = ProgramParser::new().parse(prog1!()).unwrap();
-//     let fn_env = new_fn_env(&p.fn_decls);
-//     let type_env = new_type_env(&p.type_decls);
-//     let mut var_env = VarEnv::new();
-
-//     trace!("{}", &p);
-
-//     let b = fn_env.get(&"b").unwrap();
-
-//     let body = &b.body;
-//     trace!("{}", &body);
-
-//     trace!("{:?}", check_stmts(body, &fn_env, &type_env, &mut var_env));
-// }
-
-// #[test]
-// fn test_expr_type() {
-//     use crate::grammar::*;
-//     use crate::*;
-//     use Type::*;
-
-//     // setup environment
-//     let i = prog1!();
-//     println!("{}", i);
-//     let p = ProgramParser::new().parse(prog1!()).unwrap();
-//     let fn_env = new_fn_env(&p.fn_decls);
-//     let type_env = new_type_env(&p.type_decls);
-//     let mut var_env = VarEnv::new();
-//     var_env.push_empty_scope();
-
-// // // some test variables in scope
-// var_env.new_id("i".to_string(), I32); // let i : i32 ...
-// var_env.new_id("j".to_string(), Unknown); // let i ...
-
-// println!("p {}", p);
-
-// // type of number
-// assert_eq!(
-//     expr_type(&Expr::Num(1), &fn_env, &type_env, &mut var_env),
-//     Ok(I32)
-// );
-
-// // type of variables
-
-// // not found
-// assert!(expr_type(&Expr::Id("a".to_string()), &fn_env, &type_env, &mut var_env).is_err());
-
-// // let i: i32 ...
-// assert_eq!(
-//     expr_type(&Expr::Id("i".to_string()), &fn_env, &type_env, &mut var_env),
-//     Ok(I32)
-// );
-
-// // let j ... (has no type yet)
-// assert_eq!(
-//     expr_type(&Expr::Id("j".to_string()), &fn_env, &type_env, &mut var_env),
-//     Ok(Unknown)
-// );
-
-// // // let n: A ...
-// // assert_eq!(
-// //     expr_type(&Expr::Id("n".to_string()), &fn_env, &type_env, &var_env),
-// //     Ok(Named("A".to_string()))
-// // );
-
-// // type of arithmetic operation (for now just i32)
-// assert_eq!(
-//     expr_type(
-//         &*ExprParser::new().parse("1 + 2 - 5").unwrap(),
-//         &fn_env,
-//         &type_env,
-//         &mut var_env
-//     ),
-//     Ok(I32)
-// );
-
-// // type of arithmetic unary operation (for now just i32)
-// assert_eq!(
-//     expr_type(
-//         &*ExprParser::new().parse("- 5").unwrap(),
-//         &fn_env,
-//         &type_env,
-//         &mut var_env
-//     ),
-//     Ok(I32)
-// );
-
-// // call, with check, ok
-// assert_eq!(
-//     expr_type(
-//         &*ExprParser::new().parse("b(1)").unwrap(),
-//         &fn_env,
-//         &type_env,
-//         &mut var_env
-//     ),
-//     Ok(I32)
-// );
-
-// // call, with check, ok (i: i32)
-// assert_eq!(
-//     expr_type(
-//         &*ExprParser::new().parse("b(i)").unwrap(),
-//         &fn_env,
-//         &type_env,
-//         &mut var_env
-//     ),
-//     Ok(I32)
-// );
-
-// // call, with check, error wrong number args
-// assert!(expr_type(
-//     &*ExprParser::new().parse("b(1, 2)").unwrap(),
-//     &fn_env,
-//     &type_env,
-//     &mut var_env
-// )
-// .is_err());
-
-// // call, with check, error type of arg
-// assert!(expr_type(
-//     &*ExprParser::new().parse("b(true)").unwrap(),
-//     &fn_env,
-//     &type_env,
-//     &mut var_env
-// )
-// .is_err());
-
-// // call, with check, ok (i: i32)
-// assert_eq!(
-//     expr_type(
-//         &*ExprParser::new().parse("c(n)").unwrap(),
-//         &fn_env,
-//         &type_env,
-//         &var_env
-//     ),
-//     Ok(Unit)
-// );
-
-// TODO, ref/ref mut/deref
-// }
+#[test]
+fn test_deref() {
+    let mut m: Mem = Mem::new();
+    let mut hm = IdVal::new();
+
+    hm.insert("a".to_string(), (false, Val::Num(7)));
+    hm.insert("b".to_string(), (false, Val::Ref("a".to_string())));
+    hm.insert("c".to_string(), (false, Val::Ref("b".to_string())));
+    m.push_param_scope(hm);
+    println!("mem {:?}", m);
+    let e: Expr = Expr::Id("a".to_string());
+    let v = eval_expr(&e, &mut m, &FnEnv::new());
+    println!("v {:?}", v);
+
+    let e: Expr = Expr::DeRef(Box::new(Expr::Id("b".to_string())));
+    let v = eval_expr(&e, &mut m, &FnEnv::new());
+    println!("v {:?}", v);
+
+    let e: Expr = Expr::DeRef(Box::new(Expr::Id("c".to_string())));
+    let v = eval_expr(&e, &mut m, &FnEnv::new());
+    println!("v {:?}", v);
+
+    let e: Expr = Expr::DeRef(Box::new(Expr::DeRef(Box::new(Expr::Id("c".to_string())))));
+    let v = eval_expr(&e, &mut m, &FnEnv::new());
+    println!("v {:?}", v);
+}
diff --git a/src/check.rs b/src/check.rs
index f8532f716b007ce601a6d32bb183020f36bc2ab3..8dfed9f52fe1f27b88be24b18fa7113435d3ddb6 100644
--- a/src/check.rs
+++ b/src/check.rs
@@ -5,7 +5,7 @@ use log::{trace, LevelFilter};
 use std::convert::From;
 
 use crate::ast::*;
-use crate::env::{new_fn_env, new_type_env, Borrow, Error, FnEnv, IdType, TypeEnv, VarEnv};
+use crate::env::{new_fn_env, new_type_env, Bc, Error, FnEnv, IdType, TypeEnv, VarEnv};
 
 // type check
 
@@ -447,7 +447,7 @@ pub fn check(p: &Program) -> Result<(), Error> {
                 true => Type::Mut(Box::new(ty.clone())),
                 _ => ty.clone(),
             };
-            arg_ty.insert(id.to_owned(), (Borrow::Free, ty));
+            arg_ty.insert(id.to_owned(), (Bc::Free, ty));
         }
 
         var_env.push_param_scope(arg_ty);
diff --git a/src/env.rs b/src/env.rs
index 2539edd67ad95524870c3484b0494085d2d3c7ef..6d154c7f9b86d533e824ed57085b707b2cd9d2a1 100644
--- a/src/env.rs
+++ b/src/env.rs
@@ -23,7 +23,7 @@ pub fn new_fn_env(v: &Vec<FnDecl>) -> FnEnv {
 }
 
 #[derive(Debug, PartialEq)]
-pub enum Borrow {
+pub enum Bc {
     Free, // or maybe owned
     Unique,
     Shared(Vec<String>),
@@ -32,7 +32,7 @@ pub enum Borrow {
 pub type Address = i32;
 type Scope = i32;
 
-pub type IdType = HashMap<Id, (Borrow, Type)>;
+pub type IdType = HashMap<Id, (Bc, Type)>;
 #[derive(Debug)]
 pub struct VarEnv {
     env: VecDeque<IdType>,
@@ -45,7 +45,7 @@ impl VarEnv {
         }
     }
 
-    pub fn get(&self, id: String) -> Option<(Scope, &(Borrow, Type))> {
+    pub fn get(&self, id: String) -> Option<(Scope, &(Bc, Type))> {
         self.env.iter().enumerate().find_map(|(scope, hm)| {
             hm.get(id.as_str())
                 .map(|t| ((self.env.len() - scope) as Scope, t))
@@ -56,7 +56,7 @@ impl VarEnv {
         self.get(id).map(|(s, t)| (s, &t.1))
     }
 
-    pub fn get_mut(&mut self, id: String) -> Option<&mut (Borrow, Type)> {
+    pub fn get_mut(&mut self, id: String) -> Option<&mut (Bc, Type)> {
         self.env.iter_mut().find_map(|hm| hm.get_mut(id.as_str()))
     }
 
@@ -68,7 +68,7 @@ impl VarEnv {
         let hm = self.env.front_mut().unwrap();
         println!("insert id {:?}, in scope {:?}", &id, hm);
 
-        hm.insert(id, (Borrow::Free, ty));
+        hm.insert(id, (Bc::Free, ty));
     }
 
     pub fn update(&mut self, id: String, ty: Type) -> Result<(), Error> {
diff --git a/src/grammar.lalrpop b/src/grammar.lalrpop
index 9ec3c9f4617c4099b6f8ab30d6d2b8a386fcf547..f5f0e8357da3dd802d92de1670221577624d28de 100644
--- a/src/grammar.lalrpop
+++ b/src/grammar.lalrpop
@@ -124,7 +124,7 @@ Param : Param = {
 Type : Type = {
     "bool" => Type::Bool,
     "()" => Type::Unit,
-    "i32" => Type::I32, // this should likely be a paratrized Num type later
+    "i32" => Type::I32, // this should likely be a parametrized Num type later
     Id => Type::Named(<>),
     "&" <Type> => Type::Ref(Box::new(<>)),
     "&" "mut" <Type> => Type::Ref(Box::new(Type::Mut(Box::new(<>)))),
@@ -159,7 +159,7 @@ StmtBlock: Stmt = {
 }
 
 Stmt: Stmt = {
-    "let" <m: "mut"?> <id: Id> <t: (":" <Type>)?> <e: ("=" <Expr>)?>  => Stmt::Let(id, m.is_some(), t, e),
+    "let" <m: "mut"?> <id: Id> <t: (":" <Type>)?> <e: ("=" <Expr>)?> => Stmt::Let(id, m.is_some(), t, e),
     <Expr> "=" <Expr> => Stmt::Assign(<>),
     <ExprNoBlock> => Stmt::Expr(<>),
 }
diff --git a/src/vm.rs b/src/vm.rs
index bfe900508a628864a6e2c71a07247aaeb0b10c00..e89bdd3b2de2bc8fc725bee1142e16ddceb21349 100644
--- a/src/vm.rs
+++ b/src/vm.rs
@@ -1,12 +1,9 @@
-// vm
+// boorrow
 
 use std::collections::{HashMap, VecDeque};
-// use std::convert::From;
-//use std::iter::{FromIterator, Map};
 
 use crate::ast::*;
 use crate::env::*;
-//use crate::grammar::*;
 
 type Id = String;
 
@@ -44,7 +41,12 @@ impl Mem {
                 Some((true, v)) => Some(v), // a mut reference
                 Some((_, v)) => {
                     match v {
-                        Val::Uninitialized => Some(v), // an unitialized
+                        Val::Uninitialized => Some(v), // an uninitialized
+                        Val::RefMut(_) => {
+                            // a ref mut
+                            println!("get &mut {:?}", v);
+                            Some(v)
+                        }
                         _ => panic!("cannot access as mutable"),
                     }
                 }
@@ -175,6 +177,7 @@ fn eval_expr(e: &Expr, m: &mut Mem, fn_env: &FnEnv) -> Val {
 
             match ev {
                 Val::Ref(id) => eval_expr(&Expr::Id(id), m, fn_env),
+                Val::RefMut(id) => eval_expr(&Expr::Id(id), m, fn_env),
                 _ => panic!("cannot deref"),
             }
         }
@@ -364,4 +367,4 @@ fn test_deref() {
     let e: Expr = Expr::DeRef(Box::new(Expr::DeRef(Box::new(Expr::Id("c".to_string())))));
     let v = eval_expr(&e, &mut m, &FnEnv::new());
     println!("v {:?}", v);
-}
+}
\ No newline at end of file
diff --git a/tests/test_borrow.rs b/tests/test_borrow.rs
new file mode 100644
index 0000000000000000000000000000000000000000..16dba2cf7e5fadc27a22ebbdf371711f0add0f5f
--- /dev/null
+++ b/tests/test_borrow.rs
@@ -0,0 +1,120 @@
+use erode::borrow::*;
+
+#[test]
+fn minmal_test() {
+    eval_prog("examples/minimal.rs");
+}
+
+#[test]
+fn ref_test() {
+    eval_prog("examples/ref.rs");
+}
+
+#[test]
+fn assign_test() {
+    eval_prog("examples/assign.rs");
+}
+
+#[test]
+fn ref_mut_test() {
+    eval_prog("examples/ref_mut.rs");
+}
+
+#[test]
+fn deref_test() {
+    eval_prog("examples/deref.rs");
+}
+
+#[test]
+fn deref2_test() {
+    eval_prog("examples/deref2.rs");
+}
+
+#[test]
+fn deref3_test() {
+    eval_prog("examples/deref3.rs");
+}
+
+#[test]
+fn deref_assign_test() {
+    eval_prog("examples/deref_assign.rs");
+}
+
+#[test]
+fn deref_assign2_test() {
+    eval_prog("examples/deref_assign2.rs");
+}
+
+#[test]
+fn deref_assign3_test() {
+    eval_prog("examples/deref_assign3.rs");
+}
+
+#[test]
+fn scopes_test() {
+    eval_prog("examples/scopes.rs");
+}
+
+#[test]
+fn scopes_err_test() {
+    eval_prog("examples/scopes_err.rs");
+}
+#[test]
+fn vm_test() {
+    eval_prog("examples/vm.rs");
+}
+
+#[test]
+fn if_test() {
+    eval_prog("examples/if.rs");
+}
+
+#[test]
+fn while_test() {
+    eval_prog("examples/while.rs");
+}
+
+#[test]
+fn let_test() {
+    eval_prog("examples/let.rs");
+}
+
+#[test]
+fn let2_test() {
+    eval_prog("examples/let2.rs");
+}
+
+#[test]
+fn call_test() {
+    eval_prog("examples/call.rs");
+}
+
+#[test]
+fn call2_test() {
+    eval_prog("examples/call2.rs");
+}
+
+#[test]
+fn call3_test() {
+    eval_prog("examples/call3.rs");
+}
+
+#[test]
+fn call_ref_test() {
+    eval_prog("examples/call_ref.rs");
+}
+
+#[test]
+fn call_mut_ref_test() {
+    eval_prog("examples/call_mut_ref.rs");
+}
+
+#[test]
+fn borrow() {
+    eval_prog("examples/borrow.rs");
+}
+
+#[test]
+fn borrow2() {
+    eval_prog("examples/borrow2.rs");
+}
diff --git a/tests/test_vm.rs b/tests/test_vm.rs
index 471e908cdaf16db4174bb0588fee83938f154659..61c8d03968c83d4f0784ffe8a59880b86e08d473 100644
--- a/tests/test_vm.rs
+++ b/tests/test_vm.rs
@@ -20,6 +20,21 @@ fn ref_mut_test() {
     eval_prog("examples/ref_mut.rs");
 }
 
+#[test]
+fn deref_test() {
+    eval_prog("examples/deref.rs");
+}
+
+#[test]
+fn deref2_test() {
+    eval_prog("examples/deref2.rs");
+}
+
+#[test]
+fn deref3_test() {
+    eval_prog("examples/deref3.rs");
+}
+
 #[test]
 fn deref_assign_test() {
     eval_prog("examples/deref_assign.rs");
@@ -93,3 +108,13 @@ fn call_ref_test() {
 fn call_mut_ref_test() {
     eval_prog("examples/call_mut_ref.rs");
 }
+
+#[test]
+fn borrow() {
+    eval_prog("examples/borrow.rs");
+}
+
+#[test]
+fn borrow2() {
+    eval_prog("examples/borrow2.rs");
+}