Add parser
diff --git a/.gitignore b/.gitignore
index 1c85d0e..82ffdc1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,4 @@
 .idea
 cmake-build-*
+*.user
+build
diff --git a/AstNode.cpp b/AstNode.cpp
new file mode 100644
index 0000000..7bb84fa
--- /dev/null
+++ b/AstNode.cpp
@@ -0,0 +1,35 @@
+#include "AstNode.h"
+
+AstNode::AstNode(QString function, QList<AstNode> args)
+{
+    _type = FUNC;
+    _stringVal = function;
+    _listVal = args;
+}
+
+QList<AstNode> AstNode::funcArgs()
+{
+    return _listVal;
+}
+
+bool AstNode::isFunc() const
+{
+    return _type == FUNC;
+}
+
+AstNode::operator QString() const
+{
+    if (isFunc())
+    {
+        QStringList args;
+
+        for (const AstNode &node : _listVal)
+        {
+            args.append(static_cast<QString>(node));
+        }
+
+        return "<" + _stringVal + " " + args.join(" ") + ">";
+    }
+
+    return TokenBase<AstNode>::operator QString();
+}
diff --git a/AstNode.h b/AstNode.h
new file mode 100644
index 0000000..b10872a
--- /dev/null
+++ b/AstNode.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "Token.h"
+
+class AstNode : public TokenBase<AstNode>
+{
+public:
+    using TokenBase::TokenBase;
+
+    explicit AstNode(QString function, QList<AstNode> args);
+
+    enum
+    {
+        FUNC = TOKEN_TYPE_LAST
+    };
+
+    QList<AstNode> funcArgs();
+
+    bool isFunc() const;
+
+    operator QString() const;
+};
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 61c401e..472fecc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -11,9 +11,9 @@
         Core
         REQUIRED)
 
-add_executable(REFAL main.cpp Token.cpp Token.h Matcher.cpp Matcher.h VarContext.cpp VarContext.h)
+add_executable(REFAL main.cpp Token.cpp Token.h Matcher.cpp Matcher.h VarContext.cpp VarContext.h Parser.cpp Parser.h AstNode.cpp AstNode.h)
 target_link_libraries(REFAL
         Qt::Core
         )
 
-add_compile_options("-fsanitize=address")
\ No newline at end of file
+add_compile_options("-fsanitize=address")
diff --git a/Matcher.cpp b/Matcher.cpp
index 3fc4670..427bd76 100644
--- a/Matcher.cpp
+++ b/Matcher.cpp
@@ -2,8 +2,10 @@
 
 #include <QDebug>
 
