calculator

Run Settings
LanguageC++
Language Version
Run Command
// 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; }
Editor Settings
Theme
Key bindings
Full width
Lines