Parsing/Shunting-yard algorithm: Difference between revisions
Content added Content deleted
Thundergnat (talk | contribs) (Rename Perl 6 -> Raku, alphabetize, minor clean-up) |
|||
Line 760: | Line 760: | ||
Testing string `123' <enter> |
Testing string `123' <enter> |
||
^C</pre> |
^C</pre> |
||
=={{header|C++}}== |
|||
{{trans|Java}} |
|||
<lang cpp>#include <iostream> |
|||
#include <sstream> |
|||
#include <stack> |
|||
std::string infixToPostfix(const std::string& infix) { |
|||
const std::string ops = "-+/*^"; |
|||
std::stringstream ss; |
|||
std::stack<int> s; |
|||
std::stringstream input(infix); |
|||
std::string token; |
|||
while (std::getline(input, token, ' ')) { |
|||
if (token.empty()) { |
|||
continue; |
|||
} |
|||
char c = token[0]; |
|||
size_t idx = ops.find(c); |
|||
// check for operator |
|||
if (idx != std::string::npos) { |
|||
while (!s.empty()) { |
|||
int prec2 = s.top() / 2; |
|||
int prec1 = idx / 2; |
|||
if (prec2 > prec1 || (prec2 == prec1 && c != '^')) { |
|||
ss << ops[s.top()] << ' '; |
|||
s.pop(); |
|||
} else break; |
|||
} |
|||
s.push(idx); |
|||
} else if (c == '(') { |
|||
s.push(-2); // -2 stands for '(' |
|||
} else if (c == ')') { |
|||
// until '(' on stack, pop operators. |
|||
while (s.top() != -2) { |
|||
ss << ops[s.top()] << ' '; |
|||
s.pop(); |
|||
} |
|||
s.pop(); |
|||
} else { |
|||
ss << token << ' '; |
|||
} |
|||
} |
|||
while (!s.empty()) { |
|||
ss << ops[s.top()] << ' '; |
|||
s.pop(); |
|||
} |
|||
return ss.str(); |
|||
} |
|||
int main() { |
|||
std::string infix = "3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3"; |
|||
std::cout << "infix: " << infix << '\n'; |
|||
std::cout << "postfix: " << infixToPostfix(infix) << '\n'; |
|||
return 0; |
|||
}</lang> |
|||
{{out}} |
|||
<pre>infix: 3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3 |
|||
postfix: 3 4 2 * 1 5 - 2 3 ^ ^ / +</pre> |
|||
=={{header|C sharp}}== |
=={{header|C sharp}}== |
||
Line 915: | Line 850: | ||
pop: stack[ ] out[ 3 4 2 * 1 5 - 2 3 ^ ^ / + ] |
pop: stack[ ] out[ 3 4 2 * 1 5 - 2 3 ^ ^ / + ] |
||
3 4 2 * 1 5 - 2 3 ^ ^ / +</pre> |
3 4 2 * 1 5 - 2 3 ^ ^ / +</pre> |
||
=={{header|C++}}== |
|||
{{trans|Java}} |
|||
<lang cpp>#include <iostream> |
|||
#include <sstream> |
|||
#include <stack> |
|||
std::string infixToPostfix(const std::string& infix) { |
|||
const std::string ops = "-+/*^"; |
|||
std::stringstream ss; |
|||
std::stack<int> s; |
|||
std::stringstream input(infix); |
|||
std::string token; |
|||
while (std::getline(input, token, ' ')) { |
|||
if (token.empty()) { |
|||
continue; |
|||
} |
|||
char c = token[0]; |
|||
size_t idx = ops.find(c); |
|||
// check for operator |
|||
if (idx != std::string::npos) { |
|||
while (!s.empty()) { |
|||
int prec2 = s.top() / 2; |
|||
int prec1 = idx / 2; |
|||
if (prec2 > prec1 || (prec2 == prec1 && c != '^')) { |
|||
ss << ops[s.top()] << ' '; |
|||
s.pop(); |
|||
} else break; |
|||
} |
|||
s.push(idx); |
|||
} else if (c == '(') { |
|||
s.push(-2); // -2 stands for '(' |
|||
} else if (c == ')') { |
|||
// until '(' on stack, pop operators. |
|||
while (s.top() != -2) { |
|||
ss << ops[s.top()] << ' '; |
|||
s.pop(); |
|||
} |
|||
s.pop(); |
|||
} else { |
|||
ss << token << ' '; |
|||
} |
|||
} |
|||
while (!s.empty()) { |
|||
ss << ops[s.top()] << ' '; |
|||
s.pop(); |
|||
} |
|||
return ss.str(); |
|||
} |
|||
int main() { |
|||
std::string infix = "3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3"; |
|||
std::cout << "infix: " << infix << '\n'; |
|||
std::cout << "postfix: " << infixToPostfix(infix) << '\n'; |
|||
return 0; |
|||
}</lang> |
|||
{{out}} |
|||
<pre>infix: 3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3 |
|||
postfix: 3 4 2 * 1 5 - 2 3 ^ ^ / +</pre> |
|||
=={{header|Ceylon}}== |
=={{header|Ceylon}}== |
||
Line 3,048: | Line 3,048: | ||
3 4 2 * 1 5 - 2 3 ^ ^ + reduce / |
3 4 2 * 1 5 - 2 3 ^ ^ + reduce / |
||
3 4 2 * 1 5 - 2 3 ^ ^ / reduce + |
3 4 2 * 1 5 - 2 3 ^ ^ / reduce + |
||
3 4 2 * 1 5 - 2 3 ^ ^ / +</pre> |
|||
=={{header|Perl 6}}== |
|||
<lang perl6>my %prec = |
|||
'^' => 4, |
|||
'*' => 3, |
|||
'/' => 3, |
|||
'+' => 2, |
|||
'-' => 2, |
|||
'(' => 1; |
|||
my %assoc = |
|||
'^' => 'right', |
|||
'*' => 'left', |
|||
'/' => 'left', |
|||
'+' => 'left', |
|||
'-' => 'left'; |
|||
sub shunting-yard ($prog) { |
|||
my @inp = $prog.words; |
|||
my @ops; |
|||
my @res; |
|||
sub report($op) { printf "%25s %-7s %10s %s\n", ~@res, ~@ops, $op, ~@inp } |
|||
sub shift($t) { report( "shift $t"); @ops.push: $t } |
|||
sub reduce($t) { report("reduce $t"); @res.push: $t } |
|||
while @inp { |
|||
given @inp.shift { |
|||
when /\d/ { reduce $_ }; |
|||
when '(' { shift $_ } |
|||
when ')' { while @ops and (my $x = @ops.pop and $x ne '(') { reduce $x } } |
|||
default { |
|||
my $newprec = %prec{$_}; |
|||
while @ops { |
|||
my $oldprec = %prec{@ops[*-1]}; |
|||
last if $newprec > $oldprec; |
|||
last if $newprec == $oldprec and %assoc{$_} eq 'right'; |
|||
reduce @ops.pop; |
|||
} |
|||
shift $_; |
|||
} |
|||
} |
|||
} |
|||
reduce @ops.pop while @ops; |
|||
@res; |
|||
} |
|||
say shunting-yard '3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3';</lang> |
|||
{{out}} |
|||
<pre> reduce 3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3 |
|||
3 shift + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3 |
|||
3 + reduce 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3 |
|||
3 4 + shift * 2 / ( 1 - 5 ) ^ 2 ^ 3 |
|||
3 4 + * reduce 2 / ( 1 - 5 ) ^ 2 ^ 3 |
|||
3 4 2 + reduce * ( 1 - 5 ) ^ 2 ^ 3 |
|||
3 4 2 * + shift / ( 1 - 5 ) ^ 2 ^ 3 |
|||
3 4 2 * + / shift ( 1 - 5 ) ^ 2 ^ 3 |
|||
3 4 2 * + / ( reduce 1 - 5 ) ^ 2 ^ 3 |
|||
3 4 2 * 1 + / ( shift - 5 ) ^ 2 ^ 3 |
|||
3 4 2 * 1 + / ( - reduce 5 ) ^ 2 ^ 3 |
|||
3 4 2 * 1 5 + / ( reduce - ^ 2 ^ 3 |
|||
3 4 2 * 1 5 - + / shift ^ 2 ^ 3 |
|||
3 4 2 * 1 5 - + / ^ reduce 2 ^ 3 |
|||
3 4 2 * 1 5 - 2 + / ^ shift ^ 3 |
|||
3 4 2 * 1 5 - 2 + / ^ ^ reduce 3 |
|||
3 4 2 * 1 5 - 2 3 + / ^ reduce ^ |
|||
3 4 2 * 1 5 - 2 3 ^ + / reduce ^ |
|||
3 4 2 * 1 5 - 2 3 ^ ^ + reduce / |
|||
3 4 2 * 1 5 - 2 3 ^ ^ / reduce + |
|||
3 4 2 * 1 5 - 2 3 ^ ^ / +</pre> |
3 4 2 * 1 5 - 2 3 ^ ^ / +</pre> |
||
Line 3,647: | Line 3,577: | ||
'("3" "4" "2" "*" "1" "5" "-" "2" "3" "^" "^" "/" "+") |
'("3" "4" "2" "*" "1" "5" "-" "2" "3" "^" "^" "/" "+") |
||
</pre> |
</pre> |
||
=={{header|Raku}}== |
|||
(formerly Perl 6) |
|||
<lang perl6>my %prec = |
|||
'^' => 4, |
|||
'*' => 3, |
|||
'/' => 3, |
|||
'+' => 2, |
|||
'-' => 2, |
|||
'(' => 1; |
|||
my %assoc = |
|||
'^' => 'right', |
|||
'*' => 'left', |
|||
'/' => 'left', |
|||
'+' => 'left', |
|||
'-' => 'left'; |
|||
sub shunting-yard ($prog) { |
|||
my @inp = $prog.words; |
|||
my @ops; |
|||
my @res; |
|||
sub report($op) { printf "%25s %-7s %10s %s\n", ~@res, ~@ops, $op, ~@inp } |
|||
sub shift($t) { report( "shift $t"); @ops.push: $t } |
|||
sub reduce($t) { report("reduce $t"); @res.push: $t } |
|||
while @inp { |
|||
given @inp.shift { |
|||
when /\d/ { reduce $_ }; |
|||
when '(' { shift $_ } |
|||
when ')' { while @ops and (my $x = @ops.pop and $x ne '(') { reduce $x } } |
|||
default { |
|||
my $newprec = %prec{$_}; |
|||
while @ops { |
|||
my $oldprec = %prec{@ops[*-1]}; |
|||
last if $newprec > $oldprec; |
|||
last if $newprec == $oldprec and %assoc{$_} eq 'right'; |
|||
reduce @ops.pop; |
|||
} |
|||
shift $_; |
|||
} |
|||
} |
|||
} |
|||
reduce @ops.pop while @ops; |
|||
@res; |
|||
} |
|||
say shunting-yard '3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3';</lang> |
|||
{{out}} |
|||
<pre> reduce 3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3 |
|||
3 shift + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3 |
|||
3 + reduce 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3 |
|||
3 4 + shift * 2 / ( 1 - 5 ) ^ 2 ^ 3 |
|||
3 4 + * reduce 2 / ( 1 - 5 ) ^ 2 ^ 3 |
|||
3 4 2 + reduce * ( 1 - 5 ) ^ 2 ^ 3 |
|||
3 4 2 * + shift / ( 1 - 5 ) ^ 2 ^ 3 |
|||
3 4 2 * + / shift ( 1 - 5 ) ^ 2 ^ 3 |
|||
3 4 2 * + / ( reduce 1 - 5 ) ^ 2 ^ 3 |
|||
3 4 2 * 1 + / ( shift - 5 ) ^ 2 ^ 3 |
|||
3 4 2 * 1 + / ( - reduce 5 ) ^ 2 ^ 3 |
|||
3 4 2 * 1 5 + / ( reduce - ^ 2 ^ 3 |
|||
3 4 2 * 1 5 - + / shift ^ 2 ^ 3 |
|||
3 4 2 * 1 5 - + / ^ reduce 2 ^ 3 |
|||
3 4 2 * 1 5 - 2 + / ^ shift ^ 3 |
|||
3 4 2 * 1 5 - 2 + / ^ ^ reduce 3 |
|||
3 4 2 * 1 5 - 2 3 + / ^ reduce ^ |
|||
3 4 2 * 1 5 - 2 3 ^ + / reduce ^ |
|||
3 4 2 * 1 5 - 2 3 ^ ^ + reduce / |
|||
3 4 2 * 1 5 - 2 3 ^ ^ / reduce + |
|||
3 4 2 * 1 5 - 2 3 ^ ^ / +</pre> |
|||
=={{header|REXX}}== |
=={{header|REXX}}== |
||
Line 4,880: | Line 4,881: | ||
pop: stack[ ] out[ 3 4 2 * 1 5 - 2 3 ^ ^ / + ] |
pop: stack[ ] out[ 3 4 2 * 1 5 - 2 3 ^ ^ / + ] |
||
3 4 2 * 1 5 - 2 3 ^ ^ / +</pre> |
3 4 2 * 1 5 - 2 3 ^ ^ / +</pre> |
||
=={{header|zkl}}== |
|||
{{trans|Go}} |
|||
<lang zkl>var input="3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3"; |
|||
var opa=Dictionary("^",T(4,True), "*",T(3,False), // op:(prec,rAssoc) |
|||
"/",T(3,False), "+",T(2,False), "-",T(2,False), |
|||
); |
|||
"infix: ".println(input); |
|||
"postfix:".println(parseInfix(input)); |
|||
fcn parseInfix(e){ |
|||
stack:=List(); // holds operators and left parenthesis |
|||
rpn:=""; |
|||
foreach tok in (e.split(" ")){ |
|||
switch(tok){ |
|||
case("("){ stack.append(tok) } // push "(" to stack |
|||
case(")"){ |
|||
while(True){ // pop item ("(" or operator) from stack |
|||
op:=stack.pop(); |
|||
if(op=="(") break; // discard "(" |
|||
rpn+=" " + op; // add operator to result |
|||
} |
|||
} |
|||
else{ // default |
|||
o1:=opa.find(tok); // op or Void |
|||
if(o1){ // token is an operator |
|||
while(stack){ |
|||
// consider top item on stack |
|||
op:=stack[-1]; o2:=opa.find(op); |
|||
if(not o2 or o1[0]>o2[0] or |
|||
(o1[0]==o2[0] and o1[1])) break; |
|||
// top item is an operator that needs to come off |
|||
stack.pop(); |
|||
rpn+=" " + op; // add it to result |
|||
} |
|||
// push operator (the new one) to stack |
|||
stack.append(tok); |
|||
}else // token is an operand |
|||
rpn+=(rpn and " " or "") + tok; // add operand to result |
|||
} |
|||
} // switch |
|||
display(tok,rpn,stack); |
|||
} // foreach |
|||
// drain stack to result |
|||
rpn + stack.reverse().concat(" "); |
|||
} |
|||
fcn display(tok,rpn,stack){ |
|||
"Token|".println(tok); |
|||
"Stack|".println(stack.concat()); |
|||
"Queue|".println(rpn); |
|||
println(); |
|||
}</lang> |
|||
{{out}} |
|||
<pre style="height:20ex;overflow:scroll;"> |
|||
infix: 3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3 |
|||
Token|3 |
|||
Stack| |
|||
Queue|3 |
|||
Token|+ |
|||
Stack|+ |
|||
Queue|3 |
|||
Token|4 |
|||
Stack|+ |
|||
Queue|3 4 |
|||
Token|* |
|||
Stack|+* |
|||
Queue|3 4 |
|||
Token|2 |
|||
Stack|+* |
|||
Queue|3 4 2 |
|||
Token|/ |
|||
Stack|+/ |
|||
Queue|3 4 2 * |
|||
Token|( |
|||
Stack|+/( |
|||
Queue|3 4 2 * |
|||
Token|1 |
|||
Stack|+/( |
|||
Queue|3 4 2 * 1 |
|||
Token|- |
|||
Stack|+/(- |
|||
Queue|3 4 2 * 1 |
|||
Token|5 |
|||
Stack|+/(- |
|||
Queue|3 4 2 * 1 5 |
|||
Token|) |
|||
Stack|+/ |
|||
Queue|3 4 2 * 1 5 - |
|||
Token|^ |
|||
Stack|+/^ |
|||
Queue|3 4 2 * 1 5 - |
|||
Token|2 |
|||
Stack|+/^ |
|||
Queue|3 4 2 * 1 5 - 2 |
|||
Token|^ |
|||
Stack|+/^^ |
|||
Queue|3 4 2 * 1 5 - 2 |
|||
Token|3 |
|||
Stack|+/^^ |
|||
Queue|3 4 2 * 1 5 - 2 3 |
|||
postfix:3 4 2 * 1 5 - 2 3^ ^ / + |
|||
</pre> |
|||
=={{header|Xojo}}== |
=={{header|Xojo}}== |
||
Line 5,200: | Line 5,082: | ||
Output: |
Output: |
||
3 4 2 * 1 5 - 2 3 ^ ^ / +</pre> |
3 4 2 * 1 5 - 2 3 ^ ^ / +</pre> |
||
=={{header|zkl}}== |
|||
{{trans|Go}} |
|||
<lang zkl>var input="3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3"; |
|||
var opa=Dictionary("^",T(4,True), "*",T(3,False), // op:(prec,rAssoc) |
|||
"/",T(3,False), "+",T(2,False), "-",T(2,False), |
|||
); |
|||
"infix: ".println(input); |
|||
"postfix:".println(parseInfix(input)); |
|||
fcn parseInfix(e){ |
|||
stack:=List(); // holds operators and left parenthesis |
|||
rpn:=""; |
|||
foreach tok in (e.split(" ")){ |
|||
switch(tok){ |
|||
case("("){ stack.append(tok) } // push "(" to stack |
|||
case(")"){ |
|||
while(True){ // pop item ("(" or operator) from stack |
|||
op:=stack.pop(); |
|||
if(op=="(") break; // discard "(" |
|||
rpn+=" " + op; // add operator to result |
|||
} |
|||
} |
|||
else{ // default |
|||
o1:=opa.find(tok); // op or Void |
|||
if(o1){ // token is an operator |
|||
while(stack){ |
|||
// consider top item on stack |
|||
op:=stack[-1]; o2:=opa.find(op); |
|||
if(not o2 or o1[0]>o2[0] or |
|||
(o1[0]==o2[0] and o1[1])) break; |
|||
// top item is an operator that needs to come off |
|||
stack.pop(); |
|||
rpn+=" " + op; // add it to result |
|||
} |
|||
// push operator (the new one) to stack |
|||
stack.append(tok); |
|||
}else // token is an operand |
|||
rpn+=(rpn and " " or "") + tok; // add operand to result |
|||
} |
|||
} // switch |
|||
display(tok,rpn,stack); |
|||
} // foreach |
|||
// drain stack to result |
|||
rpn + stack.reverse().concat(" "); |
|||
} |
|||
fcn display(tok,rpn,stack){ |
|||
"Token|".println(tok); |
|||
"Stack|".println(stack.concat()); |
|||
"Queue|".println(rpn); |
|||
println(); |
|||
}</lang> |
|||
{{out}} |
|||
<pre style="height:20ex;overflow:scroll;"> |
|||
infix: 3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3 |
|||
Token|3 |
|||
Stack| |
|||
Queue|3 |
|||
Token|+ |
|||
Stack|+ |
|||
Queue|3 |
|||
Token|4 |
|||
Stack|+ |
|||
Queue|3 4 |
|||
Token|* |
|||
Stack|+* |
|||
Queue|3 4 |
|||
Token|2 |
|||
Stack|+* |
|||
Queue|3 4 2 |
|||
Token|/ |
|||
Stack|+/ |
|||
Queue|3 4 2 * |
|||
Token|( |
|||
Stack|+/( |
|||
Queue|3 4 2 * |
|||
Token|1 |
|||
Stack|+/( |
|||
Queue|3 4 2 * 1 |
|||
Token|- |
|||
Stack|+/(- |
|||
Queue|3 4 2 * 1 |
|||
Token|5 |
|||
Stack|+/(- |
|||
Queue|3 4 2 * 1 5 |
|||
Token|) |
|||
Stack|+/ |
|||
Queue|3 4 2 * 1 5 - |
|||
Token|^ |
|||
Stack|+/^ |
|||
Queue|3 4 2 * 1 5 - |
|||
Token|2 |
|||
Stack|+/^ |
|||
Queue|3 4 2 * 1 5 - 2 |
|||
Token|^ |
|||
Stack|+/^^ |
|||
Queue|3 4 2 * 1 5 - 2 |
|||
Token|3 |
|||
Stack|+/^^ |
|||
Queue|3 4 2 * 1 5 - 2 3 |
|||
postfix:3 4 2 * 1 5 - 2 3^ ^ / + |
|||
</pre> |