From e2a437ed1704e99625c2bc6056cdca52531acdba Mon Sep 17 00:00:00 2001 From: Sreekar Reddy Date: Mon, 30 Jun 2025 23:45:41 +0530 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20add=20infix=E2=86=94postfix/prefix?= =?UTF-8?q?=20expression=20converters=20with=20self-tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- strings/infix_to_postfix.cpp | 122 +++++++++++++++++++++++++++++++ strings/infix_to_prefix.cpp | 135 +++++++++++++++++++++++++++++++++++ strings/prefix_to_infix.cpp | 81 +++++++++++++++++++++ 3 files changed, 338 insertions(+) create mode 100644 strings/infix_to_postfix.cpp create mode 100644 strings/infix_to_prefix.cpp create mode 100644 strings/prefix_to_infix.cpp diff --git a/strings/infix_to_postfix.cpp b/strings/infix_to_postfix.cpp new file mode 100644 index 0000000000..3f67651338 --- /dev/null +++ b/strings/infix_to_postfix.cpp @@ -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 /// for assert +#include /// for isalnum, isspace +#include /// for IO operations +#include /// for std::stack +#include /// 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 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; +} \ No newline at end of file diff --git a/strings/infix_to_prefix.cpp b/strings/infix_to_prefix.cpp new file mode 100644 index 0000000000..df0f6a7fb1 --- /dev/null +++ b/strings/infix_to_prefix.cpp @@ -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 /// for std::reverse +#include /// for assert +#include /// for isalnum +#include /// for std::cout +#include /// for std::stack +#include /// 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 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; +} \ No newline at end of file diff --git a/strings/prefix_to_infix.cpp b/strings/prefix_to_infix.cpp new file mode 100644 index 0000000000..018d3e43df --- /dev/null +++ b/strings/prefix_to_infix.cpp @@ -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 /// for assert +#include /// for isalnum +#include /// for std::cout +#include /// for std::stack +#include /// 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 stk; + + for (int i = static_cast(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; +} \ No newline at end of file From 4f73564fa2bd45de03630684827d284d9d63cfaa Mon Sep 17 00:00:00 2001 From: Sreekar Reddy Date: Fri, 11 Jul 2025 15:48:59 +0530 Subject: [PATCH 2/2] Refactor: Merge all expression conversions into ExpressionConverter class --- strings/expressionConverter.cpp | 185 ++++++++++++++++++++++++++++++++ strings/infix_to_postfix.cpp | 122 --------------------- strings/infix_to_prefix.cpp | 135 ----------------------- strings/prefix_to_infix.cpp | 81 -------------- 4 files changed, 185 insertions(+), 338 deletions(-) create mode 100644 strings/expressionConverter.cpp delete mode 100644 strings/infix_to_postfix.cpp delete mode 100644 strings/infix_to_prefix.cpp delete mode 100644 strings/prefix_to_infix.cpp diff --git a/strings/expressionConverter.cpp b/strings/expressionConverter.cpp new file mode 100644 index 0000000000..ab76769941 --- /dev/null +++ b/strings/expressionConverter.cpp @@ -0,0 +1,185 @@ +/** + * @file + * @brief Expression conversion utilities: infix to postfix/prefix and prefix to + * infix + * @details + * This module provides functions to convert expressions between + * infix, postfix (Reverse Polish Notation), and prefix (Polish Notation) + * using the Shunting Yard algorithm and stack-based methods. + * See: https://en.wikipedia.org/wiki/Shunting-yard_algorithm + */ + +#include /// for std::reverse +#include /// for assert +#include /// for isalnum, isspace +#include /// for IO operations +#include /// for std::stack +#include /// 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. + * @param op operator character + * @return integer precedence + */ +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. + * @param op operator character + * @return true if right-associative + */ +bool is_right_associative(char op) { return op == '^'; } + +/** + * @brief Converts an infix expression to postfix notation. + * @param expr input infix expression + * @return postfix expression as string + */ +std::string infix_to_postfix(const std::string &expr) { + std::stack 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(); // discard '(' + } 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 Converts an infix expression to prefix notation. + * @param expr input infix expression + * @return prefix expression as string + */ +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; +} + +/** + * @brief Converts a prefix expression to infix notation. + * @param prefix input prefix expression + * @return infix expression or "Invalid Expression" on error + */ +std::string prefix_to_infix(const std::string &prefix) { + std::stack stk; + + for (int i = static_cast(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(); + stk.push("(" + op1 + ch + op2 + ")"); + } + } + + return (stk.size() == 1) ? stk.top() : "Invalid Expression"; +} + +} // namespace expression + +/** + * @brief Self-tests for the expression conversion functions. + */ +static void test() { + using namespace expression; + + // Infix to Postfix + assert(infix_to_postfix("a+(b*c-(d/e^f)*g)*h") == "abc*def^/g*-h*+"); + assert(infix_to_postfix("A*(B+C)") == "ABC+*"); + assert(infix_to_postfix("A+B*C") == "ABC*+"); + assert(infix_to_postfix("((A+B)*C)-D^E^F") == "AB+C*DEF^^-"); + + // Infix to Prefix + assert(infix_to_prefix("(a-b/c)*(a/k-l)") == "*-a/bc-/akl"); + + // Prefix to Infix + assert(prefix_to_infix("*-A/BC-/AKL") == "((A-(B/C))*((A/K)-L))"); + + std::cout << "All tests have successfully passed!\n"; +} + +/** + * @brief Main driver + */ +int main() { + test(); + return 0; +} \ No newline at end of file diff --git a/strings/infix_to_postfix.cpp b/strings/infix_to_postfix.cpp deleted file mode 100644 index 3f67651338..0000000000 --- a/strings/infix_to_postfix.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/** - * @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 /// for assert -#include /// for isalnum, isspace -#include /// for IO operations -#include /// for std::stack -#include /// 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 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; -} \ No newline at end of file diff --git a/strings/infix_to_prefix.cpp b/strings/infix_to_prefix.cpp deleted file mode 100644 index df0f6a7fb1..0000000000 --- a/strings/infix_to_prefix.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/** - * @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 /// for std::reverse -#include /// for assert -#include /// for isalnum -#include /// for std::cout -#include /// for std::stack -#include /// 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 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; -} \ No newline at end of file diff --git a/strings/prefix_to_infix.cpp b/strings/prefix_to_infix.cpp deleted file mode 100644 index 018d3e43df..0000000000 --- a/strings/prefix_to_infix.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/** - * @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 /// for assert -#include /// for isalnum -#include /// for std::cout -#include /// for std::stack -#include /// 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 stk; - - for (int i = static_cast(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; -} \ No newline at end of file