-MatchResult match(QList<Token> data, QList<Token> pattern, VarContext context) {
-    if (data.empty() && pattern.empty()) {
+MatchResult match(QList<Token> data, QList<Token> pattern, VarContext context)
+{
+    if (data.empty() && pattern.empty())
+    {
         return MatchResult{true, context};
     }
 
@@ -11,92 +13,112 @@
     Token dataHead = data.first();
     pattern.removeFirst();
 
-    if (ph.isSym() || ph.isIdent()) {
-        if (ph == data.first()) {
+    if (ph.isSym() || ph.isIdent())
+    {
+        if (ph == data.first())
+        {
             data.removeFirst();
             return match(data, pattern, context);
-        } else {
+        }
+        else
+        {
             return MatchResult{false, context};
         }
-    } else if (ph.isParen()) {
+    }
+    else if (ph.isParen())
+    {
         data.removeFirst();
 
-        qDebug() << "parens, dh = " << dataHead;
-        qDebug() << "Matching parens" << dataHead.parenContent() << ph.parenContent();
-
         auto result = match(dataHead.parenContent(), ph.parenContent(), context);
 
-        qDebug() << "parens result was" << result.success << "in ctx" << result.context;
-
-        if (result.success) {
+        if (result.success)
+        {
             return match(data, pattern, result.context);
-        } else {
+        }
+        else
+        {
             return MatchResult{false, result.context};
         }
-    } else if (ph.isVar()) {
+    }
+    else if (ph.isVar())
+    {
         // is var bound?
-        if (context.exists(ph.name())) {
+        if (context.exists(ph.name()))
+        {
             // TODO: handle error elegantly if types don't match up (let's just assume the user isn't stupid)
 
-            switch (ph.varType()) {
-                case 's':
-                case 't':
-                    if (context.singleVar(ph.name()) == dataHead) {
-                        data.removeFirst();
-                        return match(data, pattern, context);
-                    } else {
-                        return MatchResult{false, context};
-                    }
-                case 'e':
-                    QList<Token> expected = context.expressionVar(ph.name());
+            switch (ph.varType())
+            {
+            case 's':
+            case 't':
+                if (context.singleVar(ph.name()) == dataHead)
+                {
+                    data.removeFirst();
+                    return match(data, pattern, context);
+                }
+                else
+                {
+                    return MatchResult{false, context};
+                }
+            case 'e':
+                QList<Token> expected = context.expressionVar(ph.name());
 
-                    if (listStartsWith(data, expected)) {
-                        listDrop(data, expected.length());
-                        return match(data, pattern, context);
-                    } else {
-                        return MatchResult{false, context};
-                    }
+                if (listStartsWith(data, expected))
+                {
+                    listDrop(data, expected.length());
+                    return match(data, pattern, context);
+                }
+                else
+                {
+                    return MatchResult{false, context};
+                }
             }
-        } else {
+        }
+        else
+        {
             bool typeIsOk = false;
 
-            switch (ph.varType()) {
-                case 't':
-                    if (dataHead.isParen())
-                        typeIsOk = true;
+            switch (ph.varType())
+            {
+            case 't':
+                if (dataHead.isParen())
+                    typeIsOk = true;
 
-                case 's':
-                    if (dataHead.isSym() || dataHead.isIdent())
-                        typeIsOk = true;
+            case 's':
+                if (dataHead.isSym() || dataHead.isIdent())
+                    typeIsOk = true;
 
-                    if (!typeIsOk) {
-                        return MatchResult{false, context};
-                    }
-
-                    context.add(ph.varType(), ph.name(), dataHead);
-                    data.removeFirst();
-
-                    return match(data, pattern, context);
-
-                case 'e':
-                    // Now this is tricky
-                    for (int matchSyms = 1; matchSyms < data.length(); matchSyms++) {
-                        QList<Token> slice = listSlice(data, 0, matchSyms);
-                        VarContext newContext = context;
-                        newContext.add(ph.varType(), ph.name(), slice);
-
-                        MatchResult tryMatch = match(listSlice(data, matchSyms, data.length()), pattern, newContext);
-                        if (tryMatch.success) {
-                            return tryMatch;
-                        }
-                        // else matchSyms ++
-                    }
-                    // If this worked we would have returned already
+                if (!typeIsOk)
+                {
                     return MatchResult{false, context};
+                }
 
-                default:
-                    qDebug() << "#TYPE_ERROR";
-                    return MatchResult{false, context};
+                context.add(ph.varType(), ph.name(), dataHead);
+                data.removeFirst();
+
+                return match(data, pattern, context);
+
+            case 'e':
+                // Now this is tricky
+                for (int matchSyms = 1; matchSyms < data.length(); matchSyms++)
+                {
+                    QList<Token> slice = listSlice(data, 0, matchSyms);
+                    VarContext newContext = context;
+                    newContext.add(ph.varType(), ph.name(), slice);
+
+                    MatchResult tryMatch = match(listSlice(data, matchSyms, data.length()), pattern, newContext);
+                    if (tryMatch.success)
+                    {
+                        return tryMatch;
+                    }
+                    // else matchSyms ++
+                }
+                // If this worked we would have returned already
+                return MatchResult{false, context};
+
+            default:
+                qDebug() << "#TYPE_ERROR";
+                return MatchResult{false, context};
             }
         }
     }
diff --git a/Matcher.h b/Matcher.h
index 84aee15..24fc795 100644
--- a/Matcher.h
+++ b/Matcher.h
@@ -3,17 +3,20 @@
 #include "Token.h"
 #include "VarContext.h"
 
-struct MatchResult {
+struct MatchResult
+{
     bool success;
     VarContext context;
 };
 
 template <typename T>
-bool listStartsWith(const QList<T> &haystack, const QList<T> &needle) {
+bool listStartsWith(const QList<T> &haystack, const QList<T> &needle)
+{
     if (needle.length() > haystack.length())
         return false;
 
-    for (int i = 0; i < needle.length(); i++) {
+    for (int i = 0; i < needle.length(); i++)
+    {
         if (haystack[i] != needle[i])
             return false;
     }
@@ -22,19 +25,23 @@
 }
 
 template <typename T>
-void listDrop(QList<T> &list, int n) {
-    for (; n; n--) {
+void listDrop(QList<T> &list, int n)
+{
+    for (; n; n--)
+    {
         list.removeFirst();
     }
 }
 
 template <typename T>
-QList<T> listSlice(QList<T> &list, int from, int to) {
+QList<T> listSlice(QList<T> &list, int from, int to)
+{
     QList<T> prime;
 
     // I guess we'll just panic if it's too long
     // TODO: ERROR HANDLING
-    for (int i = 0; i < to - from; i++) {
+    for (int i = 0; i < to - from; i++)
+    {
         prime.append(list[from + i]);
     }
 
diff --git a/Parser.cpp b/Parser.cpp
new file mode 100644
index 0000000..2d6b992
--- /dev/null
+++ b/Parser.cpp
@@ -0,0 +1,193 @@
+#include "Parser.h"
+
+#include <QDebug>
+
+Parser::Parser(QString input)
+{
+    _input = input;
+}
+
+QChar Parser::peek()
+{
+    if (atEnd())
+        return 0;
+
+    return _input[_pos];
+}
+
+QChar Parser::get()
+{
+    return _input[_pos++];
+}
+
+bool Parser::atEnd()
+{
+    return _pos >= _input.length();
+}
+
+void Parser::skip()
+{
+    while (peek().isSpace())
+        get();
+}
+
+bool Parser::parseSymbol(AstNode *node)
+{
+    skip();
+
+    if (peek().isLetter())
+    {
+        *node = AstNode(get());
+        return true;
+    }
+
+    return false;
+}
+
+bool Parser::parseIdentifier(AstNode *node)
+{
+    skip();
+
+    QString buffer;
+
+    if (peek().isUpper())
+    {
+        while (peek().isLetter() || peek() == '-' || peek() == '_' || peek().isNumber())
+        {
+            buffer += get();
+        }
+
+        *node = AstNode(buffer);
+        return true;
+    }
+
+    return false;
+}
+
+bool Parser::parseNumber(AstNode *node)
+{
+    skip();
+
+    QString buffer;
+
+    if (peek().isDigit())
+    {
+        while (peek().isDigit())
+            buffer += get();
+
+        *node = AstNode(buffer.toInt());
+        return true;
+    }
+
+    return false;
+}
+
+bool Parser::parseVariable(AstNode *node)
+{
+    skip();
+
+    int pos = _pos;
+
+    if (peek() == 's' || peek() == 'e' || peek() == 't')
+    {
+        char type = get().toLatin1();
+
+        if (peek() == '.')
+        {
+            get();
+
+            AstNode identNode;
+
+            if (parseIdentifier(&identNode))
+            {
+                *node = AstNode(type, identNode.name());
+                return true;
+            }
+        }
+    }
+
+    _pos = pos;
+    return false;
+}
+
+bool Parser::parseOne(AstNode *node)
+{
+    return parseFunction(node) ||
+           parseVariable(node) ||
+           parseNumber(node) ||
+           parseIdentifier(node) ||
+           parseSymbol(node) ||
+           parseParens(node);
+}
+
+QList<AstNode> Parser::parseMany()
+{
+    QList<AstNode> nodes;
+    AstNode next;
+
+    while (parseOne(&next))
+    {
+        nodes.append(next);
+    }
+
+    return nodes;
+}
+
+bool Parser::parseParens(AstNode *node)
+{
+    skip();
+
+    int pos = _pos;
+
+    if (peek() != '(')
+        return false;
+
+    get();
+
+    QList<AstNode> many = parseMany();
+    *node = AstNode(many);
+
+    skip();
+    if (peek() != ')')
+    {
+        _pos = pos;
+        return false;
+    }
+
+    get();
+
+    return true;
+}
+
+bool Parser::parseFunction(AstNode *node)
+{
+    skip();
+
+    int pos = _pos;
+
+    if (peek() != '<')
+        return false;
+
+    get();
+
+    AstNode head;
+    if (!parseIdentifier(&head))
+    {
+        _pos = pos;
+        return false;
+    }
+
+    QList<AstNode> body = parseMany();
+    *node = AstNode(head.name(), body);
+
+    skip();
+    if (peek() != '>')
+    {
+        _pos = pos;
+        return false;
+    }
+
+    get();
+
+    return true;
+}
diff --git a/Parser.h b/Parser.h
new file mode 100644
index 0000000..52cafac
--- /dev/null
+++ b/Parser.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <QString>
+
+#include "AstNode.h"
+
+class Parser
+{
+public:
+    explicit Parser(QString input);
+
+    QChar peek();
+    QChar get();
+    bool atEnd();
+
+    void skip();
+
+    bool parseSymbol(AstNode *node);
+    bool parseIdentifier(AstNode *node);
+    bool parseNumber(AstNode *node);
+    bool parseVariable(AstNode *node);
+    bool parseParens(AstNode *node);
+    bool parseFunction(AstNode *node);
+    QList<AstNode> parseMany();
+    bool parseOne(AstNode *node);
+
+private:
+    int _pos = 0;
+    QString _input;
+};
diff --git a/Token.cpp b/Token.cpp
index 4ebf058..56c32a9 100644
--- a/Token.cpp
+++ b/Token.cpp
@@ -1,103 +1,3 @@
 #include "Token.h"
 
-#include <QDebug>
-#include <utility>
-
-Token::Token(const Token &other) {
-    *this = other;
-}
-
-Token::Token(QChar symbol) {
-    _type = SYM;
-    _charVal = symbol;
-}
-
-Token::Token(QString &&identifier) {
-    _type = IDENT;
-    _stringVal = identifier;
-}
-
-Token::Token(QList<Token> parenthesized) {
-    _type = PAREN;
-    _listVal = std::move(parenthesized);
-}
-
-Token::Token(char varType, const QString &&name) {
-    _type = VAR;
-    _charVal = varType;
-    _stringVal = name;
-}
-
-bool Token::isSym() const {
-    return _type == SYM;
-}
-
-bool Token::isIdent() const {
-    return _type == IDENT;
-}
-
-bool Token::isParen() const {
-    return _type == PAREN;
-}
-
-bool Token::isVar() const {
-    return _type == VAR;
-}
-
-Token::Token() : Token("Null") {
-}
-
-bool Token::operator==(const Token &other) const {
-    return _type == other._type
-           && _stringVal == other._stringVal
-           && _charVal == other._charVal
-           && _listVal == other._listVal;
-}
-
-QList<Token> Token::parenContent() {
-    if (isParen()) {
-        return _listVal;
-    } else {
-        return {};
-    }
-}
-
-char Token::varType() const {
-    return _charVal.toLatin1();
-}
-
-const QString &Token::name() const {
-    return _stringVal;
-}
-
-bool Token::operator!=(const Token &other) const {
-    return !(this->operator==(other));
-}
-
-Token::operator QString() const {
-    if (isIdent())
-        return _stringVal;
-    if (isSym())
-        return _charVal;
-    if (isVar())
-        return QString(_charVal) + "." + _stringVal;
-    if (isParen()) {
-        QStringList parts;
-        for (const Token &tok : _listVal) {
-            parts.append(static_cast<QString>(tok));
-        }
-
-        return "(" + parts.join(" ") + ")";
-    }
-
-    return "Null";
-}
-
-int Token::type() const {
-    return _type;
-}
-
-QString Token::typeToString(int type)  {
-    static const QString typeNames[] = {"SYMBOL", "IDENT", "PAREN", "VAR"};
-    return typeNames[type];
-}
+// Nothing, aparently
diff --git a/Token.h b/Token.h
index 4835de0..76020ef 100644
--- a/Token.h
+++ b/Token.h
@@ -3,44 +3,196 @@
 #include <QList>
 #include <QChar>
 
-class Token {
+template <typename T>
+class TokenBase
+{
 public:
-    Token();
-    Token(const Token &other);
+    TokenBase();
+    TokenBase(const T &other);
 
-    explicit Token(QChar symbol);
-    explicit Token(QString &&identifier);
-    explicit Token(QList<Token> parenthesized);
-    Token(char varType, const QString &&name);
+    explicit TokenBase(QChar symbol);
+    explicit TokenBase(QString identifier);
+    explicit TokenBase(QList<T> parenthesized);
+    TokenBase(char varType, const QString name);
 
-    bool operator ==(const Token &other) const;
-    bool operator !=(const Token &other) const;
+    bool operator==(const T &other) const;
+    bool operator!=(const T &other) const;
 
     bool isSym() const;
     bool isIdent() const;
     bool isParen() const;
     bool isVar() const;
 
-    QList<Token> parenContent();
+    QList<T> parenContent();
 
     char varType() const;
     const QString &name() const;
 
     operator QString() const;
 
-    enum Type {
-        SYM, IDENT, PAREN, VAR,
+    enum
+    {
+        SYM,
+        IDENT,
+        PAREN,
+        VAR,
+        TOKEN_TYPE_LAST,
     };
 
     static QString typeToString(int type);
 
     int type() const;
 
-private:
+protected:
     int _type = 0;
     QString _stringVal = "";
-    QList<Token> _listVal;
+    QList<T> _listVal;
     QChar _charVal = 0;
 };
 
+class Token : public TokenBase<Token>
+{
+public:
+    using TokenBase::TokenBase;
+};
+
 using LTok = QList<Token>;
+
+template <typename T>
+TokenBase<T>::TokenBase(const T &other)
+{
+    _type = other._type;
+    _stringVal = other._stringVal;
+    _listVal = other._listVal;
+    _charVal = other._charVal;
+}
+
+template <typename T>
+TokenBase<T>::TokenBase(QChar symbol)
+{
+    _type = SYM;
+    _charVal = symbol;
+}
+
+template <typename T>
+TokenBase<T>::TokenBase(QString identifier)
+{
+    _type = IDENT;
+    _stringVal = identifier;
+}
+
+template <typename T>
+TokenBase<T>::TokenBase(QList<T> parenthesized)
+{
+    _type = PAREN;
+    _listVal = std::move(parenthesized);
+}
+
+template <typename T>
+TokenBase<T>::TokenBase(char varType, const QString name)
+{
+    _type = VAR;
+    _charVal = varType;
+    _stringVal = name;
+}
+
+template <typename T>
+bool TokenBase<T>::isSym() const
+{
+    return _type == SYM;
+}
+
+template <typename T>
+bool TokenBase<T>::isIdent() const
+{
+    return _type == IDENT;
+}
+
+template <typename T>
+bool TokenBase<T>::isParen() const
+{
+    return _type == PAREN;
+}
+template <typename T>
+bool TokenBase<T>::isVar() const
+{
+    return _type == VAR;
+}
+
+template <typename T>
+TokenBase<T>::TokenBase() : TokenBase("Null")
+{
+}
+
+template <typename T>
+bool TokenBase<T>::operator==(const T &other) const
+{
+    return _type == other._type && _stringVal == other._stringVal && _charVal == other._charVal && _listVal == other._listVal;
+}
+
+template <typename T>
+QList<T> TokenBase<T>::parenContent()
+{
+    if (isParen())
+    {
+        return _listVal;
+    }
+    else
+    {
+        return {};
+    }
+}
+
+template <typename T>
+char TokenBase<T>::varType() const
+{
+    return _charVal.toLatin1();
+}
+
+template <typename T>
+const QString &TokenBase<T>::name() const
+{
+    return _stringVal;
+}
+
+template <typename T>
+bool TokenBase<T>::operator!=(const T &other) const
+{
+    return !(this->operator==(other));
+}
+
+template <typename T>
+TokenBase<T>::operator QString() const
+{
+    if (isIdent())
+        return _stringVal;
+    if (isSym())
+        return _charVal;
+    if (isVar())
+        return QString(_charVal) + "." + _stringVal;
+    if (isParen())
+    {
+        QStringList parts;
+        for (const T &tok : _listVal)
+        {
+            parts.append(static_cast<QString>(tok));
+        }
+
+        return "(" + parts.join(" ") + ")";
+    }
+
+    return "Null";
+}
+
+template <typename T>
+int TokenBase<T>::type() const
+{
+    return _type;
+}
+
+template <typename T>
+QString TokenBase<T>::typeToString(int type)
+{
+    static const QString typeNames[] = {"SYMBOL", "IDENT", "PAREN", "VAR"};
+    return typeNames[type];
+}
diff --git a/main.cpp b/main.cpp
index ebf886f..fc9d547 100644
--- a/main.cpp
+++ b/main.cpp
@@ -3,89 +3,113 @@
 
 #include "Matcher.h"
 #include "Token.h"
+#include "AstNode.h"
+#include "Parser.h"
 
 int g_numFailed = 0;
 
-void printResult(const QString &test, bool shouldBe, const MatchResult &result) {
-    if (result.success != shouldBe) {
+void testMatch(const QString &test, bool shouldBe, const MatchResult &result)
+{
+    if (result.success != shouldBe)
+    {
         g_numFailed++;
-        qDebug() << "\nTEST FAILS:";
+        qDebug() << "\n\033[31mTEST FAILS:\033[0m";
         qDebug() << "with context" << result.context;
     }
 
-    qDebug() << "MatchResult" << test << result.success;
+    qDebug() << "\033[36mMatchResult\033[0m" << test << result.success;
 
-    if (result.success != shouldBe) {
+    if (result.success != shouldBe)
+    {
         qDebug() << "";
     }
 }
 
-int testResults() {
-    if (g_numFailed == 0) {
-        qDebug() << "ALL TESTS SUCCEEDED";
-    } else {
-        qDebug() << g_numFailed << "TESTS FAILED";
+void testParse(QString string)
+{
+    Parser parser{string};
+
+    QList<AstNode> result = parser.parseMany();
+
+    qDebug() << "\033[36mParse\033[0m" << string << result;
+}
+
+int testResults()
+{
+    if (g_numFailed == 0)
+    {
+        qDebug() << "\033[32mALL TESTS SUCCEEDED\033[0m";
+    }
+    else
+    {
+        qDebug().nospace() << "\033[31m" << g_numFailed << " TESTS FAILED\033[0m";
     }
 
     return g_numFailed;
 }
 
-void runTests() {
-    printResult("a = a", true, match({Token('a')}, {Token('a')}, VarContext()));
+void testAllMatches()
+{
+    testMatch("a = a", true, match({Token('a')}, {Token('a')}, VarContext()));
 
-    printResult("s.a = y", true, match({Token('y')}, {Token('s', "a")}, VarContext()));
+    testMatch("s.a = y", true, match({Token('y')}, {Token('s', "a")}, VarContext()));
 
     LTok sameTwo = {Token('s', "a"), Token('s', "a")};
-    printResult("s.a s.a = aa", true, match({Token('a'), Token('a')}, sameTwo, VarContext()));
-    printResult("s.a s.a = ab", false, match({Token('a'), Token('b')}, sameTwo, VarContext()));
+    testMatch("s.a s.a = aa", true, match({Token('a'), Token('a')}, sameTwo, VarContext()));
+    testMatch("s.a s.a = ab", false, match({Token('a'), Token('b')}, sameTwo, VarContext()));
 
     LTok sameStartEnd = {
-            Token('s', "a"),
-            Token('e', "middle"),
-            Token('s', "a")
-    };
-    printResult("s.a e.middle s.a = aea", true,
-                match({
-                              Token('a'), Token('e'), Token('a')
-                      }, sameStartEnd, VarContext()));
+        Token('s', "a"),
+        Token('e', "middle"),
+        Token('s', "a")};
+    testMatch("s.a e.middle s.a = aea", true,
+                match({Token('a'), Token('e'), Token('a')}, sameStartEnd, VarContext()));
 
-    printResult("s.a e.middle s.a = aef Hi a", true,
-                match({
-                              Token('a'), Token('e'), Token('f'), Token("Hi"), Token('a')
-                      }, sameStartEnd, VarContext()));
+    testMatch("s.a e.middle s.a = aef Hi a", true,
+                match({Token('a'), Token('e'), Token('f'), Token("Hi"), Token('a')}, sameStartEnd, VarContext()));
 
-    printResult("s.a e.middle s.a = aef Hi c", false,
-                match({
-                              Token('a'), Token('e'), Token('f'), Token("Hi"), Token('c')
-                      }, sameStartEnd, VarContext()));
+    testMatch("s.a e.middle s.a = aef Hi c", false,
+                match({Token('a'), Token('e'), Token('f'), Token("Hi"), Token('c')}, sameStartEnd, VarContext()));
 
     LTok parenthesized = {
-            Token(LTok({
-                               Token('s', "a")
-                       })),
-            Token('e', "Middle"),
-            Token('s', "a"),
+        Token(LTok({Token('s', "a")})),
+        Token('e', "Middle"),
+        Token('s', "a"),
     };
     LTok parenTest1 = {
-            Token(LTok({
-                               Token('y')
-                       })),
-            Token('f'),
-            Token("MiddleStuff"),
-            Token('y')
-    };
+        Token(LTok({Token('y')})),
+        Token('f'),
+        Token("MiddleStuff"),
+        Token('y')};
 
-    printResult("(s.a) e.Middle s.a = (y)f MiddleStuff y", true,
+    testMatch("(s.a) e.Middle s.a = (y)f MiddleStuff y", true,
                 match(parenTest1, parenthesized, VarContext()));
 
-    printResult("(a) = (a)", true,
+    testMatch("(a) = (a)", true,
                 match({Token({Token('a')})}, {Token({Token('a')})}, VarContext()));
 }
 
-int main(int argc, char *argv[]) {
+void testAllParses()
+{
+    testParse("all symbols");
+    testParse("Identifier symbols Identifier");
+    testParse("s.A");
+    testParse("(s.A) Variable-quoted");
+    testParse("<Func-name a b (c)>");
+    testParse("<Prout hi>");
+    testParse("(Prout hi)");
+    testParse("(<Prout hi>)");
+    testParse("<If T Then (<Prout hi>) Else (<Prout sorry>)>");
+}
+
+int main(int argc, char *argv[])
+{
     QCoreApplication a(argc, argv);
 
-    runTests();
+    testAllMatches();
+    qDebug() << "";
+    testAllParses();
 
+    qDebug() << "";
     return testResults();
 }
diff --git a/notes.txt b/notes.txt
new file mode 100644
index 0000000..d101e9c
--- /dev/null
+++ b/notes.txt
@@ -0,0 +1,8 @@
+export QT_LOGGING_RULES="*.debug=true"
+
+
+OPTIMIZATION POSSIBILITIES:
+
+	When parsing e.* variables check the min length of the remaining tokens. If
+	we have `e.1 s.2' for instance, we can tell right away how long `e.1' wil
+	have to be.