Add AssertionException, specialize pprint
diff --git a/Evaluator.cpp b/Evaluator.cpp
index 56685ec..9ce5bf7 100644
--- a/Evaluator.cpp
+++ b/Evaluator.cpp
@@ -287,8 +287,7 @@
 
 void rtError(QString brief, QString details)
 {
-    eout("Runtime Error: " + brief);
-    eout(details);
+    throw AssertionException(brief + "\n" + details);
 }
 
 void EvalQuitException::raise() const
@@ -326,3 +325,29 @@
 {
     return "StackOverflowException: at " + pprint(_failedAt);
 }
+
+AssertionException::AssertionException(QString message)
+    : QException()
+{
+    _message = message;
+}
+
+QString AssertionException::message() const
+{
+    return _message;
+}
+
+void AssertionException::raise() const
+{
+    throw *this;
+}
+
+AssertionException *AssertionException::clone() const
+{
+    return new AssertionException(*this);
+}
+
+AssertionException::operator QString() const
+{
+    return "AssertionException: " + _message;
+}
diff --git a/Evaluator.h b/Evaluator.h
index 47ea941..7034669 100644
--- a/Evaluator.h
+++ b/Evaluator.h
@@ -59,6 +59,23 @@
     AstNode _failedAt;
 };
 
+class AssertionException : public QException
+{
+public:
+    AssertionException(QString message = "");
+    AssertionException(const AssertionException &other) = default;
+
+    QString message() const;
+
+    void raise() const override;
+    AssertionException *clone() const override;
+
+    operator QString() const;
+
+private:
+    QString _message;
+};
+
 class Evaluator {
 public:
 	Evaluator();
diff --git a/PPrint.h b/PPrint.h
index 6298d6b..3ae9546 100644
--- a/PPrint.h
+++ b/PPrint.h
@@ -3,6 +3,8 @@
 #include <QString>
 #include <QList>
 
+#include <type_traits>
+
 #include "Token.h"
 #include "AstNode.h"
 #include "Parser.h"
@@ -16,6 +18,25 @@
 //template <>
 //QString pprint<AstNode>(AstNode val);
 
+template <typename T, typename std::enable_if_t<std::is_base_of<TokenBase<T>, T>::value>::value = true>
+QString pprint(QList<T> val)
+{
+    QString out;
+    int lastType = -1;
+
+    qInfo() << "pprint specialized";
+
+    for (const T &v : val)
+    {
+        if ((lastType != v.type() || v.type() != T::SYMBOL) && lastType != -1)
+            out += " ";
+
+        out += QString(v);
+    }
+
+    return out;
+}
+
 template <typename T>
 QString pprint(QList<T> val)
 {
diff --git a/StdLib.cpp b/StdLib.cpp
index 5d6bf6a..be91d59 100644
--- a/StdLib.cpp
+++ b/StdLib.cpp
@@ -1,6 +1,9 @@
 #include "StdLib.h"
 #include "PPrint.h"
 
+#include <QEventLoop>
+#include <QTimer>
+
 StdLib::StdLib()
 {
     _print.addNativeSentence("e.Expr", [](VarContext args)
@@ -16,10 +19,27 @@
         sout(pprint(expr));
 		return QList<Token>();
 	});
+
+    _sleep.addNativeSentence("s.Time", [](VarContext args)
+    {
+        Token time = args.singleVar("Time");
+
+        if (time.type() != Token::INTEGER)
+            rtError("Invalid argument", "First argument to <Time> must be an integer, got " + pprint(time));
+
+        QEventLoop loop;
+        QTimer timer;
+        timer.connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
+        timer.start(time.integer());
+        loop.exec();
+
+        return QList<Token>();
+    });
 }
 
 void StdLib::load(Evaluator &eval)
 {
 	eval.addFunction(_print);
 	eval.addFunction(_prout);
+    eval.addFunction(_sleep);
 }
diff --git a/StdLib.h b/StdLib.h
index 61fc2a6..0cc98a5 100644
--- a/StdLib.h
+++ b/StdLib.h
@@ -11,5 +11,6 @@
 
 private:
 	Function _print{"Print"},
-		_prout{"Prout"};
+        _prout{"Prout"},
+        _sleep{"Sleep"};
 };
diff --git a/ide/CellModel.cpp b/ide/CellModel.cpp
index 8271c87..0aa29bb 100644
--- a/ide/CellModel.cpp
+++ b/ide/CellModel.cpp
@@ -147,6 +147,11 @@
     addCell(Cell(code, result));
 }
 
+void CellModel::insertCellBefore(int index)
+{
+    insertRow(index);
+}
+
 void CellModel::announceCellChange(Cell *cell, int role)
 {
     // TODO: Optimize
diff --git a/ide/CellModel.h b/ide/CellModel.h
index 1f56056..428422a 100644
--- a/ide/CellModel.h
+++ b/ide/CellModel.h
@@ -44,6 +44,8 @@
     Q_INVOKABLE void addCell(const Cell &cell);
     Q_INVOKABLE void addCell(QString code, QString result);
 
+    Q_INVOKABLE void insertCellBefore(int index);
+
 private:
     Notebook *_notebook;
     void announceCellChange(Cell *cell, int role);
diff --git a/ide/NbRuntime.cpp b/ide/NbRuntime.cpp
index 8454597..35df413 100644
--- a/ide/NbRuntime.cpp
+++ b/ide/NbRuntime.cpp
@@ -2,10 +2,12 @@
 
 #include "NbRuntime.h"
 #include "../Parser.h"
+#include "../StdLib.h"
 
 NbRuntime::NbRuntime(QObject *parent)
     : QThread(parent)
 {
+    StdLib().load(_eval);
 }
 
 void NbRuntime::queueCell(Cell *cell)
@@ -118,5 +120,10 @@
             _running = nullptr;
             emit cellFinishedRunning(cell, RuntimeResult(ex));
         }
+        catch (AssertionException &ex)
+        {
+            _running = nullptr;
+            emit cellFinishedRunning(cell, RuntimeResult(ex));
+        }
     }
 }
diff --git a/ide/Notebook.cpp b/ide/Notebook.cpp
index 82bee7e..953e98b 100644
--- a/ide/Notebook.cpp
+++ b/ide/Notebook.cpp
@@ -2,6 +2,8 @@
 #include "CellModel.h"
 #include "../PPrint.h"
 
+// TODO: avoid potential race condition if Cell is deleted, pass by value with same UUID instead
+
 Notebook::Notebook(QObject *parent)
     : QObject(parent)
     , _cellModel(new CellModel(this))
diff --git a/ide/qml/main.qml b/ide/qml/main.qml
index bf99a8d..ef6134a 100644
--- a/ide/qml/main.qml
+++ b/ide/qml/main.qml
@@ -76,7 +76,7 @@
                     }
 
                     InsertRow {
-                        onInsertClicked: notebook.cellModel.insertRows(notebook.cellModel.index(0, 0), 1);
+                        onInsertClicked: notebook.cellModel.insertCellBefore(0)
                     }
                 }
 
@@ -86,7 +86,6 @@
                     required property var model
                     required property var index
                     required property var uuid
-                    required property int status
 
                     width: codeEditor.width - 5
 
@@ -98,7 +97,7 @@
 
                     onInsertBelowClicked: {
                         console.info(index);
-                        notebook.cellModel.insertRows(notebook.cellModel.index(index + 1, 0), 1);
+                        notebook.cellModel.insertCellBefore(index + 1);
                     }
 
                     onRunClicked: {