diff --git a/src/lang.cc b/src/lang.cc
index 3eb082d..7ba88f3 100644
--- a/src/lang.cc
+++ b/src/lang.cc
@@ -1,30 +1,47 @@
 #include "lang.h"
 
+const char *here(int row, int col) {
+  char *ret = (char *)malloc(sizeof(char *));
+  std::sprintf(ret, "%s:%d:%d", filename.string().c_str(), row, col);
+  return ret;
+}
+
+int ferr(const char *fmt, ...) {
+  std::va_list va;
+  va_start(va, fmt);
+  vfprintf(stderr, fmt, va);
+  va_end(va);
+  return 1;
+}
+
 int read_file(std::deque<std::string> args) {
   if (args.empty()) {
-    std::cout << bg_red(bold("ERROR")) << ": No file input" << std::endl;
-    return 1;
+    return ferr("%s: No file input.\n", bg_red(bold("ERROR")).c_str());
   }
-  fs::path filename = args[0];
+  filename = args[0];
   args.pop_front();
   std::ifstream file(filename);
   if (!file) {
-    std::cout << bg_red(bold("ERROR")) << ": Could not find file" << std::endl;
-    return 1;
+    return ferr("%s: Could not find file.\n", bg_red(bold("ERROR")).c_str());
   }
   std::string line;
+  int row = 1;
   while (std::getline(file, line)) {
-    std::cout << line << std::endl;
+    int error = parse_line(line, row);
+    if (error)
+      return 1;
+    row++;
   }
   return 0;
 }
 
