1 #include "yacasengine.h"
2 
3 #include <string>
4 
5 #include <QtCore/QFile>
6 #include <QtCore/QMutexLocker>
7 #include <QtCore/QSet>
8 
YacasEngine(const QString & scripts_path,YacasRequestQueue & requests,QObject * parent)9 YacasEngine::YacasEngine(const QString& scripts_path,
10                          YacasRequestQueue& requests,
11                          QObject* parent) :
12     QObject(parent),
13     _requests(requests),
14     _yacas(new CYacas(_side_effects)),
15     _idx(1)
16 {
17     if (!QFile(scripts_path + "yacasinit.ys").exists())
18         throw std::runtime_error(QString("Invalid yacas scripts path: %1")
19                                      .arg(scripts_path)
20                                      .toStdString());
21 
22     _yacas->Evaluate(std::string("DefaultDirectory(\"") +
23                      scripts_path.toStdString() + std::string("\");"));
24     _yacas->Evaluate("Load(\"yacasinit.ys\");");
25 
26     _yacas->Evaluate("Plot2D'outputs();");
27     _yacas->Evaluate("UnProtect(Plot2D'outputs);");
28     _yacas->Evaluate("Plot2D'yagy(values_IsList, _options'hash) <-- "
29                      "Yagy'Plot2D'Data(values, options'hash);");
30     _yacas->Evaluate(
31         "Plot2D'outputs() := { {\"default\", \"yagy\"}, {\"data\", "
32         "\"Plot2D'data\"}, {\"gnuplot\", \"Plot2D'gnuplot\"}, {\"java\", "
33         "\"Plot2D'java\"}, {\"yagy\", \"Plot2D'yagy\"}, };");
34     _yacas->Evaluate("Protect(Plot2D'outputs);");
35     _yacas->Evaluate("Plot3DS'outputs();");
36     _yacas->Evaluate("UnProtect(Plot3DS'outputs);");
37     _yacas->Evaluate("Plot3DS'yagy(values_IsList, _options'hash) <-- "
38                      "Yagy'Plot3DS'Data(values, options'hash);");
39     _yacas->Evaluate("Plot3DS'outputs() := { {\"default\", \"yagy\"}, "
40                      "{\"data\", \"Plot3DS'data\"}, {\"gnuplot\", "
41                      "\"Plot3DS'gnuplot\"}, {\"yagy\", \"Plot3DS'yagy\"},};");
42     _yacas->Evaluate("Protect(Plot3DS'outputs);");
43 
44     _update_symbols();
45 }
46 
~YacasEngine()47 YacasEngine::~YacasEngine()
48 {
49     delete _yacas;
50 }
51 
cancel()52 void YacasEngine::cancel()
53 {
54     _yacas->getDefEnv().getEnv().stop_evaluation = true;
55 }
56 
symbols() const57 QStringList YacasEngine::symbols() const
58 {
59     QMutexLocker lock(&_symbols_mtx);
60     return _symbols;
61 }
62 
on_start_processing()63 void YacasEngine::on_start_processing()
64 {
65     for (;;) {
66 
67         QMutexLocker lock(&_requests.mtx);
68 
69         if (_requests.shutdown)
70             return;
71 
72         busy(false);
73 
74         _requests.cnd.wait(&_requests.mtx);
75 
76         if (_requests.shutdown)
77             return;
78 
79         _yacas->getDefEnv().getEnv().stop_evaluation = false;
80 
81         busy(true);
82 
83         while (!_requests.waiting.empty()) {
84             YacasRequest* request = _requests.waiting.dequeue();
85 
86             // Beware of low flying butterflies
87             _requests.mtx.unlock();
88 
89             const QString expr = request->take();
90 
91             _side_effects.clear();
92             _side_effects.str("");
93             _yacas->Evaluate((expr + ";").toStdString());
94 
95             _update_symbols();
96 
97             if (!_yacas->IsError()) {
98                 QString result = QString::fromStdString(_yacas->Result());
99                 result = result.left(result.length() - 1).trimmed();
100 
101                 YacasRequest::ResultType result_type = YacasRequest::EXPRESSION;
102                 if (result.startsWith("Yagy'Plot2D'Data")) {
103                     result_type = YacasRequest::PLOT2D;
104                     result = result.remove("Yagy'Plot2D'Data(");
105                     result.truncate(result.length() - 1);
106                 } else if (result.startsWith("Yagy'Plot3DS'Data")) {
107                     result_type = YacasRequest::PLOT3D;
108                     result = result.remove("Yagy'Plot3DS'Data(");
109                     result.truncate(result.length() - 1);
110                 } else if (result.startsWith("Graph(")) {
111                     result_type = YacasRequest::GRAPH;
112                     result = result.remove("Graph(");
113                     result.truncate(result.length() - 1);
114                 }
115                 request->answer(_idx++,
116                                 result_type,
117                                 result,
118                                 QString::fromStdString(_side_effects.str()));
119             } else {
120                 QString msg = QString::fromStdString(_yacas->Error());
121                 request->answer(_idx++,
122                                 YacasRequest::ERROR,
123                                 msg.trimmed(),
124                                 QString::fromStdString(_side_effects.str()));
125             }
126 
127             _requests.mtx.lock();
128         }
129     }
130 }
131 
_update_symbols()132 void YacasEngine::_update_symbols()
133 {
134     QMutexLocker lock(&_symbols_mtx);
135 
136     QSet<QString> ss;
137 
138     for (auto op : _yacas->getDefEnv().getEnv().PreFix())
139         ss.insert(QString::fromStdString(*op.first));
140 
141     for (auto op : _yacas->getDefEnv().getEnv().InFix())
142         ss.insert(QString::fromStdString(*op.first));
143 
144     for (auto op : _yacas->getDefEnv().getEnv().PostFix())
145         ss.insert(QString::fromStdString(*op.first));
146 
147     for (auto op : _yacas->getDefEnv().getEnv().Bodied())
148         ss.insert(QString::fromStdString(*op.first));
149 
150     for (auto op : _yacas->getDefEnv().getEnv().CoreCommands())
151         ss.insert(QString::fromStdString(*op.first));
152 
153     for (auto& op : _yacas->getDefEnv().getEnv().UserFunctions())
154         ss.insert(QString::fromStdString(*op.first));
155 
156     _symbols = QStringList::fromSet(ss);
157 }
158