Commit 8b0049c8 authored by Alexander Semke's avatar Alexander Semke
Browse files

[maxima] properly handle the switch to the Lisp mode.

In Maxima it is possible to use Lisp interpreter directly, the switch into this mode is done via to_lisp() call in Maxima. In this case the promt is different and we need to properly handle it. The result of the evaluations are also put at a different place in the output. Also, in the Lisp mode we don't need to update the variable model of Maxima since it's a different environment.
parent f6293729
Pipeline #141196 passed with stage
in 5 minutes and 34 seconds
......@@ -266,7 +266,7 @@ void MaximaExpression::parseOutput(const QString& out)
qDebug() << "error content: " << errorContent;
if (out.contains(QLatin1String("cantor-value-separator"))
|| (out.contains(QLatin1String("<cantor-result>")) && !(m_isHelpRequest || m_isHelpRequestAdditional) ) )
|| (out.contains(QLatin1String("<cantor-result>")) && !(m_isHelpRequest || m_isHelpRequestAdditional)) )
{
//we don't interpret the error output as an error in the following cases:
//1. when fetching variables, in addition to the actual result with variable names and values,
......@@ -275,6 +275,28 @@ void MaximaExpression::parseOutput(const QString& out)
// contains actually a warning that is handled above
setStatus(Cantor::Expression::Done);
}
else if (prompt.trimmed() == QLatin1String("MAXIMA>") )
{
//prompt is "MAXIMA>", i.e. we're switching to the Lisp-mode triggered by to_lisp(). The output in this case is:
// "Type (to-maxima) to restart, ($quit) to quit Maxima.\n<cantor-prompt>\nMAXIMA> </cantor-prompt>\n"
//Or we're already in the Lisp mode and just need to show the result of the lisp evaluation.
if (static_cast<MaximaSession*>(session())->mode() != MaximaSession::Lisp)
static_cast<MaximaSession*>(session())->setMode(MaximaSession::Lisp);
auto* result = new Cantor::TextResult(errorContent.trimmed());
setResult(result);
qDebug()<<"setting status to DONE";
setStatus(Cantor::Expression::Done);
}
else if (prompt.trimmed() != QLatin1String("MAXIMA>") && static_cast<MaximaSession*>(session())->mode() == MaximaSession::Lisp)
{
//"Returning to Maxima:
//output: "Returning to Maxima\n<cantor-result><cantor-text>\n(%o1) true\n</cantor-text></cantor-result>\n<cantor-prompt>(%i2) </cantor-prompt>\n"
static_cast<MaximaSession*>(session())->setMode(MaximaSession::Maxima);
auto* result = new Cantor::TextResult(errorContent.trimmed());
addResult(result);
setStatus(Cantor::Expression::Done);
}
else if(m_isHelpRequest || m_isHelpRequestAdditional) //help messages are also part of the error output
{
//we've got help result, but maybe additional input is required -> check this
......@@ -288,7 +310,7 @@ void MaximaExpression::parseOutput(const QString& out)
//set the help result
errorContent.prepend(QLatin1Char(' '));
Cantor::HelpResult* result = new Cantor::HelpResult(errorContent);
auto* result = new Cantor::HelpResult(errorContent);
setResult(result);
//if a new input prompt was found, no further input is expected and we're done
......
......@@ -115,6 +115,15 @@ void MaximaSession::logout()
qDebug()<<"logout done";
}
MaximaSession::Mode MaximaSession::mode() const {
return m_mode;
}
void MaximaSession::setMode(MaximaSession::Mode mode)
{
m_mode = mode;
}
Cantor::Expression* MaximaSession::evaluateExpression(const QString& cmd, Cantor::Expression::FinishingBehavior behave, bool internal)
{
qDebug() << "evaluating: " << cmd;
......@@ -186,12 +195,9 @@ void MaximaSession::currentExpressionChangedStatus(Cantor::Expression::Status st
case Cantor::Expression::Done:
case Cantor::Expression::Error:
qDebug()<<"################################## EXPRESSION END ###############################################";
disconnect(expression, SIGNAL(statusChanged(Cantor::Expression::Status)),
this, SLOT(currentExpressionChangedStatus(Cantor::Expression::Status)));
disconnect(expression, &Cantor::Expression::statusChanged, this, &MaximaSession::currentExpressionChangedStatus);
finishFirstExpression();
break;
default:
break;
}
......@@ -206,8 +212,8 @@ void MaximaSession::runFirstExpression()
if(!expressionQueue().isEmpty())
{
auto* expr = expressionQueue().first();
QString command = expr->internalCommand();
connect(expr, SIGNAL(statusChanged(Cantor::Expression::Status)), this, SLOT(currentExpressionChangedStatus(Cantor::Expression::Status)));
const auto& command = expr->internalCommand();
connect(expr, &Cantor::Expression::statusChanged, this, &MaximaSession::currentExpressionChangedStatus);
expr->setStatus(Cantor::Expression::Computing);
if(command.isEmpty())
......@@ -231,7 +237,7 @@ void MaximaSession::interrupt()
if(m_process && m_process->state() != QProcess::NotRunning)
{
#ifndef Q_OS_WIN
const int pid=m_process->processId();
const int pid = m_process->processId();
kill(pid, SIGINT);
#else
; //TODO: interrupt the process on windows
......
......@@ -21,6 +21,8 @@ class MaximaSession : public Cantor::Session
static const QRegularExpression MaximaOutputPrompt;
static const QRegularExpression MaximaInputPrompt;
enum Mode {Maxima, Lisp};
explicit MaximaSession(Cantor::Backend*);
void login() override;
......@@ -38,6 +40,9 @@ class MaximaSession : public Cantor::Session
QSyntaxHighlighter* syntaxHighlighter(QObject*) override;
void runFirstExpression() override;
Mode mode() const;
void setMode(Mode);
public Q_SLOTS:
void readStdOut();
void readStdErr();
......@@ -54,6 +59,7 @@ class MaximaSession : public Cantor::Session
QProcess* m_process{nullptr};
QString m_cache;
bool m_justRestarted{false};
Mode m_mode{Maxima};
};
#endif /* _MAXIMASESSION_H */
......@@ -19,18 +19,19 @@
const QString MaximaVariableModel::inspectCommand=QLatin1String(":lisp($disp $%1)");
const QString MaximaVariableModel::variableInspectCommand=QLatin1String(":lisp(cantor-inspect $%1)");
MaximaVariableModel::MaximaVariableModel( MaximaSession* session) : Cantor::DefaultVariableModel(session),
m_variableExpression(nullptr),
m_functionExpression(nullptr)
MaximaVariableModel::MaximaVariableModel(MaximaSession* session) : Cantor::DefaultVariableModel(session)
{
}
void MaximaVariableModel::update()
{
if (static_cast<MaximaSession*>(session())->mode() != MaximaSession::Maxima)
return;
if (!m_variableExpression)
{
qDebug()<<"checking for new variables";
const QString& cmd1=variableInspectCommand.arg(QLatin1String("values"));
const QString& cmd1 = variableInspectCommand.arg(QLatin1String("values"));
m_variableExpression = static_cast<MaximaExpression*>(session()->evaluateExpression(cmd1, Cantor::Expression::FinishingBehavior::DoNotDelete, true));
connect(m_variableExpression, &Cantor::Expression::statusChanged, this, &MaximaVariableModel::parseNewVariables);
}
......@@ -38,7 +39,7 @@ void MaximaVariableModel::update()
if (!m_functionExpression)
{
qDebug()<<"checking for new functions";
const QString& cmd2=inspectCommand.arg(QLatin1String("functions"));
const QString& cmd2 = inspectCommand.arg(QLatin1String("functions"));
m_functionExpression = static_cast<MaximaExpression*>(session()->evaluateExpression(cmd2, Cantor::Expression::FinishingBehavior::DoNotDelete, true));
connect(m_functionExpression, &Cantor::Expression::statusChanged, this, &MaximaVariableModel::parseNewFunctions);
}
......@@ -63,8 +64,8 @@ QList<Cantor::DefaultVariableModel::Variable> parse(MaximaExpression* expr)
text += static_cast<Cantor::LatexResult*>(result)->plain();
}
const int nameIndex=text.indexOf(QLatin1Char(']'));
QString namesString=text.left(nameIndex);
const int nameIndex = text.indexOf(QLatin1Char(']'));
QString namesString = text.left(nameIndex);
//namesString.chop(1);
namesString=namesString.mid(1);
namesString=namesString.trimmed();
......
......@@ -19,21 +19,21 @@ class MaximaVariableModel : public Cantor::DefaultVariableModel
static const QString inspectCommand;
static const QString variableInspectCommand;
explicit MaximaVariableModel( MaximaSession* session);
explicit MaximaVariableModel(MaximaSession*);
~MaximaVariableModel() override = default;
void update() override;
private Q_SLOTS:
void parseNewVariables(Cantor::Expression::Status status);
void parseNewFunctions(Cantor::Expression::Status status);
void parseNewVariables(Cantor::Expression::Status);
void parseNewFunctions(Cantor::Expression::Status);
private:
MaximaSession* maximaSession();
private:
MaximaExpression* m_variableExpression;
MaximaExpression* m_functionExpression;
MaximaExpression* m_variableExpression{nullptr};
MaximaExpression* m_functionExpression{nullptr};
};
#endif /* _MAXIMAVARIABLEMODEL_H */
......@@ -19,8 +19,6 @@
#include <config-cantorlib.h>
#include <QDebug>
QString TestMaxima::backendName()
{
return QLatin1String("maxima");
......@@ -327,6 +325,31 @@ void TestMaxima::testVariableModel()
QCOMPARE(value2.toString(),QLatin1String("[1,2,3]"));
}
void TestMaxima::testLispMode01()
{
//switch to the Lisp-mode
auto* e1 = evalExp(QLatin1String("to_lisp();"));
QVERIFY(e1 != nullptr);
//evaluate a Lisp command and check the result
auto* e2 = evalExp(QLatin1String("(cons 'a 'b)"));
QVERIFY(e2 != nullptr);
QVERIFY(e2->result() != nullptr);
QCOMPARE(cleanOutput(e2->result()->data().toString()), QLatin1String("(A . B)"));
//switch back to Maxima mode
auto* e3 = evalExp(QLatin1String("(to-maxima)"));
QVERIFY(e3 != nullptr);
//evaluate a simple Maxima command
auto* e4 = evalExp(QLatin1String("5+5"));
QVERIFY(e4 != nullptr);
//TODO: doesn't work in the test, works in Cantor though...
// QVERIFY(e4->result() != nullptr);
// QCOMPARE(cleanOutput(e4->result()->data().toString()), QLatin1String("10"));
}
void TestMaxima::testLoginLogout()
{
// Logout from session twice and all must works fine
......
......@@ -54,8 +54,11 @@ private Q_SLOTS:
void testVariableModel();
void testLispMode01();
void testLoginLogout();
void testRestartWhileRunning();
private:
QString backendName() override;
};
......
......@@ -165,10 +165,13 @@ void Session::finishFirstExpression(bool setDoneAfterUpdate)
d->needUpdate = false;
// Some variable models could update internal lists without running expressions
// or don't need to be updated at all like for Maxima being in Lisp-mode.
// So, if after update queue still empty, set status to Done
// setDoneAfterUpdate used for compatibility with some backends, like R
// setDoneAfterUpdate used for compatibility with some backends, like R - TODO: check why this is requried
if (setDoneAfterUpdate && d->expressionQueue.isEmpty())
changeStatus(Done);
else if (d->expressionQueue.isEmpty())
changeStatus(Done);
}
else
changeStatus(Done);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment