diff --git a/examples/math.funk b/examples/math.funk
index f18cbf8bdf043618ab95486c96ec9f58dabd7805..c2e5a326ef3618af56a1c7a1903c6e1c90422757 100644
--- a/examples/math.funk
+++ b/examples/math.funk
@@ -1 +1,3 @@
-(1 + 1) / 2 * 100;
+1+1;
+2+2;
+3+3;
diff --git a/include/ast/ProgramNode.h b/include/ast/ProgramNode.h
new file mode 100644
index 0000000000000000000000000000000000000000..9bc7507908a67a5ebf3ec99508155fb146eb21ee
--- /dev/null
+++ b/include/ast/ProgramNode.h
@@ -0,0 +1,20 @@
+#pragma once
+#include "ast/Node.h"
+
+namespace funk
+{
+class ProgramNode : public Node
+{
+public:
+    ProgramNode(const SourceLocation& loc);
+    ~ProgramNode();
+
+    void add(Node* statement);
+
+    Node* evaluate() const override;
+    String to_s() const override;
+
+private:
+    Vector<Node*> statements;
+};
+} // namespace funk
diff --git a/include/parser/Parser.h b/include/parser/Parser.h
index 4a5cda67c1bb3842d0da47f863a71bcc288064b4..008617eba55ebc92012bc349cfa9776497c4d279 100644
--- a/include/parser/Parser.h
+++ b/include/parser/Parser.h
@@ -13,6 +13,7 @@
 #include "ast/BinaryOpNode.h"
 #include "ast/LiteralNode.h"
 #include "ast/Node.h"
+#include "ast/ProgramNode.h"
 #include "ast/UnaryOpNode.h"
 
 namespace funk
diff --git a/source/ast/ProgramNode.cc b/source/ast/ProgramNode.cc
new file mode 100644
index 0000000000000000000000000000000000000000..5389f9483123eb35812bde2b88cd89721f998996
--- /dev/null
+++ b/source/ast/ProgramNode.cc
@@ -0,0 +1,38 @@
+#include "ast/ProgramNode.h"
+
+namespace funk
+{
+ProgramNode::ProgramNode(const SourceLocation& loc) : Node(loc), statements{} {}
+
+ProgramNode::~ProgramNode()
+{
+    for (Node* statement : statements) { delete statement; }
+}
+
+void ProgramNode::add(Node* statement)
+{
+    statements.push_back(statement);
+}
+
+Node* ProgramNode::evaluate() const
+{
+    if (cached_eval) { return cached_eval; }
+
+    Node* result{};
+    for (Node* statement : statements) { result = statement->evaluate(); }
+    return cached_eval = result;
+}
+
+String ProgramNode::to_s() const
+{
+    String repr{};
+
+    for (Node* statement : statements)
+    {
+        repr += statement->to_s();
+        repr += '\n';
+    }
+
+    return repr;
+}
+} // namespace funk
diff --git a/source/lexer/Lexer.cc b/source/lexer/Lexer.cc
index 2e0c112325f3fbf0a9f548c0fae9f88ad43ecc00..a4ad4f48a810c71ef98585f047f9eaa87c550d2f 100644
--- a/source/lexer/Lexer.cc
+++ b/source/lexer/Lexer.cc
@@ -37,13 +37,12 @@ Vector<Token> Lexer::tokenize()
 Token Lexer::next_token()
 {
     skip_whitespace();
+    token_start_col = column;
 
     if (done()) { return make_token("", TokenType::EOF_TOKEN); }
 
     char c{peek()};
 
-    token_start_col = column;
-
     if (is_digit(c)) { return get_number(); }
     else if (is_alpha(c)) { return get_identifier(); }
     else if (c == '"') { return get_text(); }
@@ -174,8 +173,8 @@ void Lexer::skip_whitespace()
         case '\t': next(); break;
         case '\n':
             line++;
-            column = 1;
             next();
+            column = 1;
             break;
         default: return;
         }
