servatrice/common/expression.cpp
ebbit1q baaf261116
use utf8 instead of the system encoding (#4582)
* use utf8 instead of the system preference

* found another toLocal8Bit
2022-02-27 22:33:36 +01:00

109 lines
3.4 KiB
C++

#include "expression.h"
#include "./lib/peglib.h"
#include <QByteArray>
#include <QString>
#include <cmath>
#include <functional>
peg::parser math(R"(
EXPRESSION <- P0
P0 <- P1 (P1_OPERATOR P1)*
P1 <- P2 (P2_OPERATOR P2)*
P2 <- P3 (P3_OPERATOR P3)*
P3 <- NUMBER / FUNCTION / VARIABLE / '(' P0 ')'
P1_OPERATOR <- < [-+] >
P2_OPERATOR <- < [/*] >
P3_OPERATOR <- < '^' >
NUMBER <- < '-'? [0-9]+ >
NAME <- < [a-z][a-z0-9]* >
VARIABLE <- < [x] >
FUNCTION <- NAME '(' EXPRESSION ( [,\n] EXPRESSION )* ')'
%whitespace <- [ \t\r]*
)");
QMap<QString, std::function<double(double)>> *default_functions = nullptr;
Expression::Expression(double initial) : value(initial)
{
if (default_functions == nullptr) {
default_functions = new QMap<QString, std::function<double(double)>>();
default_functions->insert("sin", [](double a) { return sin(a); });
default_functions->insert("cos", [](double a) { return cos(a); });
default_functions->insert("tan", [](double a) { return tan(a); });
default_functions->insert("sqrt", [](double a) { return sqrt(a); });
default_functions->insert("log", [](double a) { return log(a); });
default_functions->insert("log10", [](double a) { return log(a); });
default_functions->insert("trunc", [](double a) { return trunc(a); });
default_functions->insert("abs", [](double a) { return fabs(a); });
default_functions->insert("floor", [](double a) { return floor(a); });
default_functions->insert("ceil", [](double a) { return ceil(a); });
default_functions->insert("round", [](double a) { return round(a); });
default_functions->insert("trunc", [](double a) { return trunc(a); });
}
fns = QMap<QString, std::function<double(double)>>(*default_functions);
}
double Expression::eval(const peg::Ast &ast)
{
const auto &nodes = ast.nodes;
if (ast.name == "NUMBER") {
return stod(ast.token);
} else if (ast.name == "FUNCTION") {
QString name = QString::fromStdString(nodes[0]->token);
if (!fns.contains(name))
return 0;
return fns[name](eval(*nodes[1]));
} else if (ast.name == "VARIABLE") {
return value;
} else if (ast.name[0] == 'P') {
double result = eval(*nodes[0]);
for (unsigned int i = 1; i < nodes.size(); i += 2) {
double arg = eval(*nodes[i + 1]);
char operation = nodes[i]->token[0];
switch (operation) {
case '+':
result += arg;
break;
case '-':
result -= arg;
break;
case '*':
result *= arg;
break;
case '/':
result /= arg;
break;
case '^':
result = pow(result, arg);
break;
default:
result = 0;
break;
}
}
return result;
} else {
return -1;
}
}
double Expression::parse(const QString &expr)
{
QByteArray ba = expr.toUtf8();
math.enable_ast();
std::shared_ptr<peg::Ast> ast;
if (math.parse(ba.data(), ast)) {
ast = peg::AstOptimizer(true).optimize(ast);
return eval(*ast);
}
return 0;
}