'===========================================================================
' Subject: CONVERT INFIX TO POSTFIX (RPN)    Date: 02-15-97 (10:00)       
'  Author: The ABC Programmer                Code: QB, QBasic, PDS        
'  Origin: voxel@freenet.edmonton.ab.ca    Packet: ALGOR.ABC
'===========================================================================
'***************************************************************************
' INFIX TO POSTFIX EXPRESSION CONVERTER programmed by William Yu (02/97)
'
'       Converts Infix:  ((A + 5) / B - 2) * C
'     To Postfix (RPN):  A 5 + B / 2 - C *
'
' Also available in ALGOR.ABC is the RPN (Postfix) evaluator.
' For the stack in this case, only the operators "()+-*/^" are stored
' instead of the operands for the RPN Evaluator.
'
' This program was converted to BASIC by William Yu using a Pascal template
' Referenced from:
'            Larry Nyhoff & Sanford Leestma
'            Data Structures and Program Design in Pascal (2nd Edition)
'            Pages 204-209.
'
' With some ingenuity, you could simply make this to an Infix evaluator.
'***************************************************************************
'DEFINT A-Z
DECLARE SUB ProcessOperator (Operator AS STRING, Stack AS INTEGER)
DECLARE FUNCTION Priority (Operator AS STRING) AS INTEGER
DECLARE SUB Pop (Stack AS INTEGER, Elem AS STRING)
DECLARE SUB ProcessRightParen (Stack AS INTEGER, ErrorCode AS INTEGER)
DECLARE SUB ConvertToRPN (Expression AS STRING)
DECLARE SUB Push (Stack AS INTEGER, Elem AS STRING)

CONST FALSE = 0
CONST TRUE = NOT FALSE
CONST Max = 25            ' Maximum number of operators to be stored
                          ' in the stack at one time.  eg. if you have
                          ' a lot of ((((((((( brackets in your expression
                          ' the stack size should be adjusted.

DIM Element(1 TO Max) AS STRING
DIM Expression AS STRING
DIM S AS STRING

CLS

' NOTE:  2(4 + 5) will not parse properly (include the '*' sign)
'  USE:  2*(4 + 5) instead.

Expression = "(A-B)*(C-(D+E)^F)"    ' Start with the Infix notation

'Expression = "((A+B)"              '<- Here's an error in expression
'Expression = "WowWee*(How+Does^Programming)-This*Work"
'Expression = "((123 + 39^3 * (43 - 89) / 10 - ((90 - 34) * 3)))"

PRINT "  The infix: "; Expression
PRINT "Now Postfix: ";
ConvertToRPN Expression              ' Then get the Postfix (RPN) expression
PRINT S

END

SUB ConvertToRPN (Expression AS STRING)
'********************************************************
'   Input:  Infix expression
' Purpose:  Convert Expression to RPN
'  Output:  To screen, the Postfix (RPN) Expression
'    Note:  Uses procedures Priority, ProcessRightParen
'           ProcessOperator, and stack-processing
'********************************************************

DIM Token AS STRING

  Stack = 0               ' Initialize an empty stack
  ErrorCode = FALSE
  Signs$ = " ()+-*/^"

  I = 1
  Token = LEFT$(Expression, 1)

  WHILE (ErrorCode = FALSE) AND (I <= LEN(Expression))
    Valid = INSTR(Signs$, Token)
    IF Valid THEN
      'PRINT " ";
      SELECT CASE Token
        CASE " "                       '{* Skip Blanks and do nothing *}
        CASE "("                       '{* Left parenthesis           *}
          Push Stack, Token
          Stack = STACK.INT(0)
          'LOCATE , POS(CSRLIN) - 1     '{* Just to make it look nice  *}
        CASE ")"                       '{* Right parenthesis          *}
          ProcessRightParen Stack, ErrorCode
          Stack = STACK.INT(0)
          ErrorCode = STACK.INT(1)
        CASE "+", "-", "*", "/", "^"   '{* Arithmetic operator        *}
          ProcessOperator Token, Stack
          Stack = STACK.INT(1)
          S = S + " "
      END SELECT
    ELSE                               '{* Operand *}
      'PRINT Token;
      S = S + Token
    END IF
    I = I + 1                          '{* Get next token and process it *}
    Token = MID$(Expression, I, 1)
  WEND

  '{* Pop and display operators on the stack *}
  WHILE Stack <> 0 AND ErrorCode = False
    Pop Stack, Token
    Stack = STACK.INT(0)
    Token = STACK.STR(1)
    IF Token <> "(" THEN
      'PRINT Token;
      S = S + Token
    ELSE
      ErrorCode = TRUE
    END IF
  WEND

  IF ErrorCode THEN
    PRINT "<<< Error in infix expression >>>"
  END IF

