Skip to content

Instantly share code, notes, and snippets.

@shrubb
Created August 16, 2015 14:53
Show Gist options
  • Save shrubb/2d85b4d8cc7d20ecf498 to your computer and use it in GitHub Desktop.
Save shrubb/2d85b4d8cc7d20ecf498 to your computer and use it in GitHub Desktop.
Arithmetic expression evaluator
//
// Created by shrubb on 16.08.15.
//
#include <string>
#include <iostream>
#include <algorithm>
const std::string validOps = "+-*/";
struct Token {
enum Type {
operation, number, openingBr, closingBr
};
Type type;
std::string text;
double value;
Token(const Type type) : type(type) { }
Token() {}
};
class Tokenizer {
private:
const std::string & str;
size_t position;
public:
Token currentToken;
Tokenizer(const std::string & str): str(str), position(0) { getNextToken(); }
bool done() { return position == std::string::npos; }
void getNextToken() {
if (position == str.length()) {
position = std::string::npos;
return;
}
if (done()) {
throw std::runtime_error("an attempt to get a token, but there are none remaining");
}
// operation
if (validOps.find(str[position]) != std::string::npos) {
currentToken = Token(Token::operation);
currentToken.text = std::string(1, str[position++]);
return;
}
// number
if (isdigit(str[position]) || str[position] == '.') {
size_t startPos = position;
while (isdigit(str[position]) || str[position] == '.') {
++position;
}
try {
currentToken = Token(Token::number);
currentToken.value = std::stod(str.substr(startPos, position - startPos));
return;
} catch (...) {
throw std::runtime_error("wrong number format");
}
}
// function
if (isalpha(str[position])) {
throw std::runtime_error("no functions defined yet");
}
// bracket
switch (str[position]) {
case '(': {
currentToken = Token(Token::openingBr);
currentToken.text = std::string(1, str[position++]);
return;
}
case ')': {
currentToken = Token(Token::closingBr);
currentToken.text = std::string(1, str[position++]);
return;
}
default: {
throw std::runtime_error("invalid character");
}
}
}
};
class Evaluator {
private:
Tokenizer tokenizer;
/*
* выражение ::= [+,-,.] слагаемое [ +- слагаемое] *
* слагаемое ::= множитель [ /* множитель] *
* множитель ::= число | (выражение)
*/
double evaluateProdEl();
double evaluateSumEl();
public:
double evaluateExpression();
Evaluator(Tokenizer & tokenizer): tokenizer(tokenizer) {}
};
double Evaluator::evaluateProdEl() {
switch (tokenizer.currentToken.type) {
case Token::openingBr: {
tokenizer.getNextToken();
double result = evaluateExpression();
if (tokenizer.currentToken.type == Token::closingBr) {
tokenizer.getNextToken();
return result;
} else {
throw std::runtime_error("syntax error");
}
}
case Token::number: {
double result = tokenizer.currentToken.value;
tokenizer.getNextToken();
return result;
}
default: {
throw std::runtime_error("syntax error");
}
}
}
double Evaluator::evaluateSumEl() {
double result = evaluateProdEl();
while (!tokenizer.done()) {
switch (tokenizer.currentToken.text[0]) {
case '*': {
tokenizer.getNextToken();
result *= evaluateProdEl();
break;
}
case '/': {
tokenizer.getNextToken();
result /= evaluateProdEl();
break;
}
default: {
return result;
}
}
}
return result;
}
double Evaluator::evaluateExpression() {
double factor = 1.0;
if (tokenizer.currentToken.type == Token::operation) {
switch (tokenizer.currentToken.text[0]) {
case '-': {
factor = -1.0;
}
case '+': {
tokenizer.getNextToken();
break;
}
default: {
throw std::runtime_error("syntax error");
}
}
}
double result = factor * evaluateSumEl();
while (!tokenizer.done()) {
if (tokenizer.currentToken.type == Token::operation) {
switch (tokenizer.currentToken.text[0]) {
case '+': {
tokenizer.getNextToken();
result += evaluateSumEl();
break;
}
case '-': {
tokenizer.getNextToken();
result -= evaluateSumEl();
break;
}
default: {
return result;
}
}
} else {
return result;
}
}
return result;
}
void removeSpaces(std::string & str) {
std::string::iterator iter, insertingIter;
iter = insertingIter = str.begin();
for (; iter != str.end(); ++iter) {
if (*iter != ' ') {
*insertingIter = *iter;
++insertingIter;
}
}
str.erase(insertingIter, iter);
}
int main() {
std::string input;
std::getline(std::cin, input);
removeSpaces(input);
Tokenizer tokenizer(input);
try {
std::cout << Evaluator(tokenizer).evaluateExpression() << std::endl;
} catch (std::runtime_error exception) {
std::cout << exception.what() << std::endl;
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment