Add evaluator
diff --git a/ide/Cell.cpp b/ide/Cell.cpp
index 2fd964b..c49466d 100644
--- a/ide/Cell.cpp
+++ b/ide/Cell.cpp
@@ -1,8 +1,15 @@
#include "Cell.h"
+QHash<QUuid, Cell *> Cell::_cellUuids = QHash<QUuid, Cell*>();
+
+Cell::~Cell()
+{
+ _cellUuids.remove(_uuid);
+}
+
Cell::Cell(QObject *parent) : QObject(parent)
{
-
+ _cellUuids[_uuid] = this;
}
Cell::Cell(const Cell ©, QObject *parent)
@@ -36,6 +43,16 @@
return _result;
}
+QUuid Cell::uuid() const
+{
+ return _uuid;
+}
+
+int Cell::status() const
+{
+ return _status;
+}
+
void Cell::setCode(QString code)
{
_code = code;
@@ -47,3 +64,17 @@
_result = result;
emit resultChanged(result);
}
+
+void Cell::setStatus(int status)
+{
+ _status = status;
+ emit statusChanged(status);
+}
+
+Cell *Cell::cellFromUuid(QUuid uuid)
+{
+ if (_cellUuids.contains(uuid))
+ return _cellUuids[uuid];
+ else
+ return nullptr;
+}
diff --git a/ide/Cell.h b/ide/Cell.h
index e3d1f9d..fb5bc42 100644
--- a/ide/Cell.h
+++ b/ide/Cell.h
@@ -2,6 +2,8 @@
#include <QObject>
#include <qqml.h>
+#include <QUuid>
+#include <QHash>
class Cell : public QObject
{
@@ -10,8 +12,11 @@
Q_PROPERTY(QString code READ code WRITE setCode NOTIFY codeChanged)
Q_PROPERTY(QString result READ result WRITE setResult NOTIFY resultChanged)
+ Q_PROPERTY(QUuid uuid READ uuid NOTIFY uuidChanged)
+ Q_PROPERTY(int status READ status WRITE setStatus NOTIFY statusChanged)
public:
+ ~Cell();
explicit Cell(QObject *parent = nullptr);
Cell(const Cell ©, QObject *parent = nullptr);
Cell(QString code, QString result, QObject *parent = nullptr);
@@ -20,16 +25,36 @@
QString code() const;
QString result() const;
+ QUuid uuid() const;
+ int status() const;
void setCode(QString code);
void setResult(QString result);
+ void setStatus(int status);
+
+ Q_INVOKABLE static Cell *cellFromUuid(QUuid uuid);
+
+ enum Status
+ {
+ RUNNING,
+ IDLE,
+ WAITING,
+ };
+
+ Q_ENUM(Status);
signals:
void codeChanged(QString code);
void resultChanged(QString result);
+ void uuidChanged(QUuid uuid);
+ void statusChanged(int status);
private:
+ int _status = IDLE;
QString _code, _result;
+ QUuid _uuid = QUuid::createUuid();
+
+ static QHash<QUuid, Cell *> _cellUuids;
};
Q_DECLARE_METATYPE(Cell)
diff --git a/ide/CellModel.cpp b/ide/CellModel.cpp
index 41acbe7..8271c87 100644
--- a/ide/CellModel.cpp
+++ b/ide/CellModel.cpp
@@ -29,9 +29,13 @@
switch (role)
{
case CodeRole:
- return _notebook->_cells[index.row()].code();
+ return _notebook->_cells[index.row()]->code();
case ResultRole:
- return _notebook->_cells[index.row()].result();
+ return _notebook->_cells[index.row()]->result();
+ case UuidRole:
+ return _notebook->_cells[index.row()]->uuid();
+ case StatusRole:
+ return _notebook->_cells[index.row()]->status();
default:
return QVariant();
}
@@ -44,10 +48,13 @@
switch (role)
{
case CodeRole:
- _notebook->_cells[index.row()].setCode(value.toString());
+ _notebook->_cells[index.row()]->setCode(value.toString());
break;
case ResultRole:
- _notebook->_cells[index.row()].setResult(value.toString());
+ _notebook->_cells[index.row()]->setResult(value.toString());
+ break;
+ case StatusRole:
+ _notebook->_cells[index.row()]->setStatus(value.toInt());
break;
default:
return false;
@@ -66,7 +73,7 @@
if (!index.isValid())
return Qt::NoItemFlags;
- return Qt::ItemIsEditable; // FIXME: Implement me!
+ return Qt::ItemIsEditable;
}
bool CellModel::insertRows(int row, int count, const QModelIndex &parent)
@@ -74,7 +81,26 @@
beginInsertRows(parent, row, row + count - 1);
for (int i = 0; i < count; i++)
- _notebook->_cells.insert(row, Cell());
+ {
+ Cell *cell = new Cell(this);
+
+ connect(cell, &Cell::codeChanged, this, [this, cell](QString)
+ {
+ announceCellChange(cell, CodeRole);
+ });
+
+ connect(cell, &Cell::resultChanged, this, [this, cell](QString)
+ {
+ announceCellChange(cell, ResultRole);
+ });
+
+ connect(cell, &Cell::statusChanged, this, [this, cell](int)
+ {
+ announceCellChange(cell, StatusRole);
+ });
+
+ _notebook->_cells.insert(row, cell);
+ }
endInsertRows();
@@ -86,7 +112,10 @@
beginRemoveRows(parent, row, row + count - 1);
for (int i = 0; i < count; i++)
+ {
+ delete _notebook->_cells[row];
_notebook->_cells.removeAt(row);
+ }
endRemoveRows();
@@ -98,6 +127,8 @@
return {
{CodeRole, "code"},
{ResultRole, "result"},
+ {UuidRole, "uuid"},
+ {StatusRole, "status"},
};
}
@@ -107,7 +138,7 @@
insertRows(i, 1, QModelIndex());
- _notebook->_cells[i] = cell;
+ *_notebook->_cells[i] = cell;
emit dataChanged(index(i), index(i), {CodeRole, ResultRole});
}
@@ -115,3 +146,17 @@
{
addCell(Cell(code, result));
}
+
+void CellModel::announceCellChange(Cell *cell, int role)
+{
+ // TODO: Optimize
+
+ for (int i = 0; i < rowCount(); i++)
+ {
+ if (_notebook->_cells[i] == cell)
+ {
+ emit dataChanged(index(i), index(i), QVector<int>() << role);
+ break;
+ }
+ }
+}
diff --git a/ide/CellModel.h b/ide/CellModel.h
index 16bfee5..1f56056 100644
--- a/ide/CellModel.h
+++ b/ide/CellModel.h
@@ -17,7 +17,9 @@
enum CellRoles
{
CodeRole = Qt::UserRole + 1,
- ResultRole
+ ResultRole,
+ UuidRole,
+ StatusRole,
};
// Basic functionality:
@@ -44,6 +46,7 @@
private:
Notebook *_notebook;
+ void announceCellChange(Cell *cell, int role);
};
Q_DECLARE_METATYPE(CellModel)
diff --git a/ide/IDE.pri b/ide/IDE.pri
index f656fd1..0341a0b 100644
--- a/ide/IDE.pri
+++ b/ide/IDE.pri
@@ -17,12 +17,14 @@
$$PWD/Cell.cpp \
$$PWD/CellModel.cpp \
$$PWD/IdeMain.cpp \
+ $$PWD/NbRuntime.cpp \
$$PWD/Notebook.cpp
HEADERS += \
$$PWD/Cell.h \
$$PWD/CellModel.h \
$$PWD/IdeMain.h \
+ $$PWD/NbRuntime.h \
$$PWD/Notebook.h
RESOURCES += \
diff --git a/ide/NbRuntime.cpp b/ide/NbRuntime.cpp
new file mode 100644
index 0000000..8454597
--- /dev/null
+++ b/ide/NbRuntime.cpp
@@ -0,0 +1,122 @@
+#include <QCoreApplication>
+
+#include "NbRuntime.h"
+#include "../Parser.h"
+
+NbRuntime::NbRuntime(QObject *parent)
+ : QThread(parent)
+{
+}
+
+void NbRuntime::queueCell(Cell *cell)
+{
+ if (!_cells.contains(cell))
+ {
+ qInfo() << "Queueing cell";
+
+ _cells.append(cell);
+
+ emit cellWaiting(cell);
+
+ if (!_running)
+ evalRemaining();
+ }
+}
+
+void NbRuntime::unqueueCell(Cell *cell)
+{
+ if (cell == _running)
+ {
+ // Exception should propagate back up to evalRemaining()
+ _eval.quit();
+ }
+ else
+ {
+ _cells.removeOne(cell);
+ }
+}
+
+void NbRuntime::evalRemaining()
+{
+ qInfo() << "evalRemaining";
+
+ while (!_cells.empty())
+ {
+ QCoreApplication::processEvents();
+
+ Cell *cell = _cells.first();
+ _cells.removeFirst();
+
+ _running = cell;
+
+ Parser parser(cell->code());
+ RuntimeResult result;
+
+ emit cellRunning(cell);
+
+ try
+ {
+ // Allow this cell to be quit
+ QCoreApplication::processEvents();
+
+ while (true)
+ {
+ ParseResult ret;
+ Function func;
+ AstNode ast;
+
+ if ((ret = parser.parseFunctionDefinition(&func)))
+ {
+ _eval.addFunction(func);
+ }
+ else if (ret.status() == ParseResult::INCOMPLETE)
+ {
+ emit cellFailedToParse(cell, ret);
+ goto endOfCell; // JANK!
+ }
+ else if ((ret = parser.parseOne(&ast)))
+ {
+ RuntimeResult nodeRes = _eval.evaluate(ast, _ctx);
+ result += nodeRes;
+
+ if (!nodeRes.success())
+ {
+ break;
+ }
+ }
+ else if (ret.status() == ParseResult::INCOMPLETE)
+ {
+ emit cellFailedToParse(cell, ret);
+ break;
+ }
+ else
+ {
+ parser.skip();
+
+ if (!parser.atEnd())
+ {
+ emit cellFailedToParse(cell, ParseResult(ParseResult::NO_MATCH, "Garbage at end of input", parser.save()));
+ goto endOfCell;
+ }
+
+ break;
+ }
+ }
+
+ emit cellFinishedRunning(cell, result);
+
+ endOfCell:
+ _running = nullptr;
+ }
+ catch (EvalQuitException &ex)
+ {
+ _running = nullptr;
+ emit cellQuit(cell);
+ }
+ catch (StackOverflowException &ex)
+ {
+ _running = nullptr;
+ emit cellFinishedRunning(cell, RuntimeResult(ex));
+ }
+ }
+}
diff --git a/ide/NbRuntime.h b/ide/NbRuntime.h
new file mode 100644
index 0000000..2f6a845
--- /dev/null
+++ b/ide/NbRuntime.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#include <QObject>
+#include <QThread>
+#include <QQueue>
+
+#include "Cell.h"
+#include "../Token.h"
+#include "../Evaluator.h"
+#include "../Parser.h"
+
+class NbRuntime : public QThread
+{
+ Q_OBJECT
+
+public:
+ explicit NbRuntime(QObject *parent = nullptr);
+
+public slots:
+ void queueCell(Cell *cell);
+ void unqueueCell(Cell *cell);
+
+signals:
+ void cellFinishedRunning(Cell *cell, RuntimeResult result);
+ void cellFailedToParse(Cell *cell, ParseResult result);
+ void cellWaiting(Cell *cell);
+ void cellRunning(Cell *cell);
+ void cellQuit(Cell *cell);
+
+protected:
+ void evalRemaining();
+
+ Evaluator _eval;
+ QQueue<Cell *> _cells;
+ Cell *_running = nullptr;
+ VarContext _ctx;
+};
diff --git a/ide/Notebook.cpp b/ide/Notebook.cpp
index 9beb332..82bee7e 100644
--- a/ide/Notebook.cpp
+++ b/ide/Notebook.cpp
@@ -1,21 +1,74 @@
#include "Notebook.h"
#include "CellModel.h"
+#include "../PPrint.h"
Notebook::Notebook(QObject *parent)
: QObject(parent)
, _cellModel(new CellModel(this))
{
+ connect(&_rt, &NbRuntime::cellFailedToParse, this, &Notebook::cellFailedToParse);
+ connect(&_rt, &NbRuntime::cellFinishedRunning, this, &Notebook::cellFinishedRunning);
+ connect(&_rt, &NbRuntime::cellQuit, this, &Notebook::cellQuit);
+ connect(&_rt, &NbRuntime::cellRunning, this, &Notebook::cellRunning);
+ connect(&_rt, &NbRuntime::cellWaiting, this, &Notebook::cellWaiting);
+ _rt.start();
}
Notebook::Notebook(const Notebook &other, QObject *parent)
- : QObject(parent)
- , _cells(other._cells)
- , _cellModel(new CellModel(this))
+ : Notebook(parent)
{
+ for (const Cell *cell : other._cells)
+ {
+ _cells.append(new Cell(*cell, this));
+ }
}
CellModel *Notebook::cellModel()
{
return _cellModel;
}
+
+void Notebook::runCell(QUuid uuid)
+{
+ qInfo() << "Running cell" << uuid;
+ _rt.queueCell(Cell::cellFromUuid(uuid));
+}
+
+void Notebook::quitCell(QUuid uuid)
+{
+ _rt.unqueueCell(Cell::cellFromUuid(uuid));
+}
+
+void Notebook::cellFinishedRunning(Cell *cell, RuntimeResult result)
+{
+ qInfo() << "cellFinishedRunning" << cell->uuid() << pprint(result);
+ cell->setResult(pprint(result));
+ cell->setStatus(Cell::IDLE);
+}
+
+void Notebook::cellFailedToParse(Cell *cell, ParseResult result)
+{
+ qInfo() << "cellFailedToParse" << cell->uuid() << pprint(result);
+ cell->setResult(pprint(result));
+ cell->setStatus(Cell::IDLE);
+}
+
+void Notebook::cellWaiting(Cell *cell)
+{
+ qInfo() << "cellWaiting" << cell->uuid();
+ cell->setStatus(Cell::WAITING);
+}
+
+void Notebook::cellRunning(Cell *cell)
+{
+ qInfo() << "cellRunning" << cell->uuid();
+ cell->setStatus(Cell::RUNNING);
+}
+
+void Notebook::cellQuit(Cell *cell)
+{
+ qInfo() << "cellQuit" << cell->uuid();
+ cell->setResult("");
+ cell->setStatus(Cell::IDLE);
+}
diff --git a/ide/Notebook.h b/ide/Notebook.h
index 0bd84ec..867c2e9 100644
--- a/ide/Notebook.h
+++ b/ide/Notebook.h
@@ -3,6 +3,7 @@
#include <QObject>
#include "Cell.h"
+#include "NbRuntime.h"
class CellModel;
@@ -19,14 +20,25 @@
CellModel *cellModel();
+ Q_INVOKABLE void runCell(QUuid uuid);
+ Q_INVOKABLE void quitCell(QUuid uuid);
+
signals:
void cellModelChanged();
+protected slots:
+ void cellFinishedRunning(Cell *cell, RuntimeResult result);
+ void cellFailedToParse(Cell *cell, ParseResult result);
+ void cellWaiting(Cell *cell);
+ void cellRunning(Cell *cell);
+ void cellQuit(Cell *cell);
+
protected:
friend class CellModel;
- QList<Cell> _cells;
+ QList<Cell *> _cells;
CellModel *_cellModel;
+ NbRuntime _rt;
};
Q_DECLARE_METATYPE(Notebook)
diff --git a/ide/icons/square.svg b/ide/icons/square.svg
new file mode 100644
index 0000000..3a4587b
--- /dev/null
+++ b/ide/icons/square.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M405.333 64H106.667C83.198 64 64 83.198 64 106.667v298.666C64 428.802 83.198 448 106.667 448h298.666C428.802 448 448 428.802 448 405.333V106.667C448 83.198 428.802 64 405.333 64z"/></svg>
\ No newline at end of file
diff --git a/ide/qml/DocumentPadding.qml b/ide/qml/DocumentPadding.qml
new file mode 100644
index 0000000..3c47413
--- /dev/null
+++ b/ide/qml/DocumentPadding.qml
@@ -0,0 +1,9 @@
+import QtQuick 2.0
+import QtQuick.Layouts 1.3
+
+ColumnLayout {
+ Layout.topMargin: 10
+ Layout.bottomMargin: 10
+ Layout.leftMargin: 12
+ Layout.rightMargin: 12
+}
diff --git a/ide/qml/NotebookCell.qml b/ide/qml/NotebookCell.qml
index 3466d05..f51ffaa 100644
--- a/ide/qml/NotebookCell.qml
+++ b/ide/qml/NotebookCell.qml
@@ -3,15 +3,19 @@
import QtQuick.Controls.Material 2.0
import QtQuick.Layouts 1.0
+import sh.swisschili.REFAL 1.0
+
Item {
id: root
required property string code
required property string result
+ property int status: Cell.IDLE
signal insertBelowClicked()
signal codeEditingFinished(string code)
signal cellFocused()
+ signal runClicked()
height: column.height
@@ -34,9 +38,18 @@
RoundButton {
Layout.alignment: Qt.AlignTop
- icon.source: "qrc:///icons/play-circle.svg"
+ icon.source: iconForState(root.state)
icon.color: Material.color(Material.Grey, Material.Shade600)
flat: true
+
+ onClicked: root.runClicked()
+
+ function iconForState(state) {
+ if (state === Cell.RUNNING)
+ return "qrc:///icons/square.svg"
+
+ return "qrc:///icons/play-circle.svg"
+ }
}
ColumnLayout {
diff --git a/ide/qml/main.qml b/ide/qml/main.qml
index a28d47d..bf99a8d 100644
--- a/ide/qml/main.qml
+++ b/ide/qml/main.qml
@@ -63,22 +63,47 @@
model: notebook.cellModel
clip: true
+ header: ColumnLayout {
+ width: codeEditor.width
+
+ DocumentPadding {
+ Layout.bottomMargin: 0
+
+ Label {
+ font.pointSize: 18
+ text: "Notebook"
+ }
+ }
+
+ InsertRow {
+ onInsertClicked: notebook.cellModel.insertRows(notebook.cellModel.index(0, 0), 1);
+ }
+ }
+
delegate: NotebookCell {
id: notebookCell
required property var model
required property var index
+ required property var uuid
+ required property int status
width: codeEditor.width - 5
code: model.code
- result: model.result
+ result: model.result.trim()
+ status: model.status
onCodeEditingFinished: model.code = code
onInsertBelowClicked: {
- console.info(index)
- cellModel.insertRows(cellModel.index(0, index), 1)
+ console.info(index);
+ notebook.cellModel.insertRows(notebook.cellModel.index(index + 1, 0), 1);
+ }
+
+ onRunClicked: {
+ console.info("Cell run clicked")
+ notebook.runCell(uuid)
}
}
}
diff --git a/ide/resources.qrc b/ide/resources.qrc
index a77a977..23ecdd0 100644
--- a/ide/resources.qrc
+++ b/ide/resources.qrc
@@ -6,5 +6,7 @@
<file>icons/add.svg</file>
<file>qml/InsertRow.qml</file>
<file>icons/trash.svg</file>
+ <file>qml/DocumentPadding.qml</file>
+ <file>icons/square.svg</file>
</qresource>
</RCC>