Task: Parsing_Shunting-yard algorithm


C++: #include <ciso646>
#include <iostream>
#include <regex>
#include <sstream>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

using std::vector;
using std::string;




#include <exception>
#include <stdexcept>
template <typename...Args> std::runtime_error error( Args...args )
{
  return std::runtime_error( (std::ostringstream{} << ... << args).str() );
};










template <typename T> struct stack : public std::vector <T>
{
  using base_type = std::vector <T> ;
  T        push ( const T& x ) { base_type::push_back( x ); return x; }
  const T& top  ()             { return base_type::back(); }
  T        pop  ()             { T x = std::move( top() ); base_type::pop_back(); return x; }
  bool     empty()             { return base_type::empty(); }
};


using Number = double;






using Operator_Name = string;
using Precedence    = int;
enum class Associates { none, left_to_right, right_to_left };

struct Operator_Info { Precedence precedence; Associates associativity; };

std::unordered_map <Operator_Name, Operator_Info>  Operators =
{
  { "^", { 4, Associates::right_to_left } },
  { "*", { 3, Associates::left_to_right } },
  { "/", { 3, Associates::left_to_right } },
  { "+", { 2, Associates::left_to_right } },
  { "-", { 2, Associates::left_to_right } },
};

Precedence precedence   ( const Operator_Name& op ) { return Operators[ op ].precedence; }
Associates associativity( const Operator_Name& op ) { return Operators[ op ].associativity; }


using Token = string;

bool is_number           ( const Token& t ) { return regex_match( t, std::regex{ R"z((\d+(\.\d*)?|\.\d+)([Ee][\+\-]?\d+)?)z" } ); }
bool is_operator         ( const Token& t ) { return Operators.count( t ); }
bool is_open_parenthesis ( const Token& t ) { return t == "("; }
bool is_close_parenthesis( const Token& t ) { return t == ")"; }
bool is_parenthesis      ( const Token& t ) { return is_open_parenthesis( t ) or is_close_parenthesis( t ); }







template <typename T> std::ostream& operator << ( std::ostream& outs, const std::vector <T> & xs )
{
  std::size_t n = 0;  for (auto x : xs) outs << (n++ ? " " : "") << x;  return outs;
}








#include <iomanip>

struct Progressive_Display
{
  string token_name;
  string token_type;

  Progressive_Display()  
  {
    std::cout << "\n"
      "  INPUT │ TYPE │ ACTION           │ STACK        │ OUTPUT\n"
      "────────┼──────┼──────────────────┼──────────────┼─────────────────────────────\n";
  }

  Progressive_Display& operator () ( const Token& token )  
  {
    token_name = token;
    token_type = is_operator   ( token ) ? "op"
               : is_parenthesis( token ) ? "()"
               : is_number     ( token ) ? "num"
               : "";
    return *this;
  }

  Progressive_Display& operator () (  
    const string         & description,
    const stack  <Token> & stack,
    const vector <Token> & output )
  {
    std::cout                                                              << std::right
      << std::setw(  7 ) << token_name                            << " │ " << std::left
      << std::setw(  4 ) << token_type                            << " │ "
      << std::setw( 16 ) << description                           << " │ "
      << std::setw( 12 ) << (std::ostringstream{} << stack).str() << " │ "
      <<                    output                                << "\n";
    return operator () ( "" );  
  }
};


vector <Token> parse( const vector <Token> & tokens )

{
  vector <Token> output;
  stack  <Token> stack;

  Progressive_Display display;

  for (auto token : tokens)  

    
    if (is_number( token ))
    {
      output.push_back( token );
      display( token )( "num --> output", stack, output );
    }

    
    else if (is_operator( token ) or is_parenthesis( token ))
    {
      display( token );

      if (!is_open_parenthesis( token ))
      {
        
        
        
        
        while (!stack.empty()
          and (   (is_close_parenthesis( token ) and !is_open_parenthesis( stack.top() ))
               or (precedence( stack.top() ) > precedence( token ))
               or (    (precedence( stack.top() ) == precedence( token ))
                   and (associativity( token ) == Associates::left_to_right))))
        {
          output.push_back( stack.pop() );
          display( "pop --> output", stack, output );
        }

        
        if (is_close_parenthesis( token ))
        {
          stack.pop();
          display( "pop", stack, output );
        }
      }

      
      if (!is_close_parenthesis( token ))
      {
        stack.push( token );
        display( "push op", stack, output );
      }
    }

    
    else throw error( "unexpected token: ", token );

  

  display( "END" );
  while (!stack.empty())
  {
    output.push_back( stack.pop() );
    display( "pop --> output", stack, output );
  }

  return output;
}


