Skip to content

Instantly share code, notes, and snippets.

@abdoei
Created August 31, 2023 04:02
Show Gist options
  • Save abdoei/bda353950b4d0a02e3b32b1df90e527b to your computer and use it in GitHub Desktop.
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
#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