Add evaluator
diff --git a/Evaluator.cpp b/Evaluator.cpp
index 5177fe5..56685ec 100644
--- a/Evaluator.cpp
+++ b/Evaluator.cpp
@@ -5,6 +5,7 @@
 #include "PPrint.h"
 
 #include <QDebug>
+#include <QCoreApplication>
 
 RuntimeResult::RuntimeResult(QList<Token> result)
 {
@@ -14,7 +15,41 @@
 RuntimeResult::RuntimeResult(QString message)
 {
 	_errorMessage = message;
-	_success = false;
+    _success = false;
+}
+
+RuntimeResult &RuntimeResult::operator =(const RuntimeResult &other)
+{
+    _errorMessage = other._errorMessage;
+    _success = other._success;
+    _result = other._result;
+
+    return *this;
+}
+
+RuntimeResult RuntimeResult::operator +(const RuntimeResult &other)
+{
+    RuntimeResult res;
+
+    if (_success)
+    {
+        res._success = other._success;
+        res._result = _result;
+        res._result.append(other._result);
+        res._errorMessage = other._errorMessage;
+    }
+    else
+    {
+        res = *this;
+    }
+
+    return res;
+}
+
+RuntimeResult &RuntimeResult::operator +=(const RuntimeResult &other)
+{
+    *this = *this + other;
+    return *this;
 }
 
 bool RuntimeResult::success() const
@@ -34,7 +69,7 @@
 
 RuntimeResult::operator QString() const
 {
-	return QString(_success) + " " + _errorMessage;
+    return _errorMessage + pprint(_result);
 }
 
 Evaluator::Evaluator()
@@ -73,6 +108,19 @@
         return copy(name.name());
     });
     addFunction(copyFn);
+
+    Function undefFn("Undef");
+    undefFn.addNativeSentence("s.Name", [this](VarContext args)
+    {
+        Token name = args.singleVar("Name");
+        if (name.type() != Token::IDENT)
+            rtError("Invalid argument", "First argument to <Undef> must be an identifier, received " + pprint(name));
+
+        clearFunction(name.name());
+
+        return QList<Token>();
+    });
+    addFunction(undefFn);
 }
 
 void Evaluator::addFunction(Function func)
@@ -85,8 +133,13 @@
 	_functions.remove(name);
 }
 
-RuntimeResult Evaluator::evaluate(AstNode node, VarContext ctx)
+RuntimeResult Evaluator::evaluate(AstNode node, VarContext ctx, int recursionDepth)
 {
+    if (recursionDepth > _recursionLimit)
+    {
+        throw StackOverflowException(node);
+    }
+
 	if (node.isSym())
 	{
 		return RuntimeResult(QList<Token>{Token(node.symbol())});
@@ -138,20 +191,20 @@
 
 		for (const AstNode &arg : node.funcArgs())
 		{
-			RuntimeResult internalResult = evaluate(arg, ctx);
+            RuntimeResult internalResult = evaluate(arg, ctx, recursionDepth + 1);
 			if (!internalResult.success())
 				return internalResult;
 
 			args.append(internalResult.result());
 		}
 
-		return callFunction(node.name(), args);
+        return callFunction(node.name(), args, recursionDepth + 1);
 	}
 
 	return RuntimeResult("#TYPE_ERROR");
 }
 
-RuntimeResult Evaluator::callFunction(QString name, QList<Token> args)
+RuntimeResult Evaluator::callFunction(QString name, QList<Token> args, int recursionDepth)
 {
 	if (!_functions.contains(name))
 		return RuntimeResult("Function " + name + " is not defined.");
@@ -173,7 +226,7 @@
 		QList<Token> final;
 		for (const AstNode &node : sentence.result())
 		{
-			RuntimeResult argRes = evaluate(node, res.context);
+            RuntimeResult argRes = evaluate(node, res.context, recursionDepth);
 			if (!argRes.success())
 				return argRes;
 
@@ -186,6 +239,11 @@
     return RuntimeResult("Function " + name + " had no matching sentences for input");
 }
 
+void Evaluator::quit()
+{
+    throw EvalQuitException();
+}
+
 QList<Token> Evaluator::dig(QString name)
 {
     if (_vars.contains(name))
@@ -232,3 +290,39 @@
     eout("Runtime Error: " + brief);
     eout(details);
 }
+
+void EvalQuitException::raise() const
+{
+    throw *this;
+}
+
+EvalQuitException *EvalQuitException::clone() const
+{
+    return new EvalQuitException(*this);
+}
+
+StackOverflowException::StackOverflowException(AstNode failedAt)
+    : QException()
+{
+    _failedAt = failedAt;
+}
+
+AstNode StackOverflowException::failedAt() const
+{
+    return _failedAt;
+}
+
+void StackOverflowException::raise() const
+{
+    throw *this;
+}
+
+StackOverflowException *StackOverflowException::clone() const
+{
+    return new StackOverflowException(*this);
+}
+
+StackOverflowException::operator QString() const
+{
+    return "StackOverflowException: at " + pprint(_failedAt);
+}