Fwd: [GSoC 2016] Julia backend for Cantor

Иван Лахтанов ivan.lakhtanov at gmail.com
Mon Mar 21 09:28:37 UTC 2016


Hello!

I've messed git diff master..vaness-julia-backend with git diff
vaness-julia-backend..master. Attaching fixed patch. You maybe noticed that
there is some workaround on detecting end of the command output (print
"julia>"), Julia detects that it is communicating through program and
doesn't show prompt. I'm now thinking  on how to overcome this or make more
reliable workaround.

I haven't submitted proposal draft yet -- planing to do it today.

2016-03-20 22:55 GMT+03:00 Filipe Saraiva <filipe at kde.org>:

> Em 18-03-2016 17:40, Иван Лахтанов escreveu:
>
> Hello, KDE-edu team!
>
> I want to participate Google Summer of Code this year. My friend
> recommended me to apply for KDE projects and I am a big fun of Qt and KDE.
>
> I looked through the ideas list and choosen "Julia backend for Cantor
> <https://community.kde.org/GSoC/2016/Ideas#Project:_Backend_for_Julia>".
> I am student of Moscow State University Mechanics and Mathematics
> department and have experience on porting Julia math library to Python 2
> during one of my courseworks.
>
> I have already developed executing Julia commands and attaching the
> current patch with this email. If you expect me to do more as preliminary
> task tell me and I will try to hurry until proposal deadline.
>
>
> Hello Ivan, thank you for the patch, looks like you did an interesting
> work.
> But your patch is reverting/removing the files and contributions of you
> did.
>
> Please could you fix the patch? Did You submit a project to GSoC?
>
> Best regards;
>
> --
> Filipe Saraivahttp://filipesaraiva.info/
>
>
> _______________________________________________
> kde-edu mailing list
> kde-edu at mail.kde.org
> https://mail.kde.org/mailman/listinfo/kde-edu
>
>


-- 
Best regards, Ivan Lakhtanov
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.kde.org/pipermail/kde-edu/attachments/20160321/bc2f1638/attachment-0003.html>
-------------- next part --------------
diff --git a/src/backends/CMakeLists.txt b/src/backends/CMakeLists.txt
index 4987b37..fa14700 100644
--- a/src/backends/CMakeLists.txt
+++ b/src/backends/CMakeLists.txt
@@ -15,6 +15,9 @@ add_subdirectory(maxima)
 add_subdirectory(octave)
 add_subdirectory(scilab)
 
+# FIXME: wrap this properly
+add_subdirectory(julia)
+
 if(NOT WIN32)
     add_subdirectory(sage)
 endif(NOT WIN32)
