Task: Parsing_Shunting-yard algorithm







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])




Fortran:       MODULE COMPILER	
       INTEGER KBD,MSG		

       INTEGER ENUFF		
       PARAMETER (ENUFF = 66)	
       CHARACTER*(ENUFF) RP	
       INTEGER LR		

       INTEGER		OPSYMBOLS		
       PARAMETER	(OPSYMBOLS = 11)	
       TYPE SYMB		
        CHARACTER*1	IS		
        INTEGER*1	PRECEDENCE	
        CHARACTER*48	USAGE		
       END TYPE SYMB		
       TYPE(SYMB) SYMBOL(0:OPSYMBOLS)	
       PARAMETER (SYMBOL =(/	
     o  SYMB(" ", 0,"Not recognised as an operator's symbol."),
     1  SYMB(" ", 1,"separates symbols and aids legibility."),
     2  SYMB(")", 4,"opened with ( to bracket a sub-expression."),
     3  SYMB("]", 4,"opened with [ to bracket a sub-expression."),
     4  SYMB("}", 4,"opened with { to bracket a sub-expression."),
     5  SYMB("+",11,"addition, and unary + to no effect."),
     6  SYMB("-",11,"subtraction, and unary - for neg. numbers."),
     7  SYMB("*",12,"multiplication."),
     8  SYMB("×",12,"multiplication, if you can find this."),
     9  SYMB("/",12,"division."),
     o  SYMB("÷",12,"division for those with a fancy keyboard."),
C                13 is used so that stacked ^ will have lower priority than incoming ^, thus delivering right-to-left evaluation.
     1  SYMB("^",14,"raise to power. Not recognised is **.")/))
       CHARACTER*3	BRAOPEN,BRACLOSE	
       PARAMETER	(BRAOPEN = "([{", BRACLOSE = ")]}")	
       INTEGER		BRALEVEL		
       INTEGER		PRBRA,PRPOW		
       PARAMETER (PRBRA = SYMBOL( 3).PRECEDENCE)	
       PARAMETER (PRPOW = SYMBOL(11).PRECEDENCE)	

       CHARACTER*10 DIGIT		
       PARAMETER (DIGIT = "0123456789")	

       INTEGER STACKLIMIT		
       PARAMETER (STACKLIMIT = 66)	
       TYPE DEFERRED			
        CHARACTER*1	OPC		
        INTEGER*1	PRECEDENCE	
       END TYPE DEFERRED		
       TYPE(DEFERRED) OPSTACK(0:STACKLIMIT)	
       INTEGER OSP			

       INTEGER INCOMING,TOKENTYPE,NOTHING,ANUMBER,OPENBRA,HUH		
       PARAMETER (NOTHING = 0, ANUMBER = -1, OPENBRA = -2, HUH = -3)	
       CONTAINS	
        SUBROUTINE EMIT(STUFF)	
         CHARACTER*(*) STUFF	
         INTEGER L		
          WRITE (MSG,1) STUFF	
    1     FORMAT ("Emit  ",A)	
          IF (STUFF.EQ."") RETURN	
          L = LEN(STUFF)	
          IF (LR + L.GE.ENUFF) STOP "Too much RPN for RP
          IF (LR.GT.0) THEN	
            LR = LR + 1			
            RP(LR:LR) = " "		
          END IF		
          RP(LR + 1:LR + L) = STUFF	
          LR = LR + L			
        END SUBROUTINE EMIT	

        SUBROUTINE STACKOP(C,P)	
         CHARACTER*1 C	
         INTEGER P	
          OSP = OSP + 1		
          IF (OSP.GT.STACKLIMIT) STOP "OSP overtopped
          OPSTACK(OSP).OPC = C		
          OPSTACK(OSP).PRECEDENCE = P	
          WRITE (MSG,1) C,OPSTACK(1:OSP)	
    1     FORMAT ("Stack ",A1,9X,",OpStk=",33(A1,I2:","))
        END SUBROUTINE STACKOP	

        LOGICAL FUNCTION COMPILE(TEXT)	
         CHARACTER*(*) TEXT	
         INTEGER L1,L2		
         CHARACTER*1 C		
         INTEGER HAPPY		
          LR = 0		
          OSP = 0		
          OPSTACK(0).OPC = ""		
          OPSTACK(0).PRECEDENCE = 0	
          BRALEVEL = 0		
          HAPPY = +1		
          L2 = 1		
Chew into an operand, possibly obstructed by an open bracket.
  100     CALL FORASIGN		
          IF (TOKENTYPE.EQ.NOTHING) THEN	
            IF (OSP.GT.0) CALL GRUMP("Another operand or one of "	
     1       //BRAOPEN//" is expected.")				
          ELSE IF (TOKENTYPE.EQ.ANUMBER) THEN	
            CALL EMIT(TEXT(L1:L2 - 1))			
          ELSE IF (TOKENTYPE.EQ.OPENBRA) THEN	
            CALL STACKOP(C,PRBRA - 1)			
            GO TO 100					
C         ELSE IF (TOKENTYPE.EQ.ANAME) THEN	
C           CALL EMIT(TEXT(L1:L2 - 1))			
          ELSE					
            CALL GRUMP("Huh? Unexpected "//C)		
          END IF				
Chase after an operator, possibly interrupted by a close bracket,.
  200     CALL FORASIGN		
          IF (TOKENTYPE.LT.0) THEN	
            CALL GRUMP("Operator expected, not "//C)	
           ELSE			
            WRITE (MSG,201) C,INCOMING,OPSTACK(1:OSP)	
  201       FORMAT ("Oprn=>",A1,"< Prec=",I2,		
     1       ",OpStk=",33(A1,I2:","))			
            DO WHILE(OPSTACK(OSP).PRECEDENCE .GE. INCOMING)	
              IF (OPSTACK(OSP).PRECEDENCE .EQ. PRBRA - 1)		
     1         CALL GRUMP("Unbalanced "//OPSTACK(OSP).OPC)		
              CALL EMIT(OPSTACK(OSP).OPC)				
              OSP = OSP - 1						
            END DO						
            IF (TOKENTYPE.GT.NOTHING) THEN	
              IF (INCOMING.EQ.PRBRA) THEN		
                CALL BALANCEBRA(C)			
                BRALEVEL = BRALEVEL - 1			
                GO TO 200				
               ELSE				
                IF (C.EQ."^") INCOMING = PRPOW - 1	
                CALL STACKOP(C,INCOMING)		
              END IF			
            END IF			
          END IF		
Carry on?
          IF (HAPPY .GT. 0) GO TO 100 	
Completed.
          COMPILE = HAPPY.GE.0	
         CONTAINS	
          SUBROUTINE GRUMP(GROWL)	
           CHARACTER*(*) GROWL	
            WRITE (MSG,1) GROWL	
            IF (L1.GT. 1)        WRITE (MSG,1) "Tasty:",TEXT( 1:L1 - 1)	
            IF (L2.GT.L1)        WRITE (MSG,1) "Nasty:",TEXT(L1:L2 - 1)	
            IF (L2.LE.LEN(TEXT)) WRITE (MSG,1) "Misty:",TEXT(L2:)	
    1       FORMAT (4X,A,1X,A)	
            HAPPY = -1	.	
          END SUBROUTINE GRUMP	

          SUBROUTINE BALANCEBRA(B)	
           CHARACTER*1 B	
           CHARACTER*1 O	
           INTEGER IT,L		
           CHARACTER*88 GROWL	
            O = OPSTACK(OSP).OPC	
            WRITE (MSG,1) O,B		
    1       FORMAT ("Match ",2A)	
            IT = INDEX(BRAOPEN,O)	
            IF (IT .EQ. INDEX(BRACLOSE,B)) THEN	
              OSP = OSP - 1			
             ELSE	
              GROWL = "Unbalanced {[(...)]} bracketing
     1         //B//" is unmatched."				
              IF (IT.GT.0) GROWL(62:) =  "A "//BRACLOSE(IT:IT)	
     1         //" would be better."		
              CALL GRUMP(GROWL)		
            END IF			
          END SUBROUTINE BALANCEBRA	

          SUBROUTINE FORASIGN	
           INTEGER I	
            L1 = L2		
   10       IF (L1.GT.LEN(TEXT)) THEN	
              C = ""			
              L2 = L1			
              TOKENTYPE = NOTHING	
              INCOMING = SYMBOL(1).PRECEDENCE	
              HAPPY = 0			
             ELSE	
Check for spaces and move past them.
              C = TEXT(L1:L1)	
              IF (C.LE." ") THEN	
                L1 = L1 + 1		
                GO TO 10		
              END IF		
Caught something to inspect.
              L2 = L1 + 1		
              IF (INDEX(DIGIT,C).GT.0) THEN	
                TOKENTYPE = ANUMBER 		
   20           IF (L2.LE.LEN(TEXT)) THEN	
                  IF (INDEX(DIGIT,TEXT(L2:L2)).GT.0) THEN	
                    L2 = L2 + 1			
                    GO TO 20			
                  END IF			
                END IF			
C             ELSE IF (INDEX(LETTERS,C).GT.0) THEN	
C               advance L2 while in NAMEISH.
              ELSE IF (INDEX(BRAOPEN,C).GT.0) THEN	
                TOKENTYPE = OPENBRA			
              ELSE			
                DO I = OPSYMBOLS,1,-1	
                  IF (SYMBOL(I).IS .EQ. C) EXIT	
                END DO			
                IF (I.LE.0) THEN	
                  TOKENTYPE = HUH		
                  INCOMING = SYMBOL(0).PRECEDENCE	
                 ELSE			
                  TOKENTYPE = I			
                  INCOMING = SYMBOL(I).PRECEDENCE	
                END IF			
              END IF		
            END IF	
            WRITE (MSG,30) C,INCOMING,TOKENTYPE		
   30       FORMAT ("Next=>",A1,"< Prec=",I2,",Ttype=",I2)	
          END SUBROUTINE FORASIGN	
        END FUNCTION COMPILE	
      END MODULE COMPILER	

      PROGRAM POKE
      USE COMPILER
      CHARACTER*66 TEXT
      LOGICAL HIC
      MSG = 6
      KBD = 5
      WRITE (MSG,1)
    1 FORMAT ("Produce RPN from infix...",/)

   10 WRITE (MSG,11)
   11 FORMAT("Infix: ",$)
      READ(KBD,12) TEXT
   12 FORMAT (A)
      IF (TEXT.EQ."") STOP "Enough."
      HIC = COMPILE(TEXT)
      WRITE (MSG,13) HIC,RP(1:LR)
   13 FORMAT (L6," RPN: >",A,"<")
      GO TO 10
      END