Created
August 16, 2015 14:53
-
-
Save shrubb/2d85b4d8cc7d20ecf498 to your computer and use it in GitHub Desktop.
Arithmetic expression evaluator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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