diff --git a/src/backends/julia/CMakeLists.txt b/src/backends/julia/CMakeLists.txt
new file mode 100644
index 0000000..3036f40
--- /dev/null
+++ b/src/backends/julia/CMakeLists.txt
@@ -0,0 +1,8 @@
+set( JuliaBackend_SRCS
+ juliabackend.cpp
+ juliasession.cpp
+ juliaexpression.cpp
+ juliacompletionobject.cpp
+)
+
+add_backend(juliabackend ${JuliaBackend_SRCS})
diff --git a/src/backends/julia/juliabackend.cpp b/src/backends/julia/juliabackend.cpp
new file mode 100644
index 0000000..31b586d
--- /dev/null
+++ b/src/backends/julia/juliabackend.cpp
@@ -0,0 +1,61 @@
+/*
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License
+    as published by the Free Software Foundation; either version 2
+    of the License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA  02110-1301, USA.
+
+    ---
+    Copyright (C) 2009 Alexander Rieder <alexanderrieder at gmail.com>
+    Copyright (C) 2016 Ivan Lakhtanov <ivan.lakhtanov at gmail.com>
+ */
+
+#include "juliabackend.h"
+
+#include <QDebug>
+
+#include "cantor_macros.h"
+#include "juliasession.h"
+
+JuliaBackend::JuliaBackend(QObject *parent, const QList<QVariant> args)
+    : Cantor::Backend(parent, args)
+{
+    setObjectName(QLatin1String("juliabackend"));
+    qDebug() << "Creating JuliaBackend";
+    setEnabled(true);
+}
+
+JuliaBackend::~JuliaBackend()
+{
+    qDebug() << "Destroying JuliaBackend";
+}
+
+QString JuliaBackend::id() const
+{
+    return QLatin1String("julia");
+}
+
+Cantor::Session *JuliaBackend::createSession()
+{
+    qDebug() << "Spawning a new Julia session";
+
+    return new JuliaSession(this);
+}
+
+Cantor::Backend::Capabilities JuliaBackend::capabilities() const
+{
+    qDebug() << "Requesting capabilities of JuliaSession";
+    return Cantor::Backend::Nothing;
+}
+
+K_PLUGIN_FACTORY_WITH_JSON(juliabackend, "juliabackend.json", registerPlugin<JuliaBackend>();)
+#include "juliabackend.moc"
diff --git a/src/backends/julia/juliabackend.h b/src/backends/julia/juliabackend.h
new file mode 100644
index 0000000..9322263
--- /dev/null
+++ b/src/backends/julia/juliabackend.h
@@ -0,0 +1,41 @@
+/*
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License
+    as published by the Free Software Foundation; either version 2
+    of the License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA  02110-1301, USA.
+
+    ---
+    Copyright (C) 2009 Alexander Rieder <alexanderrieder at gmail.com>
+    Copyright (C) 2016 Ivan Lakhtanov <ivan.lakhtanov at gmail.com>
+ */
+
+#ifndef _JULIABACKEND_H
+#define _JULIABACKEND_H
+
+#include "backend.h"
+
+class JuliaBackend: public Cantor::Backend
+{
+  Q_OBJECT
+
+public:
+    explicit JuliaBackend(QObject *parent = 0, const QList<QVariant> args = QList<QVariant>());
+    ~JuliaBackend();
+
+    QString id() const;
+
+    Cantor::Session *createSession();
+    Cantor::Backend::Capabilities capabilities() const;
+};
+
+#endif /* _JULIABACKEND_H */
diff --git a/src/backends/julia/juliabackend.json b/src/backends/julia/juliabackend.json
new file mode 100644
index 0000000..1aa221b
--- /dev/null
+++ b/src/backends/julia/juliabackend.json
@@ -0,0 +1,12 @@
+{
+    "KPlugin": {
+        "Dependencies": [],
+        "Description": "Julia backend for Cantor",
+        "Id": "juliabackend",
+        "License": "GPL",
+        "Name": "juliabackend",
+        "ServiceTypes": [
+            "Cantor/Backend"
+        ]
+    }
+}
diff --git a/src/backends/julia/juliacompletionobject.cpp b/src/backends/julia/juliacompletionobject.cpp
new file mode 100644
index 0000000..25e3c5d
--- /dev/null
+++ b/src/backends/julia/juliacompletionobject.cpp
@@ -0,0 +1,48 @@
+/*
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License
+    as published by the Free Software Foundation; either version 2
+    of the License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA  02110-1301, USA.
+
+    ---
+    Copyright (C) 2009 Alexander Rieder <alexanderrieder at gmail.com>
+    Copyright (C) 2016 Ivan Lakhtanov <ivan.lakhtanov at gmail.com>
+ */
+
+#include "juliacompletionobject.h"
+
+#include <QStringList>
+#include <QDebug>
+
+#include "juliasession.h"
+
+JuliaCompletionObject::JuliaCompletionObject(const QString &command, int index, JuliaSession *session)
+    : Cantor::CompletionObject(session)
+{
+    setLine(command, index);
+}
+
+JuliaCompletionObject::~JuliaCompletionObject()
+{
+
+}
+
+void JuliaCompletionObject::fetchCompletions()
+{
+    qDebug() << "fetching...";
+    QStringList comp;
+    for (int i = 0; i < 5; i++)
+        comp << QString::fromLatin1("%1 %2").arg(command()).arg(i);
+    setCompletions(comp);
+    emit fetchingDone();
+}
diff --git a/src/backends/julia/juliacompletionobject.h b/src/backends/julia/juliacompletionobject.h
new file mode 100644
index 0000000..fc80aed
--- /dev/null
+++ b/src/backends/julia/juliacompletionobject.h
@@ -0,0 +1,40 @@
+/*
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License
+    as published by the Free Software Foundation; either version 2
+    of the License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA  02110-1301, USA.
+
+    ---
+    Copyright (C) 2009 Alexander Rieder <alexanderrieder at gmail.com>
+    Copyright (C) 2016 Ivan Lakhtanov <ivan.lakhtanov at gmail.com>
+ */
+
+#ifndef _JULIACOMPLETIONOBJECT_H
+#define _JULIACOMPLETIONOBJECT_H
+
+#include "completionobject.h"
+
+class JuliaSession;
+
+class JuliaCompletionObject: public Cantor::CompletionObject
+{
+  public:
+    JuliaCompletionObject(const QString &command, int index, JuliaSession *session);
+    ~JuliaCompletionObject();
+
+  protected Q_SLOTS:
+    void fetchCompletions();
+
+};
+
+#endif /* _JULIACOMPLETIONOBJECT_H */
diff --git a/src/backends/julia/juliaexpression.cpp b/src/backends/julia/juliaexpression.cpp
new file mode 100644
index 0000000..ec05ce2
--- /dev/null
+++ b/src/backends/julia/juliaexpression.cpp
@@ -0,0 +1,90 @@
+/*
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License
+    as published by the Free Software Foundation; either version 2
+    of the License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA  02110-1301, USA.
+
+    ---
+    Copyright (C) 2009 Alexander Rieder <alexanderrieder at gmail.com>
+    Copyright (C) 2016 Ivan Lakhtanov <ivan.lakhtanov at gmail.com>
+ */
+
+#include "juliaexpression.h"
+
+#include <QDebug>
+#include <KIconLoader>
+#include <QTimer>
+
+#include "textresult.h"
+#include "imageresult.h"
+#include "helpresult.h"
+
+#include "juliasession.h"
+
+JuliaExpression::JuliaExpression(Cantor::Session *session)
+    : Cantor::Expression(session)
+{
+    qDebug() << "JuliaExpression constructor";
+    m_error = false;
+}
+
+JuliaExpression::~JuliaExpression()
+{
+}
+
+
+void JuliaExpression::evaluate()
+{
+    qDebug() << "evaluating " << command();
+
+    QString cmd = command();
+    m_finished = false;
+    setStatus(Cantor::Expression::Computing);
+    JuliaSession *juliasession = dynamic_cast<JuliaSession *>(session());
+    if (juliasession) {
+        juliasession->runExpression(this);
+    }
+}
+
+void JuliaExpression::interrupt()
+{
+    qDebug() << "interrupting command";
+    setStatus(Cantor::Expression::Interrupted);
+}
+
+void JuliaExpression::parseOutput(QString output)
+{
+    qDebug() << "parseOutput: " << output;
+    m_resultString += output;
+    if (!m_resultString.trimmed().isEmpty()) {
+        setResult(new Cantor::TextResult(m_resultString));
+    }
+}
+
+void JuliaExpression::parseError(QString error)
+{
+    qDebug() << "parseError: " << error;
+    m_error = true;
+    setErrorMessage(error);
+    setStatus(Error);
+}
+
+void JuliaExpression::finalize()
+{
+    qDebug() << "finalize: " << m_resultString;
+    m_finished = true;
+    setStatus(m_error ? Error : Done);
+}
+
+
+#include "juliaexpression.moc"
diff --git a/src/backends/julia/juliaexpression.h b/src/backends/julia/juliaexpression.h
new file mode 100644
index 0000000..d93eeb9
--- /dev/null
+++ b/src/backends/julia/juliaexpression.h
@@ -0,0 +1,51 @@
+/*
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License
+    as published by the Free Software Foundation; either version 2
+    of the License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA  02110-1301, USA.
+
+    ---
+    Copyright (C) 2009 Alexander Rieder <alexanderrieder at gmail.com>
+    Copyright (C) 2016 Ivan Lakhtanov <ivan.lakhtanov at gmail.com>
+ */
+
+#ifndef _JULIAEXPRESSION_H
+#define _JULIAEXPRESSION_H
+
+#include "expression.h"
+
+class JuliaExpression: public Cantor::Expression
+{
+    Q_OBJECT
+public:
+    JuliaExpression(Cantor::Session *session);
+    ~JuliaExpression();
+
+    void evaluate();
+    void interrupt();
+
+    void parseOutput(QString output);
+    void parseError(QString error);
+
+    void finalize();
+
+private:
+    QString m_resultString;
+    int m_numberOfLines;
+    bool m_plotPending;
+    bool m_finished;
+    bool m_error;
+    QStringList m_plotCommands;
+};
+
+#endif /* _JULIAEXPRESSION_H */
diff --git a/src/backends/julia/juliasession.cpp b/src/backends/julia/juliasession.cpp
new file mode 100644
index 0000000..bd33e57
--- /dev/null
+++ b/src/backends/julia/juliasession.cpp
@@ -0,0 +1,199 @@
+/*
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License
+    as published by the Free Software Foundation; either version 2
+    of the License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA  02110-1301, USA.
+
+    ---
+    Copyright (C) 2009 Alexander Rieder <alexanderrieder at gmail.com>
+    Copyright (C) 2016 Ivan Lakhtanov <ivan.lakhtanov at gmail.com>
+ */
+
+#include "juliasession.h"
+
+#include <QDebug>
+#include <KProcess>
+#include <signal.h>
+
+#include "juliaexpression.h"
+#include "juliacompletionobject.h"
+
+JuliaSession::JuliaSession(Cantor::Backend *backend)
+    : Session(backend)
+    , m_process(0)
+    , m_currentExpression(0)
+{
+    qDebug();
+
+    m_prompt.setPattern(QLatin1String("julia>"));
+}
+
+JuliaSession::~JuliaSession()
+{
+    qDebug();
+}
+
+void JuliaSession::login()
+{
+    qDebug() << "login";
+
+    QStringList args;
+    args << QLatin1String("--quiet"); // No banner on startup
+    args << QLatin1String("-i"); // Force interactive mode
+    args << QLatin1String("--color=no"); // No color
+    args << QLatin1String("--no-history-file"); // No history
+    args << QLatin1String("--no-startup"); // No startup scripts
+    m_process = new KProcess(this);
+    m_process->setProgram(QLatin1String("/usr/bin/julia"), args);
+    qDebug() << m_process->program();
+    m_process->setOutputChannelMode(KProcess::SeparateChannels);
+    connect(m_process, SIGNAL(readyReadStandardOutput()), SLOT(readOutput()));
+    connect(m_process, SIGNAL(readyReadStandardError()), SLOT(readError()));
+    connect(m_process, SIGNAL(error(QProcess::ProcessError)), SLOT(processError()));
+    m_process->start();
+
+    changeStatus(Cantor::Session::Done);
+    emit ready();
+}
+
+void JuliaSession::logout()
+{
+    qDebug() << "logout";
+    m_process->write("exit(0)\n");
+    if (!m_process->waitForFinished(1000)) {
+        m_process->kill();
+    }
+}
+
+void JuliaSession::interrupt()
+{
+    qDebug() << "interrupt";
+    if (m_currentExpression) {
+        m_currentExpression->interrupt();
+    }
+    m_expressionQueue.clear();
+    qDebug() << "Sending SINGINT to Julia";
+    kill(m_process->pid(), SIGINT);
+    changeStatus(Cantor::Session::Done);
+}
+
+Cantor::Expression *JuliaSession::evaluateExpression(const QString &cmd, Cantor::Expression::FinishingBehavior behave)
+{
+    qDebug() << "evaluateExpression: " << cmd;
+
+    JuliaExpression *expression = new JuliaExpression(this);
+    expression->setCommand(cmd);
+    expression->setFinishingBehavior(behave);
+    expression->evaluate();
+
+    return expression;
+}
+
+Cantor::CompletionObject *JuliaSession::completionFor(const QString &command, int index)
+{
+    qDebug() << "tab completion for " << command;
+    return new JuliaCompletionObject(command, index, this);
+}
+
+void JuliaSession::processError()
+{
+    qDebug() << "processError";
+    emit error(m_process->errorString());
+}
+
+void JuliaSession::readError()
+{
+    qDebug() << "readError";
+    QString error = QString::fromLocal8Bit(m_process->readAllStandardError());
+    if (!m_currentExpression || error.isEmpty()) {
+        qDebug() << error;
+        return;
+    }
+    m_currentExpression->parseError(error);
+}
+
+void JuliaSession::readOutput()
+{
+    qDebug() << "readOutput";
+    while (m_process->bytesAvailable() > 0) {
+        if (!m_process->canReadLine()) {
+            qDebug() << "Waiting";
+            return;
+        }
+
+        QString line = QString::fromLocal8Bit(m_process->readLine());
+        qDebug() << line;
+        if (!m_currentExpression) {
+            changeStatus(Done);
+            if (!m_expressionQueue.isEmpty()) {
+                runExpression(m_expressionQueue.dequeue());
+            }
+            emit ready();
+        } else if (line.contains(m_prompt)) {
+            readError();
+            m_currentExpression->finalize();
+        } else {
+            while (m_process->canReadLine()) {
+                line += QString::fromLocal8Bit(m_process->readLine());
+            }
+            m_currentExpression->parseOutput(line);
+        }
+    }
+}
+
+void JuliaSession::runExpression(JuliaExpression *expression)
+{
+    qDebug() << "runExpression";
+    if (status() != Done) {
+        m_expressionQueue.enqueue(expression);
+        qDebug() << m_expressionQueue.size();
+    } else {
+        m_currentExpression = expression;
+        changeStatus(Running);
+        connect(
+            m_currentExpression,
+            SIGNAL(statusChanged(Cantor::Expression::Status)),
+            this,
+            SLOT(currentExpressionStatusChanged(Cantor::Expression::Status))
+        );
+        QString command = expression->command();
+        command.replace(QLatin1Char('\n'), QLatin1Char(';'));
+        command = QLatin1String("println(eval(:(") +
+                  command +
+                  QLatin1String(")));println(\"julia>\");\n");
+        m_process->write(command.toLocal8Bit());
+    }
+}
+
+void JuliaSession::currentExpressionStatusChanged(Cantor::Expression::Status status)
+{
+    qDebug() << "currentExpressionStatusChanged";
+    if (!m_currentExpression) {
+        return;
+    }
+    switch (status) {
+    case Cantor::Expression::Computing:
+        break;
+    case Cantor::Expression::Interrupted:
+        break;
+    case Cantor::Expression::Done:
+    case Cantor::Expression::Error:
+        changeStatus(Done);
+        if (!m_expressionQueue.isEmpty()) {
+            runExpression(m_expressionQueue.dequeue());
+        }
+        break;
+    }
+}
+
+#include "juliasession.moc"
diff --git a/src/backends/julia/juliasession.h b/src/backends/julia/juliasession.h
new file mode 100644
index 0000000..3a5f6ad
--- /dev/null
+++ b/src/backends/julia/juliasession.h
@@ -0,0 +1,63 @@
+/*
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License
+    as published by the Free Software Foundation; either version 2
+    of the License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA  02110-1301, USA.
+
+    ---
+    Copyright (C) 2009 Alexander Rieder <alexanderrieder at gmail.com>
+    Copyright (C) 2016 Ivan Lakhtanov <ivan.lakhtanov at gmail.com>
+ */
+
+#ifndef _JULIASESSION_H
+#define _JULIASESSION_H
+
+#include <QPointer>
+#include <QQueue>
+
+#include "session.h"
+
+class JuliaExpression;
+class KProcess;
+
+class JuliaSession: public Cantor::Session
+{
+    Q_OBJECT
+public:
+    JuliaSession(Cantor::Backend *backend);
+    ~JuliaSession();
+
+    virtual void login();
+    virtual void logout();
+
+    void interrupt();
+
+    Cantor::Expression *evaluateExpression(const QString &command, Cantor::Expression::FinishingBehavior behave);
+    Cantor::CompletionObject *completionFor(const QString &cmd, int index = -1);
+
+    void runExpression(JuliaExpression *expression);
+
+private Q_SLOTS:
+    void readOutput();
+    void readError();
+    void currentExpressionStatusChanged(Cantor::Expression::Status status);
+    void processError();
+
+private:
+    QQueue<JuliaExpression *> m_expressionQueue;
+    QPointer<JuliaExpression> m_currentExpression;
+    KProcess *m_process;
+    QRegExp m_prompt;
+};
+
+#endif /* _JULIASESSION_H */


More information about the kde-edu mailing list