'===========================================================================
' 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%)
DECLARE FUNCTION Priority% (Operator AS STRING)
DECLARE SUB Pop (Stack AS INTEGER, Element AS STRING)
DECLARE SUB ProcessRightParen (Stack%, ErrorCode%)
DECLARE SUB ConvertToRPN (Expression AS STRING)
DECLARE SUB Push (Stack AS INTEGER, Element 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 SHARED Element(1 TO Max) AS STRING * 1
DIM Expression 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

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 * 1

  Stack = 0               ' Initialize an empty stack
  ErrorCode = FALSE
  Signs$ = " ()+-*/^"

  I = 1
  Token = LEFT$(Expression, 1)

  WHILE (NOT ErrorCode) AND (I <= LEN(Expression))
    Valid = FALSE
    FOR X = 1 TO LEN(Signs$)
      IF Token = MID$(Signs$, X, 1) THEN Valid = TRUE: EXIT FOR
    NEXT X
    IF Valid THEN
      PRINT " ";
      SELECT CASE Token
        CASE " "                       '{* Skip Blanks and do nothing *}
        CASE "("                       '{* Left parenthesis           *}
          Push Stack, Token
          LOCATE , POS(CSRLIN) - 1     '{* Just to make it look nice  *}
        CASE ")"                       '{* Right parenthesis          *}
          ProcessRightParen Stack, ErrorCode
        CASE "+", "-", "*", "/", "^"   '{* Arithmetic operator        *}
          ProcessOperator Token, Stack
      END SELECT
    ELSE                               '{* Operand *}
      PRINT 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 NOT ErrorCode
    Pop Stack, Token
    IF Token <> "(" THEN
      PRINT Token;
    ELSE
      ErrorCode = TRUE
    END IF
  WEND

  IF ErrorCode THEN
    PRINT "<<< Error in infix expression >>>"
  END IF

END SUB

SUB Pop (Stack AS INTEGER, Element AS STRING)
'----------------------------------------------
'  Return the top element on the stack.
'----------------------------------------------

IF Stack = 0 THEN
  PRINT " *** Attempt to pop from an empty stack ***"
ELSE
  Element = Element(Stack)
  Stack = Stack - 1
END IF

END SUB  '{* Pop *}

FUNCTION Priority% (Operator AS STRING)
'**********************************************************
'   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)
'*********************************************************************
'   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 * 1

  DonePopping = FALSE

  DO
    IF Stack = 0 THEN
      DonePopping = TRUE
    ELSE
      Pop Stack, TopOperator
      IF Priority(Operator) <= Priority(TopOperator) THEN
        PRINT TopOperator;
      ELSE
        Push Stack, TopOperator
        DonePopping = TRUE
      END IF
    END IF
  LOOP UNTIL DonePopping
  Push Stack, Operator

END SUB

SUB ProcessRightParen (Stack, ErrorCode)
'******************************************************************
'   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 * 1

  DO
     IF Stack = 0 THEN
       ErrorCode = TRUE
     ELSE
       Pop Stack, TopToken
       IF TopToken <> "(" THEN PRINT TopToken;
     END IF
  LOOP UNTIL TopToken = "(" OR ErrorCode

END SUB

SUB Push (Stack AS INTEGER, Element AS STRING)
'----------------------------------------------
'  Fill stack with Element
'----------------------------------------------

Stack = Stack + 1
IF Stack > Max THEN
  PRINT " *** Attempt to push on a full stack ***"
ELSE
  Element(Stack) = Element
END IF

END SUB '{* Push *}
