#ifndef CALCULATOR_HPP
#define CALCULATOR_HPP
#include <stdexcept>
#include <string>
#include <sstream>
#include <stack>
#include <cstddef>
#include <cctype>
namespace calculator
{
class error : public std::runtime_error
{
public:
error(const std::string& expr, const std::string& message)
: std::runtime_error(message),
expr_(expr)
{ }
#if __cplusplus < 201103L
~error() throw() { }
#endif
std::string expression() const
{
return expr_;
}
private:
std::string expr_;
};
template <typename T>
class ExpressionParser
{
public:
T eval(const std::string& expr)
{
T result = 0;
index_ = 0;
expr_ = expr;
try
{
result = parseExpr();
if (!isEnd())
unexpected();
}
catch (const calculator::error&)
{
while(!stack_.empty())
stack_.pop();
throw;
}
return result;
}
T eval(char c)
{
std::string expr(1, c);
return eval(expr);
}
private:
enum
{
OPERATOR_NULL,
OPERATOR_BITWISE_OR, OPERATOR_BITWISE_XOR, OPERATOR_BITWISE_AND, OPERATOR_BITWISE_SHL, OPERATOR_BITWISE_SHR, OPERATOR_ADDITION, OPERATOR_SUBTRACTION, OPERATOR_MULTIPLICATION, OPERATOR_DIVISION, OPERATOR_MODULO, OPERATOR_POWER, OPERATOR_EXPONENT };
struct Operator
{
int op;
int precedence;
int associativity;
Operator(int opr, int prec, int assoc) :
op(opr),
precedence(prec),
associativity(assoc)
{ }
};
struct OperatorValue
{
Operator op;
T value;
OperatorValue(const Operator& opr, T val) :
op(opr),
value(val)
{ }
int getPrecedence() const
{
return op.precedence;
}
bool isNull() const
{
return op.op == OPERATOR_NULL;
}
};
std::string expr_;
std::size_t index_;
std::stack<OperatorValue> stack_;
static T pow(T x, T n)
{
T res = 1;
while (n > 0)
{
if (n % 2 != 0)
{
res *= x;
n -= 1;
}
n /= 2;
if (n > 0)
x *= x;
}
return res;
}
T checkZero(T value) const
{
if (value == 0)
{
std::string divOperators("/%");
std::size_t division = expr_.find_last_of(divOperators, index_ - 2);
std::ostringstream msg;
msg << "Parser error: division by 0";
if (division != std::string::npos)
msg << " (error token is \""
<< expr_.substr(division, expr_.size() - division)
<< "\")";
throw calculator::error(expr_, msg.str());
}
return value;
}
T calculate(T v1, T v2, const Operator& op) const
{
switch (op.op)
{
case OPERATOR_BITWISE_OR: return v1 | v2;
case OPERATOR_BITWISE_XOR: return v1 ^ v2;
case OPERATOR_BITWISE_AND: return v1 & v2;
case OPERATOR_BITWISE_SHL: return v1 << v2;
case OPERATOR_BITWISE_SHR: return v1 >> v2;
case OPERATOR_ADDITION: return v1 + v2;
case OPERATOR_SUBTRACTION: return v1 - v2;
case OPERATOR_MULTIPLICATION: return v1 * v2;
case OPERATOR_DIVISION: return v1 / checkZero(v2);
case OPERATOR_MODULO: return v1 % checkZero(v2);
case OPERATOR_POWER: return pow(v1, v2);
case OPERATOR_EXPONENT: return v1 * pow(10, v2);
default: return 0;
}
}
bool isEnd() const
{
return index_ >= expr_.size();
}
char getCharacter() const
{
if (!isEnd())
return expr_[index_];
return 0;
}
void expect(const std::string& str)
{
if (expr_.compare(index_, str.size(), str) != 0)
unexpected();
index_ += str.size();
}
void unexpected() const
{
std::ostringstream msg;
msg << "Syntax error: unexpected token \""
<< expr_.substr(index_, expr_.size() - index_)
<< "\" at index "
<< index_;
throw calculator::error(expr_, msg.str());
}
void eatSpaces()
{
while (std::isspace(getCharacter()) != 0)
index_++;
}
Operator parseOp()
{
eatSpaces();
switch (getCharacter())
{
case '|': index_++; return Operator(OPERATOR_BITWISE_OR, 4, 'L');
case '&': index_++; return Operator(OPERATOR_BITWISE_AND, 6, 'L');
case '<': expect("<<"); return Operator(OPERATOR_BITWISE_SHL, 9, 'L');
case '>': expect(">>"); return Operator(OPERATOR_BITWISE_SHR, 9, 'L');
case '+': index_++; return Operator(OPERATOR_ADDITION, 10, 'L');
case '-': index_++; return Operator(OPERATOR_SUBTRACTION, 10, 'L');
case '/': index_++; return Operator(OPERATOR_DIVISION, 20, 'L');
case '%': index_++; return Operator(OPERATOR_MODULO, 20, 'L');
case '*': index_++; if (getCharacter() != '*')
return Operator(OPERATOR_MULTIPLICATION, 20, 'L');
index_++; return Operator(OPERATOR_POWER, 30, 'R');
case '^': index_++; return Operator(OPERATOR_POWER, 30, 'R');
case 'e': index_++; return Operator(OPERATOR_EXPONENT, 40, 'R');
case 'E': index_++; return Operator(OPERATOR_EXPONENT, 40, 'R');
default : return Operator(OPERATOR_NULL, 0, 'L');
}
}
static T 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;
T noDigit = 0xf + 1;
return noDigit;
}
T getInteger() const
{
return toInteger(getCharacter());
}
T parseDecimal()
{
T value = 0;
for (T d; (d = getInteger()) <= 9; index_++)
value = value * 10 + d;
return value;
}
T parseHex()
{
index_ = index_ + 2;
T value = 0;
for (T h; (h = getInteger()) <= 0xf; index_++)
value = value * 0x10 + h;
return value;
}
bool isHex() const
{
if (index_ + 2 < expr_.size())
{
char x = expr_[index_ + 1];
char h = expr_[index_ + 2];
return (std::tolower(x) == 'x' && toInteger(h) <= 0xf);
}
return false;
}
T parseValue()
{
T val = 0;
eatSpaces();
switch (getCharacter())
{
case '0': if (isHex())
val = parseHex();
else
val = parseDecimal();
break;
case '1': case '2': case '3': case '4': case '5':
case '6': case '7': case '8': case '9':
val = parseDecimal();
break;
case '(': index_++;
val = parseExpr();
eatSpaces();
if (getCharacter() != ')')
{
if (!isEnd())
unexpected();
throw calculator::error(expr_, "Syntax error: `)' expected at end of expression");
}
index_++; break;
case '~': index_++; val = ~parseValue(); break;
case '+': index_++; val = parseValue(); break;
case '-': index_++; val = parseValue() * static_cast<T>(-1);
break;
default : if (!isEnd())
unexpected();
throw calculator::error(expr_, "Syntax error: value expected at end of expression");
}
return val;
}
T parseExpr()
{
stack_.push(OperatorValue(Operator(OPERATOR_NULL, 0, 'L'), 0));
T value = parseValue();
while (!stack_.empty())
{
Operator op(parseOp());
while (op.precedence < stack_.top().getPrecedence() || (
op.precedence == stack_.top().getPrecedence() &&
op.associativity == 'L'))
{
if (stack_.top().isNull())
{
stack_.pop();
return value;
}
value = calculate(stack_.top().value, value, stack_.top().op);
stack_.pop();
}
stack_.push(OperatorValue(op, value));
value = parseValue();
}
return 0;
}
};
template <typename T>
inline T eval(const std::string& expression)
{
ExpressionParser<T> parser;
return parser.eval(expression);
}
template <typename T>
inline T eval(char c)
{
ExpressionParser<T> parser;
return parser.eval(c);
}
inline int eval(const std::string& expression)
{
return eval<int>(expression);
}
inline int eval(char c)
{
return eval<int>(c);
}
}
#endif