diff --git a/examples/tmp.rs b/examples/tmp.rs
index c84931cd0b29054a2e8ad0c97a852dcb820257db..bde570e67889947cf38bbd8fb21e6c4feda3c652 100644
--- a/examples/tmp.rs
+++ b/examples/tmp.rs
@@ -1,17 +1,17 @@
-extern crate nom;
-
-
-use crust::parse::test;
+use crust::{
+    ast::Span,
+    parse::{parse_assign, test},
+};
 
 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);
-    // just to check that right associative works (you don't need to implement pow)
-    test("2+3**2**3*5+1", 2 + 3i32.pow(2u32.pow(3)) * 5 + 1);
-    test("(12*2)/3-4", (12 * 2) / 3 - 4);
-    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 + + 1", - -1 + 1);  // rust does not allow + as a unary op (I do ;)
+    // test("(-1-1)+(-1+3)", (-1 - 1) + (-1) + 3);
+    // // just to check that right associative works (you don't need to implement pow)
+    // test("2+3**2**3*5+1", 2 + 3i32.pow(2u32.pow(3)) * 5 + 1);
+    // test("(12*2)/3-4", (12 * 2) / 3 - 4);
+    // 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")));
 }
-
-
diff --git a/src/ast.rs b/src/ast.rs
index bc4464e33e5158c2db7a2431dec5a2a71ed7f55d..f29611295a093d976ebed08940ce46e175920432 100644
--- a/src/ast.rs
+++ b/src/ast.rs
@@ -23,11 +23,81 @@ type SpanOp<'a> = (Span<'a>, Op);
 #[derive(Debug, Clone, PartialEq)]
 pub enum Expr<'a> {
     Num(i32),