-int parse_line(std::string line) {
+int parse_line(std::string line, int row) {
+  int col = 1;
   struct parsed_token parsed {
     empty_token, empty_token
   };
-  if (int sub = line.find("//"))
-    line = line.substr(sub);
+  if (line.length() == 0)
+    return 0;
   std::deque<std::string> tokens;
   int start = 0;
   size_t end = line.find(" ");
@@ -34,30 +51,106 @@ int parse_line(std::string line) {
     end = line.find(" ", start);
   }
   for (std::string &token : tokens) {
+    if (token == "//")
+      return 0;
+    const char *ctoken = token.c_str();
     try {
       Token curr_token = CV_Tokens.at(token);
-      parsed.lastToken = parsed.currentToken;
-      if (curr_token == parsed.lastToken && curr_token.type == TokenType::SPACE)
+      parsed.last_token = parsed.current_token;
+      if (curr_token == parsed.last_token &&
+          curr_token.type == TokenType::SPACE)
         continue;
-      else {
-        // TODO: Line number handling for better errors
-        std::cerr << "Invalid token `" << token << "'." << std::endl;
-        return 1;
+      parsed.current_token = curr_token;
+      switch (parsed.current_token.type) {
+      case TokenType::COLONCOLON: {
+        ferr("%s: Token `%s' is not yet implemented.\n", here(row, col),
+             ctoken);
+        break;
+      }
+      case TokenType::COMMA: {
+        ferr("%s: Token `%s' is not yet implemented.\n", here(row, col),
+             ctoken);
+        break;
+      }
+      case TokenType::EQ: {
+        ferr("%s: Token `%s' is not yet implemented.\n", here(row, col),
+             ctoken);
+        break;
+      }
+      case TokenType::LEFT_PAREN: {
+        ferr("%s: Token `%s' is not yet implemented.\n", here(row, col),
+             ctoken);
+        break;
+      }
+      case TokenType::RIGHT_PAREN: {
+        ferr("%s: Token `%s' is not yet implemented.\n", here(row, col),
+             ctoken);
+        break;
+      }
+      }
+      switch (parsed.current_token.intrinsic) {
+      case Intrinsic::INCLUDE: {
+        ferr("%s: Intrinsic `%s' is not yet implemented.\n", here(row, col),
+             ctoken);
+        break;
+      }
+      case Intrinsic::ENTER: {
+        ferr("%s: Intrinsic `%s' is not yet implemented.\n", here(row, col),
+             ctoken);
+        break;
+      }
+      case Intrinsic::EXIT: {
+        ferr("%s: Intrinsic `%s' is not yet implemented.\n", here(row, col),
+             ctoken);
+        break;
+      }
+      case Intrinsic::HIDE: {
+        ferr("%s: Intrinsic `%s' is not yet implemented.\n", here(row, col),
+             ctoken);
+        break;
+      }
+      case Intrinsic::LET: {
+        ferr("%s: Intrinsic `%s' is not yet implemented.\n", here(row, col),
+             ctoken);
+        break;
+      }
+      case Intrinsic::SHOW: {
+        ferr("%s: Intrinsic `%s' is not yet implemented.\n", here(row, col),
+             ctoken);
+        break;
+      }
+      case Intrinsic::UNLET: {
+        ferr("%s: Intrinsic `%s' is not yet implemented.\n", here(row, col),
+             ctoken);
+        break;
+      }
+      default:
+        break;
+      }
+      switch (parsed.current_token.constant) {
+      case Constant::CHARACTER: {
+        ferr("%s: Constant `%s' is not yet implemented.\n", here(row, col),
+             ctoken);
+        break;
+      }
+      case Constant::IMAGE: {
+        ferr("%s: Constant `%s' is not yet implemented.\n", here(row, col),
+             ctoken);
+        break;
+      }
+      case Constant::LOCATION: {
+        ferr("%s: Constant `%s' is not yet implemented.\n", here(row, col),
+             ctoken);
+        break;
+      }
+      default:
+        break;
       }
-      parsed.currentToken = curr_token;
     } catch (std::out_of_range &oor) {
       // this is most likely a variable or a string
-      if (parsed.lastToken.type == TokenType::INTRINSIC &&
-          parsed.lastToken.intrinsic == Intrinsic::LET) {
-        VariableToken var{TokenType::VARIABLE, Intrinsic::EMPTY,
-                          Constant::EMPTY, "", token};
-        parsed.currentToken = var;
-        var_tokens.push_back(var);
-      }
-      // string parsing
-      std::cerr << "Not implemented. Found `" << token << "'." << std::endl;
-      continue;
+      ferr("%s: Invalid token `%s'.\n", here(row, col), ctoken);
     }
+    col += token.length() + 1;
   }
   return 0;
 }
diff --git a/src/lang.h b/src/lang.h
index 2a9ce16..b261fb2 100644
--- a/src/lang.h
+++ b/src/lang.h
@@ -1,14 +1,15 @@
 #pragma once
 
 #include "colors.h"
+#include <cstdarg>
 #include <deque>
 #include <filesystem>
 #include <fstream>
 #include <iostream>
-#include <map>
 #include <sstream>
 #include <stdexcept>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 namespace fs = std::filesystem;
@@ -27,7 +28,7 @@ enum class TokenType {
   VARIABLE
 };
 
-enum class Intrinsic { EMPTY, LET, UNLET, ENTER, EXIT, SHOW, HIDE };
+enum class Intrinsic { INCLUDE, EMPTY, LET, UNLET, ENTER, EXIT, SHOW, HIDE };
 
 enum class Constant { EMPTY, CHARACTER, LOCATION, IMAGE };
 
@@ -59,7 +60,7 @@ struct VariableToken : Token {
   }
 };
 
-std::map<std::string, Token> CV_Tokens{
+std::unordered_map<std::string, Token> CV_Tokens{
     {"", Token{TokenType::EMPTY, Intrinsic::EMPTY, Constant::EMPTY, "", ""}},
     {" ", Token{TokenType::SPACE, Intrinsic::EMPTY, Constant::EMPTY, "", ""}},
     {"(",
@@ -70,6 +71,8 @@ std::map<std::string, Token> CV_Tokens{
      Token{TokenType::COLONCOLON, Intrinsic::EMPTY, Constant::EMPTY, "", ""}},
     {"=", Token{TokenType::EQ, Intrinsic::EMPTY, Constant::EMPTY, "", ""}},
     {",", Token{TokenType::COMMA, Intrinsic::EMPTY, Constant::EMPTY, "", ""}},
+    {"%include",
+     Token{TokenType::INTRINSIC, Intrinsic::INCLUDE, Constant::EMPTY, "", ""}},
     {"let",
      Token{TokenType::INTRINSIC, Intrinsic::LET, Constant::EMPTY, "", ""}},
     {"unlet",
@@ -92,11 +95,14 @@ std::map<std::string, Token> CV_Tokens{
 Token empty_token{TokenType::EMPTY, Intrinsic::EMPTY, Constant::EMPTY, "", ""};
 
 struct parsed_token {
-  Token lastToken;
-  Token currentToken;
+  Token last_token;
+  Token current_token;
 };
 
 std::vector<VariableToken> var_tokens;
+fs::path filename;
 
+const char *here(int row, int col);
+int ferr(std::string fmt, ...);
 int read_file(std::deque<std::string> args);
-int parse_line(std::string line);
+int parse_line(std::string line, int row);
diff --git a/test/lang_test.cc b/test/lang_test.cc
index f55f52a..f0870a1 100644
--- a/test/lang_test.cc
+++ b/test/lang_test.cc
@@ -4,9 +4,11 @@
 
 int main(int argc, char **argv) {
   if (argc != 2) {
+    std::cout << "Wrong amount of args" << std::endl;
     return 1;
   }
   std::deque<std::string> args;
   args.push_back(std::string(argv[1]));
   return read_file(args);
+  return 0;
 }