diff --git a/src/expr.rs b/src/expr.rs
index 42eef3419131b5eacb5c99f9a44463a16bf64aea..eed7c658a421f4f8cf55fa1eff4a4fc8cdd87cdd 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -1,3 +1,5 @@
+use std::fmt::Display;
+
 use bitvec::{order::Msb0, vec::BitVec};
 
 #[derive(Debug)]
@@ -16,7 +18,7 @@ pub enum FlattenedExpr {
 }
 
 impl FlattenedExpr {
-    pub fn to_blc(self, bv: &mut BitVec<u8, Msb0>) {
+    pub fn to_blc(&self, bv: &mut BitVec<u8, Msb0>) {
         match self {
             FlattenedExpr::Abs(expr) => {
                 bv.push(false);
@@ -32,7 +34,7 @@ impl FlattenedExpr {
                 right.to_blc(bv);
             }
             FlattenedExpr::Var(x) => {
-                for _ in 0..=x {
+                for _ in 0..=*x {
                     bv.push(true);
                 }
 
@@ -41,3 +43,17 @@ impl FlattenedExpr {
         }
     }
 }
+
+impl Display for FlattenedExpr {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            FlattenedExpr::Abs(expr) => {
+                write!(f, "λ{}", expr)?;
+            }
+            FlattenedExpr::App(left, right) => write!(f, "[{} {}]", left, right)?,
+            FlattenedExpr::Var(var) => write!(f, "{var}")?,
+        }
+
+        Ok(())
+    }
+}
diff --git a/src/flatten.rs b/src/flatten.rs
index 53e54322216c7149814fc28a6ffbb56219da29f1..7d51539e67b46653caa67472a8f8a124209c7103 100644
--- a/src/flatten.rs
+++ b/src/flatten.rs
@@ -12,7 +12,7 @@ impl Flattener {
     }
 
     pub fn flatten(&mut self, main: &Expr) -> Option<FlattenedExpr> {
-        self.flatten_expr(&main, HashSet::new())
+        self.flatten_expr(main, HashSet::new())
     }
 
     fn flatten_expr<'a>(
diff --git a/src/main.rs b/src/main.rs
index 45cec789815eb4e72331d21083b23943caa6fc2b..bc62b3cd25fd61b6071579d3fe7a7ce6e56e9ad5 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -7,17 +7,21 @@ mod expr;
 mod flatten;
 mod parser;
 mod scanner;
+mod stmt;
 mod token;
 
 fn main() {
     let scanner = Scanner::new(
         "
-$false=λtf.f
-$omega=λa. (a a)
+        $false=λtf.f;
+        $omega=λa. (a a);
 
 
-λa.a($omega(λbcde.d(bb)(λf.fce)))$false
-",
+        (λa.a($omega(λbcde.d(bb)(λf.fce)))$false);
+        #(λabc.cb(ab));
+        #(λabc.cba);
+                ",
+        // "((λab.ba)(λa.a))",
     );
 
     let tokens = scanner.scan_tokens().unwrap();
@@ -28,12 +32,12 @@ $omega=λa. (a a)
     let mut flattener = Flattener::new(funcs);
     let flat = flattener.flatten(&main).unwrap();
 
-    dbg!(&flat);
+    println!("{flat}");
 
-    let mut bv = BitVec::new();
+    /*let mut bv = BitVec::new();
     flat.to_blc(&mut bv);
 
     for x in bv.as_raw_slice() {
         print!("{}", *x as char);
-    }
+    }*/
 }
diff --git a/src/parser.rs b/src/parser.rs
index 576b45ff9196ccbd7191c2539bb1e9dd567b041a..035258c47c3eedc5c67469910bf45e397bde35b7 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -2,6 +2,7 @@ use std::collections::HashMap;
 
 use crate::{
     expr::Expr,
+    stmt::Stmt,
     token::{Token, TokenType},
 };
 
@@ -24,36 +25,14 @@ impl Parser {
         let mut definitions = HashMap::new();
         let mut main_expr = None;
         while !self.is_at_end() {
-            let t = self.peek().token_type.clone();
+            let stmt = self.stmt()?;
 
-            match t {
-                TokenType::DefinitionName(name) => {
-                    self.advance(); // consume definition name
-
-                    // function expr
-
-                    if self.peek().token_type != TokenType::Equals {
-                        self.print_error("Expected '=' after definition name");
-
-                        return None;
-                    }
-
-                    self.advance(); // consume equals
-
-                    let definition = self.expr()?;
-
-                    if definitions.contains_key(&name) {
-                        self.print_error(&format!("Duplicate definition for ${}", name));
-                        return None;
-                    }
-
-                    definitions.insert(name, definition);
+            match stmt {
+                Stmt::Definition(name, expr) => {
+                    definitions.insert(name, expr);
                 }
 
-                // main expr
-                _ => {
-                    let expr = self.expr()?;
-
+                Stmt::Main(expr) => {
                     if main_expr.is_some() {
                         self.print_error("Duplicate primary expression");
                         return None;
@@ -74,7 +53,51 @@ impl Parser {
         }
     }
 
+    fn stmt(&mut self) -> Option<Stmt> {
+        let stmt = if *self.peek_next() == TokenType::Equals {
+            // definition
+            let TokenType::DefinitionName(name) = &self.advance().token_type else {
+                self.print_error("Expected definition name before '='");
+                return None;
+            };
+
+            let name = name.clone();
+
+            // consume =
+            self.advance();
+
+            let expr = self.expr()?;
+
+            Stmt::Definition(name, expr)
+        } else {
+            Stmt::Main(self.expr()?)
+        };
+
+        // consume semicolon
+        if self.peek().token_type != TokenType::Semicolon {
+            self.print_error("Expected ';' after statement");
+            return None;
+        }
+
+        self.advance();
+
+        Some(stmt)
+    }
+
     fn expr(&mut self) -> Option<Expr> {
+        let mut expr = self.expr_once()?;
+
+        dbg!(&expr);
+
+        while !self.is_at_end_of_expr() {
+            let right = self.expr_once()?;
+            expr = Expr::App(Box::new(expr), Box::new(right));
+        }
+
+        Some(expr)
+    }
+
+    fn expr_once(&mut self) -> Option<Expr> {
         match &self.peek().token_type {
             TokenType::Lambda => self.abstraction(),
 
@@ -85,7 +108,22 @@ impl Parser {
                 Some(Expr::DefinitionName(name))
             }
 
-            TokenType::LeftParen => self.application(),
+            TokenType::LeftParen => {
+                self.advance(); // consume (
+
+                let expr = self.expr()?;
+
+                dbg!(&self.peek().token_type);
+
+                if self.peek().token_type != TokenType::RightParen {
+                    self.print_error("Expected ')'");
+                    return None;
+                }
+
+                self.advance(); // consume )
+
+                Some(expr)
+            }
 
             TokenType::Variable(c) => {
                 let c = *c;
@@ -95,6 +133,8 @@ impl Parser {
             }
 
             _ => {
+                dbg!(&self.peek().token_type);
+
                 self.print_error(
                     "Unexpected token. Expected one of 'λ', '/', '\\', '$', '(', or a variable",
                 );
@@ -128,23 +168,9 @@ impl Parser {
         Some(expr)
     }
 
-    fn application(&mut self) -> Option<Expr> {
-        self.advance(); // consume left paren
-
-        let left = self.expr()?;
-        let right = self.expr()?;
-
-        if self.peek().token_type != TokenType::RightParen {
-            self.print_error("Expected ')' after application");
-            return None;
-        }
-
-        self.advance(); // consume right paren
-
-        Some(Expr::App(Box::new(left), Box::new(right)))
-    }
-
     fn find_variable(&self, name: char) -> Option<usize> {
+        dbg!(name, &self.env);
+
         let ret = self
             .env
             .iter()
@@ -153,7 +179,7 @@ impl Parser {
             .map(|(i, _)| i);
 
         match ret {
-            Some(x) => Some(x),
+            Some(x) => Some(self.env.len() - x - 1),
             None => {
                 self.print_error(&format!("Unknown variable '{}'.", name));
 
@@ -182,7 +208,14 @@ impl Parser {
     }
 
     fn is_at_end(&self) -> bool {
-        self.tokens.len() == self.current
+        self.peek().token_type == TokenType::Eof
+    }
+
+    fn is_at_end_of_expr(&self) -> bool {
+        let tt = &self.peek().token_type;
+
+        // end of expr can be ), $, or end of file
+        matches!(tt, TokenType::RightParen | TokenType::Semicolon)
     }
 
     fn print_error(&self, err: &str) {
diff --git a/src/scanner.rs b/src/scanner.rs
index 20fc7b3b99535ba44e16b7661351773182013dee..94a5581120491da0e6d6f285128980282c6dce7a 100644
--- a/src/scanner.rs
+++ b/src/scanner.rs
@@ -26,6 +26,8 @@ impl Scanner {
             self.scan_token()?;
         }
 
+        self.add_token(TokenType::Eof);
+
         Ok(self.tokens)
     }
 
@@ -38,6 +40,7 @@ impl Scanner {
             ')' => RightParen,
             '.' => Period,
             '=' => Equals,
+            ';' => Semicolon,
             ' ' | '\r' | '\n' | '\t' => NoToken,
             '\\' | '/' | 'λ' => Lambda,
             _ if c.is_alphabetic() => Variable(c),
diff --git a/src/stmt.rs b/src/stmt.rs
new file mode 100644
index 0000000000000000000000000000000000000000..609f82956b948892976b4c4d20a6dd3ba01d665f
--- /dev/null
+++ b/src/stmt.rs
@@ -0,0 +1,6 @@
+use crate::expr::Expr;
+
+pub enum Stmt {
+    Main(Expr),
+    Definition(String, Expr),
+}
diff --git a/src/token.rs b/src/token.rs
index 8e2cd831bb3b402bc8f0b8d955065693f6dab3cf..9f51ce266e4251dd3d7a7043202ea82d5647b8ab 100644
--- a/src/token.rs
+++ b/src/token.rs
@@ -6,10 +6,12 @@ pub enum TokenType {
 
     Period,
     Equals,
+    Semicolon,
 
     LeftParen,
     RightParen,
 
+    Eof,
     NoToken,
 }