Add detailed parse errors
diff --git a/Parser.cpp b/Parser.cpp
index 145972f..9b8f264 100644
--- a/Parser.cpp
+++ b/Parser.cpp
@@ -2,6 +2,41 @@
 
 #include <QDebug>
 
+ParseResult::ParseResult(bool okay)
+	: ParseResult((int)okay)
+{}
+
+ParseResult::ParseResult(int status, ParsePos pos)
+	: ParseResult(status, "", pos)
+{}
+
+ParseResult::ParseResult(int status, QString message, ParsePos pos)
+{
+	_status = status;
+	_message = message;
+	_pos = pos;
+}
+
+ParseResult::operator bool() const
+{
+	return _status == COMPLETE;
+}
+
+ParsePos ParseResult::pos() const
+{
+	return _pos;
+}
+
+QString ParseResult::message() const
+{
+	return _message;
+}
+
+int ParseResult::status() const
+{
+	return _status;
+}
+
 Parser::Parser(QString input)
 {
     _input = input;
@@ -17,7 +52,19 @@
 
 QChar Parser::get()
 {
-    return _input[_pos++];
+    QChar c = _input[_pos++];
+
+	if (c == '\n')
+	{
+		_line++;
+		_offset = 0;
+	}
+	else
+	{
+		_offset++;
+	}
+
+	return c;
 }
 
 bool Parser::atEnd()
@@ -31,8 +78,20 @@
         get();
 }
 
+ParsePos Parser::save() const
+{
+	return ParsePos{_line, _pos, _offset};
+}
+
+void Parser::reset(ParsePos pos)
+{
+	_line = pos.line;
+	_pos = pos.pos;
+	_offset = pos.lineOffset;
+}
+
 template <typename T>
-bool Parser::parseSymbol(T *node)
+ParseResult Parser::parseSymbol(T *node)
 {
     skip();
 
@@ -46,7 +105,7 @@
 }
 
 template <typename T>
-bool Parser::parseIdentifier(T *node)
+ParseResult Parser::parseIdentifier(T *node)
 {
     skip();
 
@@ -67,7 +126,7 @@
 }
 
 template <typename T>
-bool Parser::parseNumber(T *node)
+ParseResult Parser::parseNumber(T *node)
 {
     skip();
 
@@ -86,11 +145,11 @@
 }
 
 template <typename T>
-bool Parser::parseVariable(T *node)
+ParseResult Parser::parseVariable(T *node)
 {
     skip();
 
-    int pos = _pos;
+	ParsePos pos = save();
 
     if (peek() == 's' || peek() == 'e' || peek() == 't')
     {
@@ -112,47 +171,68 @@
                 *node = T(type, QString(nameNode.symbol()));
                 return true;
             }
+			else
+			{
+				ParseResult ret(ParseResult::INCOMPLETE,
+								"Expected identifier or symbol after . in variable",
+								pos);
+
+				reset(pos);
+
+				return ret;
+			}
         }
     }
 
-    _pos = pos;
+	reset(pos);
     return false;
 }
 
 template <typename T>
-QList<T> Parser::parseMany()
+ParseResult Parser::parseMany(QList<T> *list)
 {
-    QList<T > nodes;
+    QList<T> nodes;
     T next;
+	ParseResult ret;
 
-    while (parseOne(&next))
+    while ((ret = parseOne(&next)))
     {
+		// qDebug() << "parseMany one" << next;
         nodes.append(next);
     }
 
-    return nodes;
+	*list = nodes;
+
+	if (ret.status() == ParseResult::INCOMPLETE)
+		return ret;
+	else
+		return true;
 }
 
 template <typename T>
-bool Parser::parseParens(T *node)
+ParseResult Parser::parseParens(T *node)
 {
     skip();
 
-    int pos = _pos;
+    ParsePos pos = save();
 
     if (peek() != '(')
         return false;
 
     get();
 
-    QList<T> many = parseMany<T>();
+    QList<T> many;
+	ParseResult ret = parseMany(&many);
+
     *node = T(many);
 
     skip();
     if (peek() != ')')
     {
-        _pos = pos;
-        return false;
+		ret = ParseResult(ParseResult::INCOMPLETE, "Expected ) in parenthesized list", save());
+		reset(pos);
+
+		return ret;
     }
 
     get();
@@ -160,11 +240,12 @@
     return true;
 }
 
-bool Parser::parseFunction(AstNode *node)
+ParseResult Parser::parseFunction(AstNode *node)
 {
     skip();
 
-    int pos = _pos;
+    ParsePos pos = save();
+	ParseResult ret;
 
     if (peek() != '<')
         return false;
@@ -172,20 +253,29 @@
     get();
 
     AstNode head;
-    if (!parseIdentifier(&head))
+    if (!(ret = parseIdentifier(&head)))
     {
-        _pos = pos;
-        return false;
+		reset(pos);
+		return ret;
     }
 
-    QList<AstNode> body = parseMany<AstNode>();
+    QList<AstNode> body;
+	ret = parseMany(&body);
+
+	if (!ret)
+	{
+		reset(pos);
+		return ret;
+	}
+
     *node = AstNode(head.name(), body);
 
     skip();
     if (peek() != '>')
     {
-        _pos = pos;
-        return false;
+		ret = ParseResult(ParseResult::INCOMPLETE, "Expected >", save());
+		reset(pos);
+        return ret;
     }
 
     get();
@@ -194,7 +284,7 @@
 }
 
 template <>
