Add REPL
diff --git a/Repl.cpp b/Repl.cpp
new file mode 100644
index 0000000..415602f
--- /dev/null
+++ b/Repl.cpp
@@ -0,0 +1,129 @@
+#include <stdio.h>
+#include <string.h>
+
+#include <QDebug>
+
+#include "Repl.h"
+#include "Parser.h"
+#include "PPrint.h"
+
+// JANK! librl isn't namespaced!
+namespace ReadLine
+{
+#include <readline/readline.h>
+#include <readline/history.h>
+}
+
+
+Repl::Repl()
+{
+}
+
+char *Repl::prompt()
+{
+	static char p[] = "\033[36mREFAL >\033[0m ";
+	return p;
+}
+
+QString Repl::readLine()
+{
+	char *line = ReadLine::readline(prompt());
+
+	if (!line)
+	{
+		_running = false;
+		return "";
+	}
+
+	ReadLine::add_history(line);
+
+	QString string = QString::fromUtf8(line);
+
+	free(line);
+
+	return string;
+}
+
+void Repl::start()
+{
+	while (_running)
+	{
+		QString line = readLine();
+
+		line = line.trimmed();
+
+		QList<AstNode> expr;
+
+		if (trySpecialCase(line))
+		{}
+		else if (tryEvaluate(line, &expr))
+		{
+			bool okay = true;
+			QList<Token> out;
+
+			for (const AstNode &node : expr)
+			{
+				RuntimeResult res = _eval.evaluate(node, VarContext());
+
+				if (res.success())
+				{
+					out.append(res.result());
+				}
+				else
+				{
+					qDebug() << "Failed to evaluate" << node;
+					qDebug() << res.message();
+					okay = false;
+					break;
+				}
+			}
+
+			if (okay)
+			{
+				qDebug() << pprint(out);
+			}
+		}
+		else
+		{
+			qDebug() << "What?";
+		}
+	}
+}
+
+bool Repl::trySpecialCase(QString line)
+{
+	if (line.startsWith("."))
+	{
+		if (line == ".q" || line == ".quit")
+		{
+			_running = false;
+		}
+		else
+		{
+			qDebug().noquote() << "Unknown special command, try .help";
+		}
+
+		return true;
+	}
+
+	return false;
+}
+
+bool Repl::tryEvaluate(QString line, QList<AstNode> *expr)
+{
+	Parser parser(line);
+	Function func;
+
+	if (parser.parseFunctionDefinition(&func))
+	{
+		_eval.addFunction(func);
+		*expr = {};
+
+		return true;
+	}
+
+	*expr = parser.parseMany<AstNode>();
+	parser.skip();
+
+	return parser.atEnd();
+}