diff --git a/source/main.cc b/source/main.cc
index ab7916d403445d22e31e37bf646296f64c4e0def..5fba43d16accbbe298b04216de9f4a0c7ade220a 100644
--- a/source/main.cc
+++ b/source/main.cc
@@ -111,7 +111,13 @@ void process_file(const String& file, const Config& config)
         Node* ast = parser.parse();
         LOG_DEBUG("File parsed!");
 
-        if (config.ast) { LOG_INFO("AST: " + ast->to_s()); }
+        if (config.ast)
+        {
+            LOG_INFO("Abstract Syntax Tree:");
+            std::istringstream stream(ast->to_s());
+            String line;
+            while (std::getline(stream, line)) { LOG_INFO(line); }
+        }
 
         LOG_DEBUG("Evaluating AST...");
         Node* res{ast->evaluate()};
diff --git a/source/parser/Parser.cc b/source/parser/Parser.cc
index ae664d7382f19a7a2ad1fba5397848b63e9ccf63..949eaf2e90ef4ca1a04375c05700ebed4955a81b 100644
--- a/source/parser/Parser.cc
+++ b/source/parser/Parser.cc
@@ -10,7 +10,10 @@ Parser::~Parser() {}
 Node* Parser::parse()
 {
     LOG_DEBUG("Parse program");
-    return parse_statement();
+
+    ProgramNode* program = new ProgramNode(SourceLocation(filename, 0, 0));
+    while (!done()) { program->add(parse_statement()); }
+    return program;
 }
 
 Parser Parser::load(String filename)
@@ -42,7 +45,7 @@ Token Parser::peek_next() const
 
 bool Parser::done() const
 {
-    return index >= static_cast<int>(tokens.size());
+    return peek().get_type() == TokenType::EOF_TOKEN;
 }
 
 bool Parser::check(TokenType type) const
@@ -61,7 +64,15 @@ Node* Parser::parse_statement()
 {
     LOG_DEBUG("Parse statement");
     Node* expr{parse_expression()};
-    if (!match(TokenType::SEMICOLON)) throw SyntaxError(peek_prev().get_location(), "Expected ';'");
+
+    if (!match(TokenType::SEMICOLON))
+    {
+        Token prev{peek_prev()};
+        SourceLocation loc{prev.get_location()};
+        SourceLocation error_loc(loc.filename, loc.line, static_cast<int>(loc.column + prev.get_lexeme().length()));
+        throw SyntaxError(error_loc, "Expected ';'");
+    }
+
     return expr;
 }
 
@@ -185,8 +196,7 @@ Node* Parser::parse_unary()
 Node* Parser::parse_factor()
 {
     LOG_DEBUG("Parse factor");
-    if (check(TokenType::IDENTIFIER))
-        return parse_identifier();
+    if (check(TokenType::IDENTIFIER)) { return parse_identifier(); }
     else if (check(TokenType::NUMB) || check(TokenType::REAL) || check(TokenType::BOOL) || check(TokenType::CHAR) ||
              check(TokenType::TEXT))
     {
@@ -195,11 +205,19 @@ Node* Parser::parse_factor()
     else if (match(TokenType::L_PAR))
     {
         Node* expr{parse_expression()};
-        if (!match(TokenType::R_PAR)) throw SyntaxError(peek_prev().get_location(), "Expected ')'");
+
+        if (!match(TokenType::R_PAR))
+        {
+            Token prev{peek_prev()};
+            SourceLocation loc{prev.get_location()};
+            SourceLocation error_loc(loc.filename, loc.line, static_cast<int>(loc.column + prev.get_lexeme().length()));
+            throw SyntaxError(error_loc, "Expected ')'");
+        }
+
         return expr;
     }
 
-    throw SyntaxError(peek().get_location(), "???");
+    throw SyntaxError(peek_prev().get_location(), "Expected expression, got " + token_type_to_s(peek().get_type()));
 }
 
 Node* Parser::parse_literal()