int main( int argc, char** argv )

try
{
  auto tokens   = vector <Token> ( argv+1, argv+argc );
  auto rpn_expr = parse( tokens );
  std::cout
    << "\nInfix = " << tokens
    << "\nRPN   = " << rpn_expr
    << "\n";
}
catch (std::exception e)
{
  std::cerr << "error: " << e.what() << "\n";
  return 1;
}






Python: from collections import namedtuple
from pprint import pprint as pp

OpInfo = namedtuple('OpInfo', 'prec assoc')
L, R = 'Left Right'.split()

ops = {
 '^': OpInfo(prec=4, assoc=R),
 '*': OpInfo(prec=3, assoc=L),
 '/': OpInfo(prec=3, assoc=L),
 '+': OpInfo(prec=2, assoc=L),
 '-': OpInfo(prec=2, assoc=L),
 '(': OpInfo(prec=9, assoc=L),
 ')': OpInfo(prec=0, assoc=L),
 }

NUM, LPAREN, RPAREN = 'NUMBER ( )'.split()


def get_input(inp = None):
    'Inputs an expression and returns list of (TOKENTYPE, tokenvalue)'
    
    if inp is None:
        inp = input('expression: ')
    tokens = inp.strip().split()
    tokenvals = []
    for token in tokens:
        if token in ops:
            tokenvals.append((token, ops[token]))
        
        
        else:    
            tokenvals.append((NUM, token))
    return tokenvals

def shunting(tokenvals):
    outq, stack = [], []
    table = ['TOKEN,ACTION,RPN OUTPUT,OP STACK,NOTES'.split(',')]
    for token, val in tokenvals:
        note = action = ''
        if token is NUM:
            action = 'Add number to output'
            outq.append(val)
            table.append( (val, action, ' '.join(outq), ' '.join(s[0] for s in stack), note) )
        elif token in ops:
            t1, (p1, a1) = token, val
            v = t1
            note = 'Pop ops from stack to output' 
            while stack:
                t2, (p2, a2) = stack[-1]
                if (a1 == L and p1 <= p2) or (a1 == R and p1 < p2):
                    if t1 != RPAREN:
                        if t2 != LPAREN:
                            stack.pop()
                            action = '(Pop op)'
                            outq.append(t2)
                        else:    
                            break
                    else:        
                        if t2 != LPAREN:
                            stack.pop()
                            action = '(Pop op)'
                            outq.append(t2)
                        else:    
                            stack.pop()
                            action = '(Pop & discard "(")'
                            table.append( (v, action, ' '.join(outq), ' '.join(s[0] for s in stack), note) )
                            break
                    table.append( (v, action, ' '.join(outq), ' '.join(s[0] for s in stack), note) )
                    v = note = ''
                else:
                    note = ''
                    break
                note = '' 
            note = '' 
            if t1 != RPAREN:
                stack.append((token, val))
                action = 'Push op token to stack'
            else:
                action = 'Discard ")"'
            table.append( (v, action, ' '.join(outq), ' '.join(s[0] for s in stack), note) )
    note = 'Drain stack to output'
    while stack:
        v = ''
        t2, (p2, a2) = stack[-1]
        action = '(Pop op)'
        stack.pop()
        outq.append(t2)
        table.append( (v, action, ' '.join(outq), ' '.join(s[0] for s in stack), note) )
        v = note = ''
    return table

if __name__ == '__main__':
    infix = '3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3'
    print( 'For infix expression: %r\n' % infix )
    rp = shunting(get_input(infix))
    maxcolwidths = [len(max(x, key=len)) for x in zip(*rp)]
    row = rp[0]
    print( ' '.join('{cell:^{width}}'.format(width=width, cell=cell) for (width, cell) in zip(maxcolwidths, row)))
    for row in rp[1:]:
        print( ' '.join('{cell:<{width}}'.format(width=width, cell=cell) for (width, cell) in zip(maxcolwidths, row)))

    print('\n The final output RPN is: %r' % rp[-1][2])