diff --git a/src/backends/lua/luaexpression.h b/src/backends/lua/luaexpression.h --- a/src/backends/lua/luaexpression.h +++ b/src/backends/lua/luaexpression.h @@ -30,17 +30,13 @@ Q_OBJECT public: - LuaExpression( Cantor::Session* session, lua_State* L); + LuaExpression( Cantor::Session* session); ~LuaExpression(); void evaluate(); void interrupt(); + void parseOutput(QString& output); -private: - // evaluates an expression, executing it on the Lua state and building an adequate response - void execute(QString& ret, Cantor::Expression::Status& status); - - lua_State* m_L; }; #endif /* _LUAEXPRESSION_H */ diff --git a/src/backends/lua/luaexpression.cpp b/src/backends/lua/luaexpression.cpp --- a/src/backends/lua/luaexpression.cpp +++ b/src/backends/lua/luaexpression.cpp @@ -32,8 +32,8 @@ #include #include -LuaExpression::LuaExpression( Cantor::Session* session, lua_State* L) - : Cantor::Expression(session), m_L(L) +LuaExpression::LuaExpression( Cantor::Session* session) + : Cantor::Expression(session) { } @@ -43,59 +43,41 @@ void LuaExpression::evaluate() { - QString ret; - Cantor::Expression::Status status; - execute(ret, status); - - if(status == Cantor::Expression::Done) - { - QString cmd = command().simplified(); - - if( cmd.startsWith(QLatin1String("show(")) || cmd.startsWith(QLatin1String("show (")) ) - setResult(new Cantor::ImageResult(QUrl::fromLocalFile(ret), ret)); - else - setResult(new Cantor::TextResult(ret)); - } - else - { - setErrorMessage(ret); + /* + * start evaluating the current expression + * set the status to computing + * decide what needs to be done if the user is trying to define a function etc + */ + setStatus(Cantor::Expression::Computing); + if (command().isEmpty()) { + setStatus(Cantor::Expression::Done); + return; } - setStatus(status); -} + LuaSession* currentSession = dynamic_cast(session()); + currentSession->runExpression(this); -void LuaExpression::interrupt() -{ - setStatus(Cantor::Expression::Interrupted); } -void LuaExpression::execute(QString& ret, Cantor::Expression::Status& status) +void LuaExpression::parseOutput(QString &output) { - int top = lua_gettop(m_L); + output.replace(command(), QLatin1String("")); + output.replace(QLatin1String("return"), QLatin1String("")); + output.replace(QLatin1String(">"), QLatin1String("")); + output = output.trimmed(); - // execute the command - QString err = luahelper_dostring(m_L, QLatin1String("return ") + command() ); // try to return values... - if( !err.isNull() ) err = luahelper_dostring(m_L, command() ); // try the original expression + qDebug() << "final output of the command " << command() << ": " << output << endl; - if( err.isNull() ) - { - QStringList list; - int n_out = lua_gettop(m_L) - top; + setResult(new Cantor::TextResult(output)); - for(int i = -n_out; i < 0; ++i) - list << luahelper_tostring(m_L, i); + setStatus(Cantor::Expression::Done); - ret = list.join(QLatin1String("\n")) + luahelper_getprinted(m_L); - status = Cantor::Expression::Done; - } - else - { - qDebug() << "error when executing" << command() << ":" << err; - ret = err; - status = Cantor::Expression::Error; - } +} - lua_settop(m_L, top); +void LuaExpression::interrupt() +{ + setStatus(Cantor::Expression::Interrupted); } + #include "luaexpression.moc" diff --git a/src/backends/lua/luasession.h b/src/backends/lua/luasession.h --- a/src/backends/lua/luasession.h +++ b/src/backends/lua/luasession.h @@ -25,6 +25,7 @@ #include class LuaExpression; +class QProcess; class LuaSession : public Cantor::Session { @@ -38,16 +39,27 @@ void interrupt(); + void runExpression(LuaExpression* currentExpression); + Cantor::Expression* evaluateExpression(const QString& command, Cantor::Expression::FinishingBehavior behave); Cantor::CompletionObject* completionFor(const QString& cmd, int index=-1); virtual QSyntaxHighlighter* syntaxHighlighter(QObject* parent); lua_State* getState() const; +public Q_SLOTS: + void readIntroMessage(); + void readOutput(); + void readError(); + void processStarted(); + private Q_SLOTS: - void expressionFinished(); + void expressionFinished(Cantor::Expression::Status status); private: lua_State* m_L; + QProcess* m_process; + LuaExpression* m_currentExpression; + QString m_output; }; #endif /* _LUASESSION_H */ diff --git a/src/backends/lua/luasession.cpp b/src/backends/lua/luasession.cpp --- a/src/backends/lua/luasession.cpp +++ b/src/backends/lua/luasession.cpp @@ -26,7 +26,12 @@ #include #include "ui_settings.h" -LuaSession::LuaSession( Cantor::Backend* backend) : Session(backend) +#include + +LuaSession::LuaSession( Cantor::Backend* backend) : + Session(backend), + m_process(0), + m_currentExpression(0) { } @@ -36,45 +41,81 @@ void LuaSession::login() { + /* + * setup Qprocess here + * load the autoscripts + */ + + m_process = new QProcess(this); + m_process->setProgram(QLatin1String("/usr/bin/lua")); + m_process->setArguments(QStringList() << QLatin1String("-i")); + + m_process->setProcessChannelMode(QProcess::SeparateChannels); + + connect(m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(readIntroMessage())); + connect(m_process, SIGNAL(started()), this, SLOT(processStarted())); + m_process->start(); + + // we need this for tab completion m_L = luaL_newstate(); luaL_openlibs(m_L); +} - QStringList errors; - errors << luahelper_dostring(m_L, QLatin1String("__cantor = {}")); +void LuaSession::readIntroMessage() +{ + while(m_process->bytesAvailable()) { + m_output.append(QString::fromLocal8Bit(m_process->readLine())); + } - errors << luahelper_dostring(m_L, - QLatin1String("function print(...)\n" - "local t = {}\n" - "for i = 1, select('#',...) do\n" - "local a = select(i,...)\n" - "t[i] = tostring(a)\n" - "end\n" - "table.insert(__cantor, table.concat(t,'\t'))\n" - " end")); + if(!m_output.isEmpty() && m_output.trimmed().endsWith(QLatin1String(">"))) { + qDebug() << " reading the intro message " << m_output ; + m_output.clear(); - errors << luahelper_dostring(m_L, - QLatin1String("function show(a)\n" - "assert(type(a) == 'string')\n" - "return a\n" - "end")); + disconnect(m_process, SIGNAL(readyReadStandardOutput()), this , SLOT(readIntroMessage())); + connect(m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(readOutput())); + connect(m_process, SIGNAL(readyReadStandardError()), this, SLOT(readError())); + } - if(!errors.empty()) - qDebug() << errors.join(QLatin1String("\n")); - foreach (const QString &str, LuaSettings::self()->autorunScripts()) - evaluateExpression(QLatin1String("dofile('") + str + QLatin1String("')"), Cantor::Expression::DeleteOnFinish); +} - changeStatus(Cantor::Session::Done); +void LuaSession::readOutput() +{ + /* + * parse the output + * clear all the garbage + * set it as output + */ + // keep reading till the output ends with '>'. + // '>' marks the end of output for a particular command; + while(m_process->bytesAvailable()) { + m_output.append(QString::fromLocal8Bit(m_process->readLine())); + } + if(m_currentExpression && !m_output.isEmpty() && m_output.trimmed().endsWith(QLatin1String(">"))) { + // we have our complete output + // clean the output and parse it and clear m_output; + m_currentExpression->parseOutput(m_output); + m_output.clear(); + + } + +} + +void LuaSession::readError() +{ + qDebug() << m_process->readAllStandardError() << endl; +} + +void LuaSession::processStarted() +{ + qDebug() << m_process->program() << " pid " << m_process->processId() << " started " << endl; emit ready(); } void LuaSession::logout() { - if(m_L) - { - lua_close(m_L); - m_L = 0; - } + if(m_process) + m_process->kill(); } void LuaSession::interrupt() @@ -87,24 +128,48 @@ { changeStatus(Cantor::Session::Running); - LuaExpression* expr = new LuaExpression(this, m_L); - connect(expr, &LuaExpression::statusChanged, this, &LuaSession::expressionFinished); - expr->setFinishingBehavior(behave); - expr->setCommand(cmd); - expr->evaluate(); + m_currentExpression = new LuaExpression(this); + connect(m_currentExpression, SIGNAL(statusChanged(Cantor::Expression::Status)), this, SLOT(expressionFinished(Cantor::Expression::Status))); + m_currentExpression->setFinishingBehavior(behave); + m_currentExpression->setCommand(cmd); + m_currentExpression->evaluate(); + + return m_currentExpression; +} + +void LuaSession::runExpression(LuaExpression *currentExpression) +{ + /* + * get the current command + * format it and write to m_process + */ + QString command = currentExpression->command(); + + command += QLatin1String("\n"); + + qDebug() << "final command to be executed " << command << endl; - return expr; + m_process->write(command.toLocal8Bit()); } Cantor::CompletionObject* LuaSession::completionFor(const QString& command, int index) { return new LuaCompletionObject(command, index, this); } -void LuaSession::expressionFinished() +void LuaSession::expressionFinished(Cantor::Expression::Status status) { - // synchronous - changeStatus(Cantor::Session::Done); + switch(status) { + + case Cantor::Expression::Computing: + break; + + case Cantor::Expression::Done: + case Cantor::Expression::Error: + case Cantor::Expression::Interrupted: + changeStatus(Cantor::Session::Done); + break; + } } QSyntaxHighlighter* LuaSession::syntaxHighlighter(QObject* parent)