-bool Parser::parseOne<Token>(Token *node)
+ParseResult Parser::parseOne<Token>(Token *node)
 {
     return parseVariable(node) ||
            parseNumber(node) ||
@@ -204,7 +294,7 @@
 }
 
 template <>
-bool Parser::parseOne<AstNode>(AstNode *node)
+ParseResult Parser::parseOne<AstNode>(AstNode *node)
 {
     return parseFunction(node) ||
            parseVariable(node) ||
@@ -214,43 +304,74 @@
            parseParens(node);
 }
 
-bool Parser::parseSentence(Sentence *sentence)
+ParseResult Parser::parseSentence(Sentence *sentence)
 {
-    int pos = _pos;
+	ParsePos pos = save();
 
-    QList<Token> pattern = parseMany<Token>();
+	qDebug() << "Parsing sentence" << peek();
+
+	if (peek() == '}')
+	{
+		return false;
+	}
+
+    QList<Token> pattern;
+	ParseResult ret = parseMany(&pattern);
+
+	if (!ret)
+	{
+		qDebug() << "Many failed" << ret.message();
+		reset(pos);
+		return ret;
+	}
 
     skip();
 
+	qDebug() << "will we get an =?" << peek();
+
     if (get() != '=')
     {
-        _pos = pos;
-        return false;
+        ret = ParseResult(ParseResult::INCOMPLETE, "Expected = in sentence", save());
+		reset(pos);
+		return ret;
     }
 
-    QList<AstNode> result = parseMany<AstNode>();
+    QList<AstNode> result;
+	ret = parseMany(&result);
+
+	if (!ret)
+	{
+		qDebug() << "sentence parseMany returned" << ret.message();
+		reset(pos);
+		return ret;
+	}
 
     skip();
 
-    if (get() != ';')
+	qDebug() << "end of sentence" << peek();
+
+    if (peek() != '}' && get() != ';')
     {
-        _pos = pos;
-        return false;
+		ret = ParseResult(ParseResult::INCOMPLETE, "Expected ; or } after sentence", save());
+		reset(pos);
+		return ret;
     }
 
     *sentence = Sentence(pattern, result);
+
     return true;
 }
 
-bool Parser::parseFunctionDefinition(Function *function)
+ParseResult Parser::parseFunctionDefinition(Function *function)
 {
-    int pos = _pos;
+	ParsePos pos = save();
+	ParseResult ret;
 
     Token identifier;
-    if (!parseIdentifier(&identifier))
+    if (!(ret = parseIdentifier(&identifier)))
     {
-        _pos = pos;
-        return false;
+		reset(pos);
+        return ret;
     }
 
     QString name = identifier.name();
@@ -260,23 +381,33 @@
 
     if (get() != '{')
     {
-        _pos = pos;
-        return false;
+		reset(pos);
+		return false;
     }
 
     Sentence sentence;
-    while (parseSentence(&sentence))
+    while ((ret = parseSentence(&sentence)))
     {
         func.addSentence(sentence);
         skip();
     }
 
+	if (ret.status() == ParseResult::INCOMPLETE)
+	{
+		qDebug() << "Function incomplete";
+		reset(pos);
+		return ret;
+	}
+
     if (get() != '}')
     {
-        _pos = pos;
-        return false;
+		ret = ParseResult(ParseResult::INCOMPLETE, "Expected } at end of function");
+		reset(pos);
+        return ret;
     }
 
+	qDebug() << "Function parsing succeeded";
+
     *function = func;
     return true;
 }
diff --git a/Parser.h b/Parser.h
index 1426989..f65e787 100644
--- a/Parser.h
+++ b/Parser.h
@@ -6,6 +6,40 @@
 #include "AstNode.h"
 #include "Function.h"
 
