Created
August 31, 2023 04:02
-
-
Save abdoei/bda353950b4d0a02e3b32b1df90e527b to your computer and use it in GitHub Desktop.
I have written a linked stack class and used it to implement a Python-like eval fuction to evaluate math expressions from scratch
This file contains 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
#include <iostream> | |
#include <string> | |
#include <cmath> | |
using std::pow; | |
using namespace std; | |
/*********************************** Stack implementation ***********************************/ | |
template <class T> | |
class Stack{ | |
struct Node { | |
T value; | |
Node * next = NULL; | |
}; | |
Node* top; | |
public: | |
Stack() : top(nullptr) {} | |
~Stack(){ | |
while (top != NULL) | |
{ | |
this->pop(); | |
} | |
} | |
T getTop(){ | |
if(isEmpty()) {cout << "can't getTop() an empty stack\n"; return -1;} | |
else | |
return top->value; | |
} | |
void push(T v){ | |
Node *tmp = new Node; | |
if(tmp == NULL) cout << "Stack::push(T v) can't allocate memory\n"; | |
else{ | |
tmp->value = v; | |
tmp->next = top; | |
top = tmp; | |
} | |
} | |
void pop(){ | |
if(isEmpty()) cout << "Can't pop() an empty stack" << endl; | |
else{ | |
Node * tmp = top; | |
top = top->next; | |
tmp = tmp->next = NULL; | |
delete tmp; | |
} | |
} | |
bool isEmpty(){ | |
return (top == NULL); | |
} | |
void disp(){ | |
Node* cur = top; | |
cout << '['; | |
while (cur != NULL) | |
{ | |
cout << cur->value << " "; | |
cur = cur->next; | |
} | |
cout << ']'; | |
} | |
}; | |
/*********************************** infix2postfix implementation ***********************************/ | |
bool isBalanced(std::string exp){ | |
Stack<char> st; | |
for(const char c: exp){ | |
if (c == ')' || c == ']' || c == '}'){ | |
if (st.isEmpty()) return false; | |
const char cur = st.getTop(); | |
if( (cur == '(' && c == ')') | |
|| (cur == '[' && c == ']') | |
|| (cur == '{' && c == '}')){ | |
st.pop(); | |
} | |
} | |
else if (c == '(' || c == '[' || c == '{') | |
st.push(c); | |
} | |
return st.isEmpty(); | |
} | |
int order(const char c){ | |
if (c == '+' || c == '-') return 0; | |
if (c == '*' || c == '/') return 1; | |
return -1; | |
} | |
bool IsOperand(char C) | |
{ | |
if (C >= '0' && C <= '9') | |
return true; | |
if (C >= 'a' && C <= 'z') | |
return true; | |
if (C >= 'A' && C <= 'Z') | |
return true; | |
return false; | |
} | |
bool IsOperator(char C) | |
{ | |
if (C == '+' || C == '-' || C == '*' || C == '/' || C == '^') | |
return true; | |
return false; | |
} | |
int GetOperatorWeight(char op) | |
{ | |
int weight = -1; | |
switch (op) | |
{ | |
case '+': | |
case '-': | |
weight = 1; | |
break; | |
case '*': | |
case '/': | |
weight = 2; | |
break; | |
case '^': | |
weight = 3; | |
break; | |
} | |
return weight; | |
} | |
int IsRightAssociative(char op) | |
{ | |
// this means if we have 2 ^ 3 ^ 4 | |
// it would be evaluated like this | |
// 2 ^ (3 ^ 4) as right associative | |
if (op == '^') | |
return true; | |
return false; | |
} | |
int HasHigherPrecedence(char op1, char op2) | |
{ | |
int op1Weight = GetOperatorWeight(op1); | |
int op2Weight = GetOperatorWeight(op2); | |
// If operators have equal precedence, return true if they are left associative. | |
// return false, if right associative. | |
// if operator is left-associative, left one should be given priority. | |
if (op1Weight == op2Weight) | |
{ | |
if (IsRightAssociative(op1)) | |
return false; | |
else | |
return true; | |
} | |
return op1Weight > op2Weight ? true : false; | |
} | |
/** | |
* 5 ** (8 ** (6 ** 7)) | |
* 5 ** 8 ** 6 ** 7 | |
* 5 (8 (6 7 **) **) ** | |
* | |
* ((5 + 8) + 6) + 7 | |
* 5 + 8 + 6 + 7 | |
* ((5 8 +) 6 +) 7 + | |
* | |
*/ | |
std::string infix2postfix(const std::string& exp){ | |
if(!isBalanced(exp)) return "-1"; | |
Stack<char> st; | |
std::string out; | |
for(auto c: exp){ | |
if(c == ' ') continue; | |
else if(IsOperand(c)) { | |
if(IsOperator(out.back())) out += " "; | |
out += c; | |
} | |
else if(IsOperator(c)){ | |
out += " "; | |
while (!st.isEmpty() && st.getTop() != '(' && HasHigherPrecedence(st.getTop(), c)) | |
{ | |
out += st.getTop(); | |
out += " "; | |
st.pop(); | |
} | |
st.push(c); // push the higher precedance operator on top of the lower on the stack | |
} | |
else if(c == '(') st.push(c); | |
else if(c == ')'){ | |
out += " "; | |
while (!st.isEmpty() && st.getTop() != '(') | |
{ | |
out += st.getTop(); | |
out += " "; | |
st.pop(); | |
} | |
st.pop(); // pop the last ( out | |
} | |
} | |
while (!st.isEmpty()) | |
{ | |
if (out.back() != ' ') | |
out += " "; | |
out += st.getTop(); | |
st.pop(); | |
} | |
return out; | |
} | |
/*********************************** eval implementation ***********************************/ | |
float performOperation(long op1, long op2, char op) | |
{ | |
float ans; | |
switch (op){ | |
case '+': | |
ans = op2 + op1; | |
break; | |
case '-': | |
ans = op2 - op1; | |
break; | |
case '*': | |
ans = op2 * op1; | |
break; | |
case '/': | |
ans = 1.0 * op2 / op1; | |
break; | |
case '^': | |
ans = pow(op2, op1); | |
} | |
return ans; | |
} | |
float eval(const std::string exp){ | |
Stack<double> st; | |
char buf[20]; | |
int bufIdx = 0; | |
for(const char c: exp){ | |
if(IsOperand(c)) buf[bufIdx++] = c; | |
else if(c == ' '){ | |
if(bufIdx > 0){ | |
buf[bufIdx] = '\0'; | |
long x = atoi(buf); | |
st.push(x); | |
bufIdx = 0; | |
} | |
} | |
else if(IsOperator(c)){ | |
long x = st.getTop(); | |
st.pop(); | |
long y = st.getTop(); | |
st.pop(); | |
st.push(performOperation(x, y, c)); | |
} | |
} | |
return st.getTop(); | |
} | |
/*********************************** main function ***********************************/ | |
int main(){ | |
// Stack<int> s; | |
// s.push(5); | |
// s.push(6); | |
// s.push(7); | |
// s.push(8); | |
// s.push(9); | |
// s.push(10); | |
// s.pop(); | |
// cout << s.getTop() << endl; | |
// s.pop(); | |
// cout << s.getTop() << endl; | |
// s.disp(); | |
// std::string ex1 = "(5 + 6) * 2"; | |
// std::string ex2 = "[5 + {6}] * (2"; | |
// cout << ex1 << " validity is " << isBalanced(ex1) << endl; | |
// cout << ex2 << " validity is " << isBalanced(ex2) << endl; | |
cout << infix2postfix("10 * (2 - 1 + 5) / 2 - 3 ^ 2 ^ 3") << endl; | |
cout << eval(infix2postfix("10 * (2 - 1 + 5) / 2 - 3 ^ 2 ^ 3")) << endl; | |
double val = 10 * (2 - 1 + 5) / 2 - pow(3,pow(2, 3)); | |
cout << "supposed to equal " << val << endl; | |
return 0; | |
} | |
/** | |
* 1 3 (9 9 *) 9 2 - / ^ ^ | |
* 1 3 81 (9 2 -)/ ^ ^ | |
* 1 3 (81 7 /)^ ^ | |
* 1 (3 11.571428571 ^)^ | |
* 1 331354.533685527 ^ | |
* 4.809953394×10²¹ | |
* | |
* 2 (3 ((4 5 *) (9 5 -)/)^)^ | |
* ( 20 ) ( 4 ) | |
* ( 5 ) | |
* ( 243 ) | |
* 1.413477652×10⁷³ | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment