How to resolve the algorithm Parsing/Shunting-yard algorithm step by step in the Python programming language

Published on 12 May 2024 09:40 PM

How to resolve the algorithm Parsing/Shunting-yard algorithm step by step in the Python programming language

Table of Contents

Problem Statement

Given the operator characteristics and input from the Shunting-yard algorithm page and tables, use the algorithm to show the changes in the operator stack and RPN output as each individual token is processed.

Add extra text explaining the actions and an optional comment for the action on receipt of each token.

The handling of functions and arguments is not required.

Let's start with the solution:

Step by Step solution about How to resolve the algorithm Parsing/Shunting-yard algorithm step by step in the Python programming language

The provided Python code takes an infix mathematical expression as input and converts it into an equivalent Reverse Polish Notation (RPN) expression. The key idea behind this conversion is to rearrange the operators and operands in the expression such that the operators appear in the order of their precedence, and operands appear in the same order as in the infix expression. This conversion is useful for evaluating mathematical expressions efficiently using a stack-based approach.

Here's a step-by-step explanation of how this code works:

  1. NamedTuple and Constants:

    • The code defines a named tuple OpInfo to represent the precedence and associativity of operators.
    • It also defines constants L and R to represent left and right associativity, respectively.
  2. Operator Information:

    • The ops dictionary contains information about the precedence and associativity of various operators.
  3. Tokenization:

    • The get_input function takes an infix expression as input and tokenizes it, returning a list of tuples containing the token type (e.g., NUM) and the token value (e.g., the number "3").
  4. Shunting-Yard Algorithm:

    • The shunting function implements the Shunting-yard algorithm to convert the tokenized infix expression into RPN. This algorithm maintains two data structures: an output queue and an operator stack.
    • It iterates through the tokenized expression and processes each token as follows:
      • If the token is a number, it is directly added to the output queue.
      • If the token is an operator, it is compared with operators on the operator stack based on their precedence and associativity. Operators with higher precedence are pushed onto the stack, while operators with lower precedence or equal precedence and left associativity are popped from the stack and added to the output queue.
      • If the token is a left parenthesis, it is pushed onto the operator stack.
      • If the token is a right parenthesis, operators are popped from the stack and added to the output queue until a left parenthesis is encountered. The left parenthesis is then discarded.
    • Once all tokens have been processed, any remaining operators on the stack are popped and added to the output queue.
  5. Formatting and Output:

    • The main part of the code defines an infix expression as a string infix and invokes the get_input function to tokenize this expression.
    • It then calls the shunting function to convert the tokenized expression to RPN.
    • The resulting RPN expression is printed, along with a formatted table that shows the steps involved in the conversion, including the token, action taken, RPN output so far, operator stack contents, and notes explaining the actions.

In summary, this code provides a detailed implementation of the Shunting-yard algorithm to convert infix expressions into RPN, with step-by-step explanations in a formatted table. This conversion is useful for efficiently evaluating mathematical expressions using a stack-based approach.

Source code in the python programming language

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]))
        #elif token in (LPAREN, RPAREN):
        #    tokenvals.append((token, 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])


  

You may also check:How to resolve the algorithm Regular expressions step by step in the MAXScript programming language
You may also check:How to resolve the algorithm Modular inverse step by step in the Maple programming language
You may also check:How to resolve the algorithm Queue/Definition step by step in the FreeBASIC programming language
You may also check:How to resolve the algorithm The Twelve Days of Christmas step by step in the C++ programming language
You may also check:How to resolve the algorithm Bitmap/Histogram step by step in the PHP programming language