diff --git a/Cargo.lock b/Cargo.lock
index cd8a8874f898a4e22cc18f829196546f9304b672..04a459946e737bf6b02e4d92d8aaeb08e62cc608 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -175,9 +175,9 @@ dependencies = [
 
 [[package]]
 name = "either"
-version = "1.6.0"
+version = "1.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f"
+checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
 
 [[package]]
 name = "ena"
@@ -235,9 +235,9 @@ dependencies = [
 
 [[package]]
 name = "getrandom"
-version = "0.1.14"
+version = "0.1.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
+checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
 dependencies = [
  "cfg-if",
  "libc",
@@ -252,9 +252,9 @@ checksum = "00d63df3d41950fb462ed38308eea019113ad1508da725bbedcd0fa5a85ef5f7"
 
 [[package]]
 name = "hermit-abi"
-version = "0.1.15"
+version = "0.1.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9"
+checksum = "4c30f6d0bc6b00693347368a67d41b58f2fb851215ff1da49e90fe2c5c667151"
 dependencies = [
  "libc",
 ]
@@ -329,9 +329,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.76"
+version = "0.2.77"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3"
+checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235"
 
 [[package]]
 name = "log"
@@ -387,9 +387,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.20"
+version = "1.0.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "175c513d55719db99da20232b06cda8bab6b83ec2d04e3283edf0213c37c1a29"
+checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c"
 dependencies = [
  "unicode-xid",
 ]
@@ -458,18 +458,18 @@ dependencies = [
 
 [[package]]
 name = "serde"
-version = "1.0.115"
+version = "1.0.116"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e54c9a88f2da7238af84b5101443f0c0d0a3bbdc455e34a5c9497b1903ed55d5"
+checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.115"
+version = "1.0.116"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "609feed1d0a73cc36a0182a840a9b37b4a82f0b1150369f0536a9e3f2a31dc48"
+checksum = "f630a6370fd8e457873b4bd2ffdae75408bc291ba72be773772a4c2a065d9ae8"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -515,9 +515,9 @@ checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
 
 [[package]]
 name = "syn"
-version = "1.0.40"
+version = "1.0.41"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "963f7d3cc59b59b9325165add223142bbf1df27655d07789f109896d353d8350"
+checksum = "6690e3e9f692504b941dc6c3b188fd28df054f7fb8469ab40680df52fdcc842b"
 dependencies = [
  "proc-macro2",
  "quote",
diff --git a/src/bc_env.rs b/src/bc_env.rs
new file mode 100644
index 0000000000000000000000000000000000000000..5565df2a16ea8c98ddc83bacd5f96fe96d4895aa
--- /dev/null
+++ b/src/bc_env.rs
@@ -0,0 +1,111 @@
+// borrow check environment
+
+use std::iter::FromIterator;
+// use std::iter::{FromIterator, Map};
+
+use crate::ast::*;
+// use crate::grammar::*;
+use std::collections::{HashMap, VecDeque};
+
+#[derive(Debug)]
+pub enum Error {
+    NotFound(String),
+    Shared(String),
+    Unique(String),
+}
+#[derive(Debug)]
+pub enum Bc {
+    Free,
+    Shared,
+    Unique,
+    Ref(Box<Bc>),
+}
+
+type Scope = i32;
+
+pub type IdBc = HashMap<Id, Bc>;
+
+#[derive(Debug)]
+pub struct BcEnv(VecDeque<IdBc>);
+
+impl BcEnv {
+    pub fn new() -> Self {
+        BcEnv(VecDeque::new())
+    }
+
+    pub fn get(&self, id: String) -> Option<(Scope, &Bc)> {
+        self.0
+            .iter()
+            .enumerate()
+            .find_map(|(i, hm)| hm.get(id.as_str()).map(|bc| (i as i32, bc)))
+    }
+
+    pub fn get_mut(&mut self, id: String) -> Option<(Scope, &mut Bc)> {
+        self.0
+            .iter_mut()
+            .enumerate()
+            .find_map(|(i, hm)| hm.get_mut(id.as_str()).map(|bc| (i as i32, bc)))
+    }
+
+    pub fn new_id(&mut self, id: String, bc: Bc) {
+        let hm = self.0.front_mut().unwrap();
+        println!("insert id {:?}, in scope {:?}", id, hm);
+
+        hm.insert(id, bc);
+    }
+
+    pub fn update(&mut self, id: String, bc: Bc) -> Result<(), ()> {
+        println!("env: update");
+        match self.get_mut(id.clone()) {
+            Some(b) => match b {
+                (_i, Bc::Free) => {
+                    *b.1 = bc;
+                    Ok(())
+                }
+                (_i, Bc::Shared) => match bc {
+                    Bc::Shared => Ok(()),
+
+                    Bc::Unique => Err(()),
+                    _ => panic!("ICE update to Free"),
+                },
+                _ => Err(()),
+            },
+            None => Err(()),
+        }
+    }
+
+    pub fn push_empty_scope(&mut self) {
+        self.0.push_front(HashMap::new());
+    }
+
+    pub fn push_param_scope(&mut self, args: IdBc) {
+        self.0.push_front(args)
+    }
+
+    pub fn pop_scope(&mut self) {
+        self.0.pop_front();
+    }
+}
+
+#[test]
+fn test_bc_env() {
+    let mut bc_env = BcEnv::new();
+    println!("{:?}", bc_env);
+    bc_env.push_empty_scope();
+    bc_env.new_id("a".to_string(), Bc::Free);
+    println!("{:?}", bc_env.get("a".to_string()));
+    bc_env.push_empty_scope();
+    bc_env.new_id("b".to_string(), Bc::Free);
+    println!("{:?}", bc_env.get("a".to_string()));
+    println!("{:?}", bc_env.get("b".to_string()));
+    bc_env.update("a".to_string(), Bc::Shared).unwrap();
+    println!("{:?}", bc_env.get("a".to_string()));
+    bc_env.update("a".to_string(), Bc::Shared).unwrap();
+    println!("{:?}", bc_env.get("a".to_string()));
+
+    // it should be an error to assign a shared ref to a unique
+    bc_env.update("a".to_string(), Bc::Unique).unwrap_err();
+    bc_env.pop_scope();
+    println!("{:?}", bc_env.get("b".to_string()));
+    println!("{:?}", bc_env.get("a".to_string()));
+}
diff --git a/src/borrow.rs b/src/borrow.rs
new file mode 100644
index 0000000000000000000000000000000000000000..5b4781cb06186ff498e15eeaffaac4c9198771c6
--- /dev/null
+++ b/src/borrow.rs
@@ -0,0 +1,568 @@
+use std::io::Write;
+
+use env_logger::Builder;
+use log::{trace, LevelFilter};
+use std::convert::From;
+
+use crate::ast::*;
+use crate::bc_env::{Bc, BcEnv, Error, IdBc};
+use crate::env::{new_fn_env, new_type_env, FnEnv, TypeEnv};
+
+// type check
+
+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);
+
+    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!(),
+    }
+}
+
+// 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,
+    }
+}
+
+// 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"),
+    }
+}
+
+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(())
+}
+
+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(())
+}
+
+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_env, type_env)
+}
+
+// pub fn dump_env(fn_env: &FnEnv, type_env: &TypeEnv) {
+//     trace!("fn_env {:?}", fn_env);
+//     trace!("type_env {:?}", type_env);
+// }
+
+// check a whole Program
+pub fn bc_prog(p: &Program) -> Result<(), Error> {
+    let (fn_env, type_env) = build_env(&p);
+
+    println!("Input program \n{}", &p);
+
+    for fd in p.fn_decls.iter() {
+        println!("check function\n{}", fd);
+        println!("ast\n{:?}", fd);
+
+        let mut bc_env = BcEnv::new();
+
+        // build a scope for the arguments
+        let mut arg_ty = IdBc::new();
+
+        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);
+        }
+
+        //         var_env.push_param_scope(arg_ty);
+
+        //         let stmt_type = check_stmts(&fd.body, &fn_env, &type_env, &mut var_env);
+        //         trace!("result {}: {} {:?}", &fd.id, &fd.result, &stmt_type);
+
+        //         let stmt_type = strip_mut(stmt_type?);
+
+        //         if stmt_type != fd.result {
+        //             Err(format!(
+        //                 "return type {} does not match statements type {}",
+        //                 fd.result, stmt_type
+        //             ))?;
+        //         }
+    }
+    Ok(())
+}
+
+// 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
+// }
diff --git a/src/lib.rs b/src/lib.rs
index 5ecdd867f8d5361ba5f2e3a9f3629494a292096e..193a9e74c440da949a49e9e941b25590a3d04207 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -11,6 +11,8 @@ pub mod check;
 #[macro_use]
 pub mod input;
 
+pub mod bc_env;
+pub mod borrow;
 pub mod env;
 pub mod read_file;
 pub mod vm;