Add basic IDE UI
diff --git a/REFAL.pro b/REFAL.pro
new file mode 100644
index 0000000..b4fa9c5
--- /dev/null
+++ b/REFAL.pro
@@ -0,0 +1,41 @@
+QT += core
+
+CONFIG += c++14
+
+# You can make your code fail to compile if it uses deprecated APIs.
+# In order to do so, uncomment the following line.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
+
+SOURCES += \
+	AstNode.cpp \
+	Evaluator.cpp \
+	Function.cpp \
+	main.cpp \
+	Matcher.cpp \
+	Parser.cpp \
+	PPrint.cpp \
+	Repl.cpp \
+	StdLib.cpp \
+	Token.cpp \
+	VarContext.cpp
+
+HEADERS += \
+	AstNode.h \
+	Evaluator.h \
+	Function.h \
+	Matcher.h \
+	Parser.h \
+	PPrint.h \
+	Repl.h \
+	StdLib.h \
+	Token.h \
+	VarContext.h
+
+include(ide/IDE.pri)
+
+# Default rules for deployment.
+qnx: target.path = /tmp/$${TARGET}/bin
+else: unix:!android: target.path = /opt/$${TARGET}/bin
+!isEmpty(target.path): INSTALLS += target
+
+LIBS += -lreadline
diff --git a/ide/IDE.pri b/ide/IDE.pri
new file mode 100644
index 0000000..e430568
--- /dev/null
+++ b/ide/IDE.pri
@@ -0,0 +1,23 @@
+QT += quick quickcontrols2
+QT -= core
+
+CONFIG +=
+
+QML_IMPORT_NAME = sh.swisschili.REFAL
+QML_IMPORT_MAJOR_VERSION = 1
+QML_IMPORT_MINOR_VERSION = 0
+
+# Additional import path used to resolve QML modules in Qt Creator's code model
+QML_IMPORT_PATH =
+
+# Additional import path used to resolve QML modules just for Qt Quick Designer
+QML_DESIGNER_IMPORT_PATH =
+
+SOURCES += \
+    $$PWD/IdeMain.cpp
+
+HEADERS += \
+    $$PWD/IdeMain.h
+
+RESOURCES += \
+    $$PWD/resources.qrc
diff --git a/ide/IdeMain.cpp b/ide/IdeMain.cpp
new file mode 100644
index 0000000..a8f918c
--- /dev/null
+++ b/ide/IdeMain.cpp
@@ -0,0 +1,25 @@
+#include "IdeMain.h"
+
+#include <QQmlApplicationEngine>
+#include <QQuickStyle>
+
+int ideMain(QGuiApplication *app)
+{
+    QQmlApplicationEngine engine;
+
+    // This is done implicitly now.
+    // registerTypes(&engine);
+
+    QQuickStyle::setStyle("Material");
+
+    const QUrl url(QStringLiteral("qrc:/qml/main.qml"));
+    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
+                     app, [url](QObject *obj, const QUrl &objUrl)
+    {
+        if (!obj && url == objUrl)
+            QCoreApplication::exit(-1);
+    }, Qt::QueuedConnection);
+    engine.load(url);
+
+    return app->exec();
+}
diff --git a/ide/IdeMain.h b/ide/IdeMain.h
new file mode 100644
index 0000000..6b911ba
--- /dev/null
+++ b/ide/IdeMain.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include <QGuiApplication>
+
+using Application = QGuiApplication;
+
+int ideMain(QGuiApplication *app);
diff --git a/ide/icons/add.svg b/ide/icons/add.svg
new file mode 100644
index 0000000..00ffc63
--- /dev/null
+++ b/ide/icons/add.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M416 277.333H277.333V416h-42.666V277.333H96v-42.666h138.667V96h42.666v138.667H416v42.666z"/></svg>
\ No newline at end of file
diff --git a/ide/icons/play-circle.svg b/ide/icons/play-circle.svg
new file mode 100644
index 0000000..ff8963e
--- /dev/null
+++ b/ide/icons/play-circle.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 48C141.2 48 48 141.2 48 256s93.2 208 208 208 208-93.2 208-208S370.8 48 256 48zm-41.6 301.6V162.4L339.2 256l-124.8 93.6z"/></svg>
\ No newline at end of file
diff --git a/ide/qml/Cell.qml b/ide/qml/Cell.qml
new file mode 100644
index 0000000..d060dee
--- /dev/null
+++ b/ide/qml/Cell.qml
@@ -0,0 +1,44 @@
+import QtQuick 2.0
+import QtQuick.Controls 2.15
+import QtQuick.Controls.Material 2.0
+import QtQuick.Layouts 1.0
+
+ColumnLayout {
+    id: root
+
+    RowLayout {
+        Layout.fillWidth: true
+
+        Button {
+            Layout.alignment: Qt.AlignTop
+            icon.source: "qrc:///icons/play-circle.svg"
+            icon.color: Material.color(Material.Grey, Material.Shade600)
+            flat: true
+        }
+
+        ColumnLayout {
+            Layout.fillWidth: true
+            Layout.fillHeight: true
+
+            TextArea {
+                Layout.fillWidth: true
+                Layout.fillHeight: true
+                id: code
+                font.family: "monospace"
+                text: "Hello"
+                selectByMouse: true
+                wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
+            }
+
+            Label {
+                Layout.fillWidth: true
+                font.family: "monospace"
+                text: "Result\nasdfasdf\nasdad"
+            }
+        }
+    }
+
+    InsertRow {
+
+    }
+}
diff --git a/ide/qml/InsertRow.qml b/ide/qml/InsertRow.qml
new file mode 100644
index 0000000..b12896a
--- /dev/null
+++ b/ide/qml/InsertRow.qml
@@ -0,0 +1,62 @@
+import QtQuick 2.0
+import QtQuick.Controls 2.15
+import QtQuick.Controls.Material 2.0
+import QtQuick.Layouts 1.0
+
+Rectangle {
+    id: root
+
+    color: Material.color(Material.Grey, Material.Shade800)
+    height: 2
+    Layout.fillWidth: true
+    Layout.topMargin: 14
+    Layout.bottomMargin: 14
+
+    transitions: Transition {
+        NumberAnimation {
+            property: "opacity"
+            duration: 100
+            easing.type: Easing.OutCubic
+        }
+    }
+
+    MouseArea {
+        id: mouseArea
+        height: 30
+        width: parent.width
+        anchors.centerIn: parent
+        hoverEnabled: true
+
+        states: [
+            State {
+                when: mouseArea.containsMouse
+                PropertyChanges {
+                    target: root
+                    opacity: 1
+                }
+            },
+            State {
+                when: !mouseArea.containsMouse
+                PropertyChanges {
+                    target: root
+                    opacity: 0
+                }
+            }
+        ]
+    }
+
+    Item {
+        id: insertRow
+        anchors.centerIn: parent
+        height: 24
+        width: 24
+
+        Button {
+            id: addButton
+            anchors.centerIn: parent
+            icon.source: "qrc:///icons/add.svg"
+            icon.color: Material.color(Material.Grey, Material.Shade400)
+            flat: true
+        }
+    }
+}
diff --git a/ide/qml/main.qml b/ide/qml/main.qml
new file mode 100644
index 0000000..d3bca25
--- /dev/null
+++ b/ide/qml/main.qml
@@ -0,0 +1,67 @@
+import QtQuick 2.5
+import QtQuick.Controls 2.15
+import QtQuick.Controls.Material 2.0
+import QtQuick.Layouts 1.3
+
+ApplicationWindow {
+    id: root
+    width: 1080
+    height: 720
+    title: "Notebook"
+    visible: true
+
+    Material.theme: Material.Dark
+    Material.accent: Material.Orange
+
+    ColumnLayout {
+        id: column
+        anchors.fill: parent
+
+        TabBar {
+            id: bar
+
+            Layout.fillWidth: true
+
+            TabButton {
+                text: "Example Workspace"
+            }
+
+            TabButton {
+                text: "Another Workspace"
+            }
+
+            TabButton {
+                text: "Testing"
+            }
+        }
+
+        SplitView {
+            id: split
+            Layout.fillHeight: true
+            Layout.fillWidth: true
+            orientation: Qt.Horizontal
+
+            ListView {
+                id: codeEditor
+                SplitView.fillWidth: true
+                SplitView.minimumWidth: 400
+                model: 3
+                clip: true
+
+                delegate: Cell {
+                    width: codeEditor.width - 5
+                }
+            }
+
+            Item {
+                id: variables
+                SplitView.minimumWidth: 240
+
+                Label {
+                    anchors.centerIn: parent
+                    text: "Vars"
+                }
+            }
+        }
+    }
+}
diff --git a/ide/resources.qrc b/ide/resources.qrc
new file mode 100644
index 0000000..bab3643
--- /dev/null
+++ b/ide/resources.qrc
@@ -0,0 +1,9 @@
+<RCC>
+    <qresource prefix="/">
+        <file>qml/main.qml</file>
+        <file>qml/Cell.qml</file>
+        <file>icons/play-circle.svg</file>
+        <file>icons/add.svg</file>
+        <file>qml/InsertRow.qml</file>
+    </qresource>
+</RCC>
diff --git a/main.cpp b/main.cpp
index 7f1a3ad..32f8e6f 100644
--- a/main.cpp
+++ b/main.cpp
@@ -11,6 +11,12 @@
 #include "Repl.h"
 #include "PPrint.h"
 
+#ifdef NO_IDE
+using Application = QCoreApplication;
+#else
+#include "ide/IdeMain.h"
+#endif
+
 int g_numFailed = 0;
 
 
@@ -261,9 +267,9 @@
 
 int main(int argc, char *argv[])
 {
-    QCoreApplication a(argc, argv);
-	QCoreApplication::setApplicationName("REFAL");
-	QCoreApplication::setApplicationVersion("1.0-a1");
+    Application a(argc, argv);
+    a.setApplicationName("REFAL");
+    a.setApplicationVersion("1.0-a1");
 
 	QCommandLineParser cli;
 	cli.setApplicationDescription("REFAL interpreter");
@@ -274,6 +280,8 @@
 
 	QCommandLineOption testOption(QStringList{"t", "test"}, "Run test suite.");
 	cli.addOption(testOption);
+    QCommandLineOption replOption(QStringList({"r", "repl"}), "Start CLI REPL");
+    cli.addOption(replOption);
 
 	cli.process(a);
 
@@ -295,9 +303,13 @@
 
 		return testResults();
 	}
-	else
+    else if (cli.isSet(replOption))
 	{
 		Repl repl;
 		repl.start();
 	}
+    else
+    {
+        return ideMain(&a);
+    }
 }