Skip to content

feat: add infix to postfix, prefix, and prefix to infix expression converters #2960

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions strings/infix_to_postfix.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/**
* @file
* @brief Conversion of infix expressions to postfix using the Shunting Yard
* algorithm.
* @details
* Infix expressions (e.g., A + B) are converted to postfix (e.g., AB+) to
* simplify evaluation. See [Shunting Yard
* Algorithm](https://en.wikipedia.org/wiki/Shunting-yard_algorithm)
*/

#include <cassert> /// for assert
#include <cctype> /// for isalnum, isspace
#include <iostream> /// for IO operations
#include <stack> /// for std::stack
#include <string> /// for std::string

/**
* @namespace expression
* @brief Algorithms for expression parsing and conversion
*/
namespace expression {

/**
* @brief Checks if the given character is a supported operator.
* @param c input character
* @return true if operator, false otherwise
*/
bool is_operator(char c) {
return c == '+' || c == '-' || c == '*' || c == '/' || c == '^';
}

/**
* @brief Returns precedence level of operator
*/
int precedence(char op) {
if (op == '^')
return 3;
if (op == '*' || op == '/')
return 2;
if (op == '+' || op == '-')
return 1;
return 0;
}

/**
* @brief Returns true if operator is right-associative
*/
bool is_right_associative(char op) { return op == '^'; }

/**
* @brief Converts an infix expression to postfix (RPN).
* @param expr the input infix expression
* @return postfix expression as string
*/
std::string infix_to_postfix(const std::string &expr) {
std::stack<char> ops;
std::string result;

for (char token : expr) {
if (std::isspace(token)) {
continue;
}
if (std::isalnum(token)) {
result += token;
} else if (token == '(') {
ops.push(token);
} else if (token == ')') {
while (!ops.empty() && ops.top() != '(') {
result += ops.top();
ops.pop();
}
if (!ops.empty()) {
ops.pop(); // remove '('
}
} else if (is_operator(token)) {
while (!ops.empty() && is_operator(ops.top())) {
char top = ops.top();
if ((is_right_associative(token) &&
precedence(token) < precedence(top)) ||
(!is_right_associative(token) &&
precedence(token) <= precedence(top))) {
result += top;
ops.pop();
} else {
break;
}
}
ops.push(token);
}
}

while (!ops.empty()) {
result += ops.top();
ops.pop();
}

return result;
}

} // namespace expression

/**
* @brief Self-test implementations
* @returns void
*/
static void test() {
assert(expression::infix_to_postfix("a+(b*c-(d/e^f)*g)*h") ==
"abc*def^/g*-h*+");
assert(expression::infix_to_postfix("A*(B+C)") == "ABC+*");
assert(expression::infix_to_postfix("A+B*C") == "ABC*+");
assert(expression::infix_to_postfix("((A+B)*C)-D^E^F") == "AB+C*DEF^^-");

std::cout << "All tests have successfully passed!\n";
}

/**
* @brief Main function
*/
int main() {
test();
return 0;
}
135 changes: 135 additions & 0 deletions strings/infix_to_prefix.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/**
* @file
* @brief Converts infix expressions to prefix notation using reversal and
* infix-to-postfix logic.
* @details
* Uses the idea that prefix = reverse(postfix(reverse(infix))).
* See [Prefix notation](https://en.wikipedia.org/wiki/Polish_notation) for
* theory and use cases.
*/

#include <algorithm> /// for std::reverse
#include <cassert> /// for assert
#include <cctype> /// for isalnum
#include <iostream> /// for std::cout
#include <stack> /// for std::stack
#include <string> /// for std::string

