diff --git a/examples/crust.rs b/examples/crust.rs
index b86dcf20e6727e4a39116b98715f6c09c0ad8324..61af057e3c3a589ab11c12e2539fcfde6083f6f7 100644
--- a/examples/crust.rs
+++ b/examples/crust.rs
@@ -33,7 +33,8 @@ fn main() -> Result<(), Box<dyn Error>> {
     match parse_block(Span::new(
         "
         {
-            return  2 + 3
+            let abba : i32 = 7;
+            return  2 + abba
         }
         ",
     )) {
@@ -50,6 +51,7 @@ fn main() -> Result<(), Box<dyn Error>> {
                 builder: &builder,
                 module: &module,
                 fn_value_opt: Some(function),
+                variables: HashMap::new(),
                 //&fpm,
             };
             let res = compiler.compile_block(prog);
@@ -74,8 +76,7 @@ pub struct Compiler<'a> {
     // pub fpm: &'a PassManager<FunctionValue>,
     pub module: &'a Module,
     // pub function: &'a Func<'a>,
-
-    // variables: HashMap<String, PointerValue>,
+    variables: HashMap<String, PointerValue>,
     fn_value_opt: Option<FunctionValue>,
 }
 
@@ -94,6 +95,16 @@ impl<'a> Compiler<'a> {
 
     fn compile_expr(&self, expr: &SpanExpr) -> IntValue {
         match expr.1.clone() {
+            Expr::Id(id) => match self.variables.get(&id) {
+                Some(var) => {
+                    self.builder.build_load(*var, &id).into_int_value()
+                }
+                None => panic!(
+                    "Could not find a matching variable.
+                {} in {:?}",
+                    id, self.variables
+                ),
+            },
             Expr::Num(i) => self.context.i32_type().const_int(i as u64, false),
 
             Expr::BinOp(op, l, r) => {
@@ -109,7 +120,7 @@ impl<'a> Compiler<'a> {
     }
 
     /// Creates a new stack allocation instruction in the entry block of the function.
-    fn create_entry_block_alloca(&self, name: &str) -> PointerValue {
+    fn create_entry_block_alloca(&mut self, name: &str) -> PointerValue {
         let builder = self.context.create_builder();
 
         let entry = self.fn_value().get_first_basic_block().unwrap();
@@ -118,12 +129,13 @@ impl<'a> Compiler<'a> {
             Some(first_instr) => builder.position_before(&first_instr),
             None => builder.position_at_end(&entry),
         }
-
-        builder.build_alloca(self.context.f64_type(), name)
+        let alloca = builder.build_alloca(self.context.i32_type(), name);
+        self.variables.insert(name.to_string(), alloca);
+        alloca
     }
 
-    fn compile_cmd(&self, cmd: &Cmd) -> (InstructionValue, bool) {
-        println!("{:?}", cmd);
+    fn compile_cmd(&mut self, cmd: &Cmd) -> (InstructionValue, bool) {
+        // println!("{:?}", cmd);
         match cmd {
             //     Cmd::Assign(lexp, rexp) => {
             //         let rval = eval_expr(rexp, mem, venv, fenv);
@@ -146,33 +158,22 @@ impl<'a> Compiler<'a> {
             //             }
             //         }
             //     }
-            // Cmd::Let(_, id, _, exp) => {
-            //     let val = eval_expr(exp, stack, 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::Let(_, id, _, exp) => {
+                let exp = self.compile_expr(exp);
+                println!("\n-- let id.fragment {}\n", id.fragment);
+                let alloca = self.create_entry_block_alloca(id.fragment);
+                let store = self.builder.build_store(alloca, exp);
+                (store, false)
+            }
             Cmd::Return(exp) => {
                 let expr = self.compile_expr(exp);
                 (self.builder.build_return(Some(&expr)), true)
             }
-            //     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
-            //     }
             _ => unimplemented!(),
         }
     }
 
-    pub fn compile_block(&self, cmds: Vec<Cmd>) -> InstructionValue {
+    pub fn compile_block(&mut self, cmds: Vec<Cmd>) -> InstructionValue {
         for c in &cmds {
             let (cmd, ret) = self.compile_cmd(c);
             if ret {
diff --git a/src/parse.rs b/src/parse.rs
index 50897e99051e75eef0e7467472988f4acb4d0cd7..4d05e49ba2a35c9d24f75155abbdc7e3f9dff7c6 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -6,7 +6,9 @@ use std::slice::Iter;
 use nom::{
     branch::alt,
     bytes::complete::tag,
-    character::complete::{alpha1, alphanumeric0, char, digit1, multispace0, multispace1},
+    character::complete::{
+        alpha1, alphanumeric0, char, digit1, multispace0, multispace1,
+    },
     combinator::{cut, map, opt},
     error::ParseError,
     multi::{many1, separated_list},
@@ -15,7 +17,8 @@ use nom::{
 };
 
 use crate::ast::{
-    Block, Cmd, Expr, Func, Item, Mutability, Op, Prog, Span, SpanCmd, SpanExpr, SpanId, Type,
+    Block, Cmd, Expr, Func, Item, Mutability, Op, Prog, Span, SpanCmd,
+    SpanExpr, SpanId, Type,
 };
 
 pub fn parse_i32(i: Span) -> IResult<Span, (Span, i32)> {
@@ -55,9 +58,13 @@ pub fn parse_id(i: Span) -> IResult<Span, Span> {
     // an identifier needs to start with one or more alphas (head)
     // followed by zero or more alphanumerics (tail)
     map(
-        preceded(multispace0, tuple((alpha1, alphanumeric0))),
+        preceded(multispace0, tuple((alpha1, alphanumeric0, tag("")))),
         // we concatenate the head and tail into a single String
-        |(_, _)| i, // head.to_string() + &tail.to_string(),
+        |(_, _, end): (Span, Span, Span)| {
+            let mut res = i;
+            res.fragment = &i.fragment[..(end.offset - i.offset)];
+            res
+        },
     )(i)
 }
 
@@ -67,7 +74,10 @@ fn parse_terminal(i: Span) -> IResult<Span, SpanToken> {
         map(tag("true"), |s| (s, Token::Bool(true))),
         map(tag("false"), |s| (s, Token::Bool(false))),
         map(
-            tuple((parse_id, parse_par(separated_list(char(','), parse_tokens)))),
+            tuple((
+                parse_id,
+                parse_par(separated_list(char(','), parse_tokens)),
+            )),
             |(s, t)| (s, Token::Call(s.to_string(), t)),
         ),
         map(parse_id, |s: Span| (s, Token::Id(s.to_string()))),
@@ -103,12 +113,17 @@ fn compute_atom<'a>(t: &mut Peekable<Iter<SpanToken<'a>>>) -> SpanExpr<'a> {
                 .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
+        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 {
@@ -154,7 +169,9 @@ fn parse_if(i: Span) -> IResult<Span, Cmd> {
                 opt(preceded(preceded(multispace0, tag("else")), parse_block)),
             ))),
         ),
-        |(pred, true_branch, maybe_false_branch)| Cmd::If(pred, true_branch, maybe_false_branch),
+        |(pred, true_branch, maybe_false_branch)| {
+            Cmd::If(pred, true_branch, maybe_false_branch)
+        },
     )(i)
 }
 
@@ -307,22 +324,34 @@ fn get_prec(op: &Op) -> (u8, Ass) {
 }
 
 // helpers
-fn parse_par<'a, O, F, E>(inner: F) -> impl Fn(Span<'a>) -> IResult<Span<'a>, O, E>
+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(')')))
+    delimited(
+        char('('),
+        preceded(multispace0, inner),
+        cut(preceded(multispace0, char(')'))),
+    )
 }
 
-fn parse_sem<'a, O, F, E>(inner: F) -> impl Fn(Span<'a>) -> IResult<Span<'a>, O, E>
+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('}')))
+    delimited(
+        char('{'),
+        preceded(multispace0, inner),
+        cut(preceded(multispace0, char('}'))),
+    )
 }