END SUB

SUB Pop (Stack AS INTEGER, Elem AS STRING)
'----------------------------------------------
'  Return the top element on the stack.
'----------------------------------------------

IF Stack = 0 THEN
  PRINT " *** Attempt to pop from an empty stack ***"
ELSE
  Elem = Element(Stack)
  Stack = Stack - 1
END IF

END SUB  '{* Pop *}

FUNCTION Priority (Operator AS STRING) AS INTEGER
'**********************************************************
'   Input:  The Character Operator
' Purpose:  Find the priority of Operator, and arithmetic
'           operator of (.
'  Output:  Returns priority (0 - 2) of Operator.
'**********************************************************
 
  SELECT CASE Operator
    CASE "("            ' Not so great
      Priority = 0
    CASE "+", "-"
      Priority = 1
    CASE "*", "/"
      Priority = 2
    CASE "^"            ' Greatest priority (Exponential)
      Priority = 3
  END SELECT

END FUNCTION

SUB ProcessOperator (Operator AS STRING, Stack AS INTEGER)
'*********************************************************************
'   Input:  A Character denoting an arithmetic Operator, and a
'           stack of operators.
' Purpose:  Process an arithmetic operator.  Operators are popped
'           from the stack until the stack becomes empty or an
'           operator appears on the top of the stack whose priority
'           is less than or equal to that of the Operator.  Operator
'           is then pushed onto the stack.
'  Output:  Modified stack position.
'*********************************************************************

DIM TopOperator AS STRING

  DonePopping = FALSE

  DO
    IF Stack = 0 THEN
      DonePopping = TRUE
    ELSE
      Pop Stack, TopOperator
      Stack = STACK.INT(0)
      TopOperator = STACK.STR(1)
      IF Priority(Operator) <= Priority(TopOperator) THEN
        S = S + TopOperator
      ELSE
        Push Stack, TopOperator
        Stack = STACK.INT(0)
        TopOperator = STACK.STR(1)
        DonePopping = TRUE
      END IF
    END IF
  LOOP UNTIL DonePopping
  Push Stack, Operator
  Stack = STACK.INT(0)

END SUB

SUB ProcessRightParen (Stack AS INTEGER, ErrorCode AS INTEGER)
'******************************************************************
'   Input:  The stack position
' Purpose:  Pop and display operators from Stack until a left
'           parenthesis is on top of the stack; it too is popped,
'           but not displayed.
'  Output:  Modified stack position, and ErrorCode, which is true
'           if the stack becomes empty with no left parenthesis
'           being found.
'******************************************************************

DIM TopToken AS STRING

  DO
     IF Stack = 0 THEN
       ErrorCode = TRUE
     ELSE
       Pop Stack, TopToken
       Stack = STACK.INT(0)
       TopToken = STACK.STR(1)
       IF TopToken <> "(" THEN
         'PRINT TopToken;
         S = S + TopToken
       END IF
     END IF
  LOOP UNTIL TopToken = "(" OR ErrorCode

END SUB

SUB Push (Stack AS INTEGER, Elem AS STRING)
'----------------------------------------------
'  Fill stack with Element
'----------------------------------------------

Stack = Stack + 1
IF Stack > Max THEN
  PRINT " *** Attempt to push on a full stack ***"
ELSE
  Element(Stack) = Elem
END IF

END SUB '{* Push *}