/**
* @namespace expression
* @brief Algorithms for expression parsing and conversion
*/
namespace expression {

/**
* @brief Check if a character is an operator.
*/
bool is_operator(char ch) {
return ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '^';
}

/**
* @brief Get precedence of an operator.
*/
int precedence(char op) {
if (op == '^')
return 3;
if (op == '*' || op == '/')
return 2;
if (op == '+' || op == '-')
return 1;
return 0;
}

/**
* @brief Check if an operator is right-associative.
*/
bool is_right_associative(char op) { return op == '^'; }

/**
* @brief Convert infix to postfix (reused from infix_to_postfix.cpp).
*/
std::string infix_to_postfix(const std::string &expr) {
std::stack<char> ops;
std::string result;

for (char token : expr) {
if (std::isspace(token))
continue;

if (std::isalnum(token)) {
result += token;
} else if (token == '(') {
ops.push(token);
} else if (token == ')') {
while (!ops.empty() && ops.top() != '(') {
result += ops.top();
ops.pop();
}
if (!ops.empty())
ops.pop();
} else if (is_operator(token)) {
while (!ops.empty() && is_operator(ops.top())) {
char top = ops.top();
if ((is_right_associative(token) &&
precedence(token) < precedence(top)) ||
(!is_right_associative(token) &&
precedence(token) <= precedence(top))) {
result += top;
ops.pop();
} else
break;
}
ops.push(token);
}
}

while (!ops.empty()) {
result += ops.top();
ops.pop();
}

return result;
}

/**
* @brief Convert infix expression to prefix using reverse + postfix conversion.
* @param expr input infix expression
* @returns prefix expression
*/
std::string infix_to_prefix(const std::string &expr) {
std::string rev_expr = expr;
std::reverse(rev_expr.begin(), rev_expr.end());

for (char &ch : rev_expr) {
if (ch == '(')
ch = ')';
else if (ch == ')')
ch = '(';
}

std::string postfix = infix_to_postfix(rev_expr);
std::reverse(postfix.begin(), postfix.end());
return postfix;
}

} // namespace expression

/**
* @brief Self-tests
*/
static void test() {
assert(expression::infix_to_prefix("(a-b/c)*(a/k-l)") == "*-a/bc-/akl");
assert(expression::infix_to_prefix("a+b*c") == "+a*bc");
assert(expression::infix_to_prefix("((a+b)*c)-d^e^f") == "-*+abc^d^ef");

std::cout << "All tests have successfully passed!\n";
}

/**
* @brief Main function
*/
int main() {
test();
return 0;
}
81 changes: 81 additions & 0 deletions strings/prefix_to_infix.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* @file
* @brief Converts prefix expressions to infix using a stack.
* @details
* Traverses from right to left, building binary operations.
* See [Prefix to Infix](https://www.geeksforgeeks.org/prefix-infix-conversion/)
* for reference and algorithm details.
*/

#include <cassert> /// for assert
#include <cctype> /// for isalnum
#include <iostream> /// for std::cout
#include <stack> /// for std::stack
#include <string> /// for std::string

/**
* @namespace expression
* @brief Algorithms for expression parsing and conversion
*/
namespace expression {

/**
* @brief Check if the character is an operator.
*/
bool is_operator(char ch) {
return ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '^';
}

/**
* @brief Convert prefix expression to infix.
* @param prefix the input prefix expression
* @return infix expression as a string
*/
std::string prefix_to_infix(const std::string &prefix) {
std::stack<std::string> stk;

for (int i = static_cast<int>(prefix.length()) - 1; i >= 0; --i) {
char ch = prefix[i];

if (std::isspace(ch))
continue;

if (std::isalnum(ch)) {
stk.push(std::string(1, ch));
} else if (is_operator(ch)) {
if (stk.size() < 2)
return "Invalid Expression";

std::string op1 = stk.top();
stk.pop();
std::string op2 = stk.top();
stk.pop();
std::string expr = "(" + op1 + ch + op2 + ")";
stk.push(expr);
}
}

return (stk.size() == 1) ? stk.top() : "Invalid Expression";
}

} // namespace expression

/**
* @brief Self-test implementations
*/
static void test() {
assert(expression::prefix_to_infix("*-A/BC-/AKL") ==
"((A-(B/C))*((A/K)-L))");
assert(expression::prefix_to_infix("+AB") == "(A+B)");
assert(expression::prefix_to_infix("*+AB-CD") == "((A+B)*(C-D))");

std::cout << "All tests have successfully passed!\n";
}

/**
* @brief Main function
*/
int main() {
test();
return 0;
}