+    Bool(bool),
     Par(Box<SpanExpr<'a>>),
-    // Identifier
-    // Function application
+    Id(String),
+    Fn(String, Vec<SpanExpr<'a>>),
     BinOp(Op, Box<SpanExpr<'a>>, Box<SpanExpr<'a>>),
     UnaryOp(Op, Box<SpanExpr<'a>>),
 }
 
 pub type SpanExpr<'a> = (Span<'a>, Expr<'a>);
+
+#[derive(Debug, PartialEq, Clone)]
+pub enum Cmd<'a> {
+    // let <mut> id : <& <mut>>Type = expr
+    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>),
+    // /// while predicate do-this
+    // While(Expr, Block),
+    // Return(Expr),
+}
+
+pub type SpanCmd<'a> = (Span<'a>, Cmd<'a>);
+
+#[derive(Debug, PartialEq, Clone)]
+pub enum Mutability {
+    Imm,
+    Mut,
+}
+
+pub type SpanMut<'a> = (Span<'a>, Mutability);
+
+// #[derive(Debug, PartialEq, Clone)]
+// pub enum Cmd {
+//     /// let <mut> id : <& <mut>>Type = expr
+//     Let(Mutability, String, Type, Expr),
+//     /// id = expr
+//     Assign(Expr, Expr),
+//     /// if predicate do-this, and optionally do-that)
+//     If(Expr, Block, Option<Block>),
+//     /// while predicate do-this
+//     While(Expr, Block),
+//     Return(Expr),
+// }
+
+pub type SpanBlock<'a> = (Span<'a>, Vec<SpanCmd<'a>>);
+
+#[derive(Debug, PartialEq, Clone)]
+pub enum Type<'a> {
+    I32,
+    Bool,
+    Unit,
+    Mut(Box<SpanType<'a>>),
+    Ref(Box<SpanType<'a>>),
+    // no structs
+}
+
+pub type SpanType<'a> = (Span<'a>, Type<'a>);
+
+// #[derive(Debug, PartialEq, Clone)]
+// pub enum TypeDecl {
+//     Struct(String, Vec<(String, Type)>),
+// }
+
+// #[derive(Debug, PartialEq, Clone)]
+// pub struct Function {
+//     pub sig: (String, Vec<(String, Type)>, Type),
+//     pub body: Block,
+// }
+
+// #[derive(Debug, PartialEq, Clone)]
+// pub enum Item {
+//     TypeDecl(TypeDecl),
+//     Function(Function),
+// }
+
+// pub type Prog = Vec<Item>;
diff --git a/src/parse.rs b/src/parse.rs
index 77e8edc0807348a39b4e79deb9bf096086e193c6..95e21051bbf1d11dac5cc4da68476d24ace92ffd 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -6,16 +6,15 @@ use std::slice::Iter;
 use nom::{
     branch::alt,
     bytes::complete::tag,
-    character::complete::char,
-    character::complete::{digit1, multispace0},
+    character::complete::{alpha1, char, digit1, multispace0},
     combinator::{cut, map},
     error::ParseError,
-    multi::many1,
-    sequence::{delimited, preceded},
+    multi::{many1, separated_list},
+    sequence::{delimited, preceded, tuple},
     IResult,
 };
 
-use crate::ast::{Expr, Op, Span, SpanExpr};
+use crate::ast::{Cmd, Expr, Op, Span, SpanCmd, SpanExpr};
 
 pub fn parse_i32(i: Span) -> IResult<Span, (Span, i32)> {
     map(digit1, |digit_str: Span| {
@@ -41,6 +40,9 @@ fn parse_op(i: Span) -> IResult<Span, (Span, Op)> {
 #[derive(Debug, Clone, PartialEq)]
 pub enum Token<'a> {
     Num(i32),
+    Bool(bool),
+    Id(String),
+    Fn(String, Vec<(Span<'a>, Vec<SpanToken<'a>>)>),
     Par(Vec<SpanToken<'a>>),
     Op(Op),
 }
@@ -50,6 +52,12 @@ type SpanToken<'a> = (Span<'a>, Token<'a>);
 fn parse_terminal(i: Span) -> IResult<Span, SpanToken> {
     alt((
         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(alpha1, |s: Span| (s, Token::Id(s.to_string()))),
         map(parse_par(parse_tokens), |(s, tokens)| {
             (s, Token::Par(tokens))
         }),
@@ -71,8 +79,12 @@ fn parse_tokens(i: Span) -> IResult<Span, (Span, Vec<SpanToken>)> {
 fn compute_atom<'a>(t: &mut Peekable<Iter<SpanToken<'a>>>) -> SpanExpr<'a> {
     match t.next() {
         Some((s, Token::Num(i))) => (*s, Expr::Num(*i)),
+        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::Op(op))) => {
+            (*s, Expr::UnaryOp(*op, Box::new(climb(t, 4))))
+        } // assume highest precedence
         _ => panic!("error in compute atom"),
     }
 }
@@ -107,12 +119,15 @@ fn climb<'a>(
     result
 }
 
+pub fn parse_expr(i: Span) -> IResult<Span, SpanExpr> {
+    map(parse_tokens, |(_, tokens)| {
+        climb(&mut tokens.iter().peekable(), 0)
+    })(i)
+}
+
 pub fn test(s: &str, v: i32) {
-    match parse_tokens(Span::new(s)) {
-        Ok((Span { fragment: "", .. }, (_, t))) => {
-            let mut t = t.iter().peekable();
-            println!("{:?}", &t);
-            let e = climb(&mut t, 0);
+    match parse_expr(Span::new(s)) {
+        Ok((Span { fragment: "", .. }, e)) => {
             println!("{:?}", &e);
             println!("eval {} {}", math_eval(&e), v);
             assert_eq!(math_eval(&e), v);
@@ -138,6 +153,209 @@ where
     delimited(char('('), preceded(multispace0, inner), cut(char(')')))
 }
 
+// fn parse_if<'a>(i: Span) -> IResult<Span, SpanCmd> {
+//     map(
+//         preceded(
+//             // here to avoid ambiguity with other names starting with `if`, if we added
+//             // variables to our language, we say that if must be terminated by at least
+//             // one whitespace character
+//             terminated(tag("if"), multispace1),
+//             cut(tuple((
+//                 parse_expr,
+//                 parse_block,
+//                 opt(preceded(preceded(multispace0, tag("else")), parse_block)),
+//             ))),
+//         ),
+//         |(pred, true_branch, maybe_false_branch)| {
+//             Cmd::If(pred, true_branch, maybe_false_branch)
+//         },
+//     )(i)
+// }
+
+// pub fn parse_let(i: &str) -> IResult<&str, Cmd, VerboseError<&str>> {
+//     context(
+//         "let expression",
+//         map(
+//             preceded(
+//                 // here to avoid ambiguity with other names starting with `let`, if we added
+//                 // variables to our language, we say that if must be terminated by at least
+//                 // one whitespace character
+//                 terminated(tag("let"), multispace1),
+//                 cut(tuple((
+//                     opt(preceded(multispace0, terminated(tag("mut"), multispace1))),
+//                     parse_id,
+//                     preceded(preceded(multispace0, tag(":")), parse_type),
+//                     preceded(preceded(multispace0, tag("=")), parse_expr),
+//                 ))),
+//             ),
+//             |(m, id, t, expr)| {
+//                 Cmd::Let(
+//                     if m.is_some() {
+//                         Mutability::Mut
+//                     } else {
+//                         Mutability::Imm
+//                     },
+//                     id,
+//                     t,
+//                     expr,
+//                 )
+//             },
+//         ),
+//     )(i)
+// }
+
+pub fn parse_assign<'a>(i: Span<'a>) -> IResult<Span<'a>, Cmd<'a>> {
+    map(
+        // here to avoid ambiguity with other names starting with `let`, if we added
+        // variables to our language, we say that if must be terminated by at least
+        // one whitespace character
+        tuple((
+            parse_expr,
+            preceded(preceded(multispace0, tag("=")), parse_expr),
+        )),
+        |(id_expr, expr)| Cmd::Assign(id_expr, expr),
+    )(i)
+}
+
+// pub fn parse_return(i: &str) -> IResult<&str, Cmd, VerboseError<&str>> {
+//     context(
+//         "assign",
+//         map(
+//             preceded(terminated(tag("return"), multispace1), parse_expr),
+//             |expr| Cmd::Return(expr),
+//         ),
+//     )(i)
+// }
+
+// pub fn parse_while(i: &str) -> IResult<&str, Cmd, VerboseError<&str>> {
+//     context(
+//         "while expression",
+//         map(
+//             preceded(
+//                 // here to avoid ambiguity with other names starting with `let`, if we added
+//                 // variables to our language, we say that if must be terminated by at least
+//                 // one whitespace character
+//                 terminated(tag("while"), multispace1),
+//                 cut(tuple((parse_expr, parse_block))),
+//             ),
+//             |(pred, body)| Cmd::While(pred, body),
+//         ),
+//     )(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_block(i: &str) -> IResult<&str, Block, VerboseError<&str>> {
+//     preceded(multispace0, s_cmd(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
+//     F: Fn(&'a str) -> IResult<&'a str, O, VerboseError<&'a str>>,
+// {
+//     // delimited allows us to split up the input
+//     // cut allows us to consume the input (and prevent backtracking)
+//     preceded(
+//         multispace0,
+//         delimited(
+//             char('{'),
+//             preceded(multispace0, inner),
+//             context(
+//                 "closing curly bracket",
+//                 cut(preceded(multispace0, char('}'))),
+//             ),
+//         ),
+//     )
+// }
+
+// pub fn parse_type(i: &str) -> IResult<&str, Type, VerboseError<&str>> {
+//     preceded(
+//         multispace0,
+//         alt((
+//             map(tag("i32"), |_| Type::I32),
+//             map(tag("bool"), |_| Type::Bool),
+//             map(preceded(tag("&"), parse_type), |t| Type::Ref(Box::new(t))),
+//             map(
+//                 preceded(terminated(tag("mut"), multispace1), parse_type),
+//                 |t| Type::Mut(Box::new(t)),
+//             ),
+//         )),
+//     )(i)
+// }
+
+// pub fn parse_field_decl(i: &str) -> IResult<&str, (String, Type), VerboseError<&str>> {
+//     map(
+//         tuple((parse_id, preceded(multispace0, tag(":")), parse_type)),
+//         |(l, _, r)| (l, r),
+//     )(i)
+// }
+
+// pub fn parse_field_decls(i: &str) -> IResult<&str, Vec<(String, Type)>, VerboseError<&str>> {
+//     s_cmd(separated_list(tag(","), parse_field_decl))(i)
+// }
+
+// // pub fn parse_par_decls(i: &str) -> IResult<&str, Vec<(String, Type)>, VerboseError<&str>> {
+// //     s_cmd(separated_list(tag(","), parse_par_decl))(i)
+// // }
+
+// pub fn parse_par_decl(i: &str) -> IResult<&str, (String, Type), VerboseError<&str>> {
+//     map(
+//         tuple((
+//             opt(preceded(multispace0, terminated(tag("mut"), multispace1))),
+//             parse_id,
+//             preceded(multispace0, tag(":")),
+//             parse_type,
+//         )),
+//         |(b, id, _, t)| (id, t),
+//     )(i)
+// }
+
+// pub fn parse_type_decl(i: &str) -> IResult<&str, TypeDecl, VerboseError<&str>> {
+//     preceded(
+//         preceded(multispace0, terminated(tag("struct"), multispace1)),
+//         map(tuple((parse_id, parse_field_decls)), |(id, fields)| {
+//             TypeDecl::Struct(id, fields)
+//         }),
+//     )(i)
+// }
+
+// pub fn parse_function_decl(i: &str) -> IResult<&str, Function, VerboseError<&str>> {
+//     map(
+//         preceded(
+//             preceded(multispace0, terminated(tag("fn"), multispace1)),
+//             tuple((
+//                 parse_id,
+//                 s_exp(separated_list(tag(","), parse_par_decl)),
+//                 opt(preceded(
+//                     preceded(multispace0, terminated(tag("->"), multispace1)),
+//                     parse_type,
+//                 )),
+//                 parse_block,
+//             )),
+//         ),
+//         |(id, par, ret, body)| Function {
+//             sig: (id, par, ret.unwrap_or(Type::Unit)),
+//             body: body,
+//         },
+//     )(i)
+// }
+
+// pub fn parse_prog(i: &str) -> IResult<&str, Prog, VerboseError<&str>> {
+//     separated_list(
+//         multispace0,
+//         alt((
+//             map(parse_function_decl, |f| Item::Function(f)),
+//             map(parse_type_decl, |t| Item::TypeDecl(t)),
+//         )),
+//     )(i)
+// }
+
+// tests
 fn math_eval(e: &SpanExpr) -> i32 {
     match e.clone().1 {
         Expr::Num(i) => i,