-
-
Notifications
You must be signed in to change notification settings - Fork 7.5k
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
Sreekar-Reddy-D
wants to merge
4
commits into
TheAlgorithms:master
Choose a base branch
from
Sreekar-Reddy-D:feat/expression_conversion
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
e2a437e
feat: add infix↔postfix/prefix expression converters with self-tests
Sreekar-Reddy-D fecb407
Merge branch 'master' into feat/expression_conversion
realstealthninja 4f73564
Refactor: Merge all expression conversions into ExpressionConverter c…
Sreekar-Reddy-D 0b5546d
Resolve merge conflicts
Sreekar-Reddy-D File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
@@ -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 <algorithm> /// for std::reverse | ||||||||
#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. | ||||||||
* @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<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(); // 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()); | ||||||||
Comment on lines
+121
to
+122
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there no way to implement the prefix directly? This repository is for learning so It may be better to include the implementation. |
||||||||
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<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(); | ||||||||
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; | ||||||||
} | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possibly better to use a switch here