// https://github.com/kimwalisch/calculator
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <stack>
#include <cstddef>
#include <cctype>
#include <cmath>
struct Calculator {
// if error occurs, save it here
bool error;
std::string message;
double x, y, t;
// eval("3 + 5") => 8
double eval(const std::string& expr, double x, double y, double t) {
error = false;
message.clear();
double value = 0;
this->x = x;
this->y = y;
this->t = t;
_index = 0;
_expr = expr;
value = parseExpr();
if (!isEnd()) unexpect("EOF");
return value;
}
double eval(const std::string& expr, double x) {
return eval(expr, x, x, x);
}
double eval(const std::string& expr, double x, double y) {
return eval(expr, x, y, 0);
}
// + - * / ^ e
enum { NONE, ADD, SUB, MUL, DIV, POW, EXP };
// + 10 left
struct Operator {
int op, prec, assoc;
Operator(int op, int prec, int assoc) :
op(op), prec(prec), assoc(assoc) {}
};
// <+> = 8 in (3 + 5)
struct Value {
Operator op;
double value;
Value(const Operator& op, double value) :
op(op), value(value) {}
int prec() const {
return op.prec;
}
bool none() const {
return op.op == NONE;
}
};
// for private use
std::string _expr;
std::size_t _index;
std::stack<Value> _stack;
Calculator() : error(false) {}
// return error = true if division by 0
double checkZero(double value) {
if (value == 0) {
std::string divmod = "/%";
std::size_t position = _expr.find_last_of(divmod, _index - 2);
error = true;
message = "division by 0";
if (position != std::string::npos)
message += " at\"" + _expr.substr(position) + "\"";
}
return value;
}
// calc atom
double calculate(double a, double b, const Operator& op) {
switch (op.op) {
case ADD:
return a + b;
case SUB:
return a - b;
case MUL:
return a * b;
case DIV:
return a / checkZero(b);
case POW:
return std::pow(a, b);
case EXP:
return a * std::pow(10, b);
default:
return 0;
}
}
bool isEnd() {
return _index >= _expr.length();
}
char getChar() {
if (isEnd()) return 0;
return _expr[_index];
}
void expect(const std::string& str) {
if (_expr.compare(_index, str.length(), str))
unexpect(str);
else _index += str.length();
}
void unexpect(const std::string& str) {
error = true;
message = "unexpected \"" + _expr.substr(_index) + "\" at " +
sizeToString(_index) + ", expect \"" + str + "\"";
}
std::string sizeToString(std::size_t size) {
std::stringstream ss;
ss << size;
return ss.str();
}
void eatSpaces() {
while (std::isspace(getChar())) _index++;
}
Operator parseOp() {
eatSpaces();
switch (getChar()) {
case '+':
_index++;
return Operator(ADD, 10, 'L');
case '-':
_index++;
return Operator(SUB, 10, 'L');
case '/':
_index++;
return Operator(DIV, 20, 'L');
case '*':
_index++;
return Operator(MUL, 20, 'L');
case '^':
_index++;
return Operator(POW, 30, 'R');
case 'e':
case 'E':
_index++;
return Operator(EXP, 40, 'R');
default:
return Operator(NONE, 0, 'L');
}
}
static int toInteger(char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 0xa;
if (c >= 'A' && c <= 'F') return c - 'A' + 0xa;
return 0xf + 1; // not an integer
}
int getInteger() {
return toInteger(getChar());
}
int parseDec() {
int value = 0;
for (int d; (d = getInteger()) <= 9; _index++)
value = value * 10 + d;
return value;
}
int parseHex() {
_index += 2;
int value = 0;
for (int h; (h = getInteger()) <= 0xf; _index++)
value = value * 0x10 + h;
return value;
}
bool isHex() {
if (_index + 2 < _expr.length()) {
char x = _expr[_index + 1];
char h = _expr[_index + 2];
return (x == 'x' || x == 'X') && toInteger(h) <= 0xf;
}
return false;
}
double parseReal() {
int dec = 0, base = 10;
double real = 0;
if (getChar() != '.') dec = parseDec();
_index++;
for (int d; (d = getInteger()) <= 9; _index++) {
real += (double) d / base;
base *= 10;
}
return dec + real;
}
bool isReal() {
bool result = false;
int index_ = _index;
while (std::isdigit(getChar()))
_index++;
if (getChar() == '.') {
_index++;
result = std::isdigit(getChar());
} else
result = false;
_index = index_;
return result;
}
bool beginWith(const std::string& a, const std::string& b, int start) {
unsigned n = b.length();
if (n + start > a.length()) return false;
for (unsigned i = 0; i < n; ++i)
if (a[i + start] != b[i]) return false;
return true;
}
bool isSin() {
return beginWith(_expr, "sin(", _index);
}
bool isSqrt() {
return beginWith(_expr, "sqrt(", _index);
}
double parseValue() {
double value = 0;
eatSpaces();
switch (getChar()) {
case '.':
value = parseReal();
break;
case '0':
if (isHex())
value = parseHex();
else if (isReal())
value = parseReal();
else
value = parseDec();
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (isReal())
value = parseReal();
else
value = parseDec();
break;
case '(':
_index++;
value = parseExpr();
eatSpaces();
expect(")");
break;
case '+':
_index++;
value = parseValue();
break;
case '-':
_index++;
value = -parseValue();
break;
case 's':
if (isSin()) {
_index += 4;
value = std::sin(parseExpr());
} else if (isSqrt()) {
_index += 5;
value = std::sqrt(parseExpr());
} else
unexpect("sin( or sqrt(");
eatSpaces();
expect(")");
break;
case 'c':
expect("cos(");
value = std::cos(parseExpr());
eatSpaces();
expect(")");
break;
case 'l':
expect("log(");
value = std::log(parseExpr());
eatSpaces();
expect(")");
break;
case 'x':
_index++;
value = x;
break;
case 'y':
_index++;
value = y;
break;
case 't':
_index++;
value = t;
break;
default:
if (!isEnd()) unexpect("value");
}
return value;
}
double parseExpr() {
_stack.push(Value(Operator(NONE, 0, 'L'), 0));
double value = parseValue();
while (!_stack.empty()) {
Operator op(parseOp());
while (op.prec < _stack.top().prec() ||
(op.prec == _stack.top().prec() &&
op.assoc == 'L')) {
if (_stack.top().none()) {
_stack.pop();
return value;
}
value = calculate(_stack.top().value, value, _stack.top().op);
_stack.pop();
}
_stack.push(Value(op, value));
value = parseValue();
}
return 0;
}
};
// sinx + 8y
// -> 2sin(x) + 8y sinx -> sin(x)
// -> 2 * sin(1) + 8 * 2 x, y -> 1, 2
std::string& gsub(std::string& x, const std::string& p, const std::string& r) {
std::string::size_type i = 0;
while ((i = x.find(p, i)) != std::string::npos) {
x.replace(i, p.length(), r);
i += r.length();
}
return x;
}
// \d\w -> \d * \w
std::string& rewrite(std::string& x) {
gsub(x, "sinx", "sin(x)");
gsub(x, "cosx", "cos(x)");
gsub(x, "logx", "log(x)");
gsub(x, "sqrtx", "sqrt(x)");
gsub(x, "siny", "sin(y)");
gsub(x, "cosy", "cos(y)");
gsub(x, "logy", "log(y)");
gsub(x, "sqrty", "sqrt(y)");
gsub(x, "sint", "sin(t)");
gsub(x, "cost", "cos(t)");
gsub(x, "logt", "log(t)");
gsub(x, "sqrtt", "sqrt(t)");
for (unsigned i = 0; i < x.length() - 1; ++i)
if (std::isdigit(x[i]) && std::isalpha(x[i + 1]))
x.insert(++i, 1, '*');
return x;
}
int main() {
Calculator c;
std::string input = "sqrt(0.2sinx + 8.2y + 7/t) - 5 / 3";
// std::cin >> input;
rewrite(input);
std::cout << input << std::endl;
std::cout << c.eval(input, 1.2, 0.3, 5) << std::endl;
return 0;
}