+struct ParsePos
+{
+	int line = 0,
+		pos = 0,
+		lineOffset = 0;
+};
+
+class ParseResult
+{
+public:
+	ParseResult() = default;
+	ParseResult(bool okay);
+	explicit ParseResult(int status, ParsePos pos = {});
+	ParseResult(int status, QString message, ParsePos pos = {});
+	
+	enum
+	{
+		NO_MATCH,
+		COMPLETE,
+		INCOMPLETE,
+	};
+
+	operator bool() const;
+
+	ParsePos pos() const;
+	QString message() const;
+	int status() const;
+
+private:
+	int _status = COMPLETE;
+	QString _message = "";
+	ParsePos _pos;
+};
+
 class Parser
 {
 public:
@@ -18,38 +52,44 @@
     void skip();
 
     template <typename T>
-    bool parseSymbol(T *node);
+    ParseResult parseSymbol(T *node);
 
     template <typename T>
-    bool parseIdentifier(T *node);
+    ParseResult parseIdentifier(T *node);
 
     template <typename T>
-    bool parseNumber(T *node);
+    ParseResult parseNumber(T *node);
 
     template <typename T>
-    bool parseVariable(T *node);
+    ParseResult parseVariable(T *node);
 
     template <typename T>
-    bool parseParens(T *node);
+    ParseResult parseParens(T *node);
 
-    bool parseFunction(AstNode *node);
+    ParseResult parseFunction(AstNode *node);
 
     template <typename T>
-    QList<T> parseMany();
+    ParseResult parseMany(QList<T> *list);
 
     template <typename T>
-    bool parseOne(T *node);
+    ParseResult parseOne(T *node);
 
-    bool parseSentence(Sentence *sentence);
-    bool parseFunctionDefinition(Function *function);
+    ParseResult parseSentence(Sentence *sentence);
+    ParseResult parseFunctionDefinition(Function *function);
+
+	ParsePos save() const;
+	void reset(ParsePos pos);
 
 private:
     int _pos = 0;
+	int _line = 1;
+	int _offset = 1;
+
     QString _input;
 };
 
 template <>
-bool Parser::parseOne<Token>(Token *node);
+ParseResult Parser::parseOne<Token>(Token *node);
 
 template <>
-bool Parser::parseOne<AstNode>(AstNode *node);
+ParseResult Parser::parseOne<AstNode>(AstNode *node);
diff --git a/Repl.cpp b/Repl.cpp
index 6137c19..0bddf3d 100644
--- a/Repl.cpp
+++ b/Repl.cpp
@@ -127,7 +127,9 @@
 		return true;
 	}
 
-	*expr = parser.parseMany<AstNode>();
+	if (!parser.parseMany(expr))
+		return false;
+
 	parser.skip();
 
 	return parser.atEnd();
diff --git a/main.cpp b/main.cpp
index 12f6dea..8e29b11 100644
--- a/main.cpp
+++ b/main.cpp
@@ -23,26 +23,31 @@
 
 	Function func;
 
-	QList<AstNode> expr = exprParser.parseMany<AstNode>();
-	QList<Token> res = resParser.parseMany<Token>();
+	QList<AstNode> expr;
+	ParseResult exprRet = exprParser.parseMany<AstNode>(&expr);
+	QList<Token> res;
+	ParseResult resRet = resParser.parseMany<Token>(&res);
+	ParseResult funcRet;
 
 	QList<Token> result;
 
 	exprParser.skip();
 	resParser.skip();
-	while (funcParser.parseFunctionDefinition(&func))
+	while ((funcRet = funcParser.parseFunctionDefinition(&func)))
 	{
 		eval.addFunction(func);
 	}
 
 	funcParser.skip();
 
-	if (!exprParser.atEnd() || !resParser.atEnd() || !funcParser.atEnd())
+	if (!exprRet || !resRet || funcRet.status() == ParseResult::INCOMPLETE)
 	{
         g_numFailed++;
         qDebug() << "\n\033[31mTEST FAILS:\033[0m";
 		qDebug() << "Failed to fully parse expression, function or result";
 		qDebug() << function << expression << expected;
+		qDebug() << funcRet.message() << exprRet.message() << resRet.message();
+		qDebug() << funcRet << exprRet << resRet;
 
 		goto end;
 	}
@@ -103,15 +108,21 @@
     Parser dataParser(data),
         patternParser(pattern);
 
+	QList<Token> parsedData, parsedPattern;
+	
+	dataParser.parseMany<Token>(&parsedData);
+	patternParser.parseMany<Token>(&parsedPattern);
+
     testMatch(pattern + " = " + data, shouldBe,
-			  match(dataParser.parseMany<Token>(), patternParser.parseMany<Token>(), VarContext()));
+			  match(parsedData, parsedPattern, VarContext()));
 }
 
 void testParseAst(QString string, QList<AstNode> equals = {})
 {
     Parser parser{string};
 
-    QList<AstNode> result = parser.parseMany<AstNode>();
+    QList<AstNode> result;
+	parser.parseMany<AstNode>(&result);
 
 	if (!equals.empty() && result != equals)
 	{
@@ -126,13 +137,15 @@
 void testParseFunc(QString string)
 {
     Parser parser{string};
+	ParseResult ret;
 
     Function func;
 
-    if (!parser.parseFunctionDefinition(&func))
+    if (!(ret = parser.parseFunctionDefinition(&func)))
     {
         g_numFailed++;
         qDebug() << "\n\033[31mTEST FAILS:\033[0m";
+		qDebug() << ret.message();
         qDebug() << string;
     }
     else