Here's my code for this' weeks homework I had. The code should take an expression , preferably in prefix notation ( otherwise throws exception ), that evaluate prefix expression. I've given three example in the main function. The expression gets saved in the object at the mText variable type of string and then the function 'evaluate' evaluates the expression.
The code works fine as said by my professor, but he highly criticized my poor style of writing.
I'm interested in your opinion and what exactly should I be working on in the future ? I'm pretty sure my biggest mistake is writing bunch of code in one line instead of separating it in a several lines.
The code:
#include <iostream>
#include <string>
#include <stdexcept>
#include <cctype>
#include <sstream>
#include <limits>
#include <cmath>
using namespace std;
class Expression{
public:
using Value=int;
enum TokenType {operand,unaryOp,binaryOp};
struct Token{
TokenType tokentype;
char sym;
Value val;
bool start;
TokenType type() const { return tokentype; }
char symbol() const { return sym; }
Value value() const { return val; }
};
Expression(){}
Expression(string text);
Value evaluate();
string getText() { return mText; }
private:
static string valid,whites;
string mText;
size_t processed_size=0;
Token mnextToken;
Token getToken();
};
Expression::Expression(string text) : mText{text} {}
string Expression::valid="+*/-^~0123456789";
string Expression::whites=" \t\v\n";
Expression::Value solve(Expression::Value a, Expression::Value b,char symbol){
long long num1=a,num2=b;
switch (symbol){
case '+': return !(abs(num1+num2) > numeric_limits<int>::max()) ? a+b : throw runtime_error("Wrong expression [Two big(or low) numbers generated for the type int].");
case '-': return !(abs(num1-num2) > numeric_limits<int>::max()) ? a-b : throw runtime_error("Wrong expression [Two big(or low) numbers generated for the type int].");
case '*': return !(abs(num1*num2) > numeric_limits<int>::max()) ? a*b : throw runtime_error("Wrong expression [Two big(or low) numbers generated for the type int].");
case '/': return !b ? a/b : throw runtime_error("Wrong expression [dividing with 0].");
case '^': { auto b_th_power_of = [num2] (long long x) mutable { if (!num2) return static_cast<long long>(1);
if (num2<0) return static_cast<long long>(0); long long temp=1;
while (num2-->0) temp*=x;
return temp; };
num2=b_th_power_of(num1);
return !(abs(num2) > numeric_limits<int>::max()) ? num2 : throw runtime_error("Wrong expression [Two big(or low) numbers generated for the type int]."); }
default: throw runtime_error("Undefined error occured."); //Za svaki slucaj
}
}
Expression::Value Expression::evaluate(){
Token t=getToken();
if (t.type()==unaryOp){
Value a=evaluate();
if (t.start && mText.find_first_not_of(Expression::whites,processed_size)!=string::npos) throw runtime_error("Wrong expression [Too many characters on the right side]");
return a-(2*a);
}
else if(t.type() == binaryOp){
Value a=evaluate();
Value b=evaluate();
if (t.start && mText.find_first_not_of(Expression::whites,processed_size)!=string::npos) throw runtime_error("Wrong expression [Too many characters on the right side].");
return solve(a,b,t.symbol());
}
else{
return t.value();
}
}
Expression::Token Expression::getToken(){
processed_size ? mnextToken.start=false : mnextToken.start=true;
if (static_cast<int>(processed_size) > static_cast<int>(mText.length())-1 ) throw runtime_error("Wrong expression [Too many operators on the left side or empty expression]."); // Fali mi u dz
while (Expression::whites.find( mText.at(processed_size) ) != string::npos){
++processed_size;
if (processed_size > mText.length()-1) throw runtime_error("Wrong expression [Too many operators on the left side].");
}
size_t found=Expression::valid.find(mText[processed_size]);
if (found < 5){
mnextToken.sym=mText[processed_size++];
mnextToken.tokentype=binaryOp;
}
else if (found == 5){
++processed_size;
mnextToken.tokentype=unaryOp;
}
else if (found < 16 && found > 5){
if (mnextToken.start) throw runtime_error("Wrong expression [Expression shouldn't start with a number].");
Expression::Value number;
stringstream buffer;
while (isdigit( mText[processed_size] )){ buffer << mText[processed_size]; processed_size++; }
if ( !(buffer >> number) ) throw runtime_error("Wrong expression [Too large number(s) in the expression].");
mnextToken.val=number;
mnextToken.tokentype=operand;
}
else throw runtime_error("Wrong expression [Invalid symbol inside the expression].");
return mnextToken;
}
int main()
{
try{
Expression a1("+ 2 3");
cout << a1.getText() << " = " << a1.evaluate() << " [Correct result: 5]" << endl;
Expression a2(" * 4 ^ 2++2 3 1 ");
cout << a2.getText() << " = " << a2.evaluate() << " [Correct result: 256]" << endl;
Expression a3("-+*2 3 *5 4 9");
cout << a3.getText() << " = " << a3.evaluate() << " [Correct result: 17]" << endl;
} catch (const exception& ex){ cout << "Exception: " << ex.what() << endl; }
return 0;
}
Just to mention that I was supposed to work with runtime_error exception as that was the part of the homework.
I guess [somebody does not like wide lines] Is that generally a bad way of writing
Try to visualise a newspaper. Competent presentation of information is making it easy to grasp. Competent presentation of text is making it easy to read. If darting back from the end of any given line the eye doesn't "automatically" continue with the next line, the line has been too long. \$\endgroup\$ – greybeard Apr 6 '20 at 20:58