' Translation to Rapid-Q by William Yu

DECLARE SUB pusho (op() AS BYTE, operator$)
DECLARE FUNCTION popn# (ops#() AS DOUBLE) AS DOUBLE
DECLARE FUNCTION popo$ (op() AS BYTE) AS STRING
DECLARE FUNCTION peekat$ (op() AS BYTE) AS STRING
DECLARE SUB pushn (ops#() AS DOUBLE, op# AS DOUBLE)
DECLARE SUB parse (BYREF expression$)
DECLARE FUNCTION evaluate# (expression$) AS DOUBLE

'Programmer: Ian Pitt
'Date: 7/9/1997
'Email: IanPitt@bigfoot.com
'Program Name: The Evaluator
'Purpose: Gets a mathematical expression from the user and parse it then
'         evaluates it.

'NOTE: If you need any explanation contact me at the above email address.
'      Or if you find the code particularly helpful in any way please let
'      me know as it encourages me to do more.

'DEFINT A-Z
CONST False = 0
CONST True = NOT False

'Print welcoming message and instructions
CLS
PRINT "WELCOME TO THE EVALUATOR"
PRINT : PRINT
PRINT "The following are examples of legal expressions:-"
PRINT "   2 + 2                Result is 4"
PRINT "   2 - 2                Result is 0"
PRINT "   2 * 2                Result is 4"
PRINT "   2 / 2                Result is 1"
PRINT "   2 ^ 2                Result is 4  NOTE: ie 2 raised to the power 2"
PRINT "   3+(4*(-3+2*34)+2)^2  Result is 68647"
PRINT
PRINT "You may type 'QUIT' at any time to quit the program"
PRINT


WHILE UCASE$(expression$) <> "QUIT"
  INPUT "Enter expression to evaluate: ", expression$
  'expression$ = "3+(4*(-3+2*34)+2)^2"
  PRINT " Expression = "; expression$
  parse expression$
  PRINT expression$; " After parsing"
  value# = evaluate#(expression$)
  PRINT value#
  'expression$ = "quit"
WEND
END

FUNCTION evaluate# (expression$) AS DOUBLE
  DIM operands#(20) AS DOUBLE, operators(10) AS BYTE

  lexpression = LEN(expression$)
  hierarchy$ = "^*/+-()"

  operands#(0) = 0   'clears operands stack
  operators(0) = 0   'clears operators stack
  'pushn operands#(), 0  'seeds stack with 0

  symbolstart = 1
  DO
    symbolend = INSTR(symbolstart, expression$, " ")
    IF symbolend <= 0 THEN symbolend = lexpression + 1
    symbol$ = UCASE$(MID$(expression$, symbolstart, symbolend - symbolstart))
    symbolpos = INSTR(hierarchy$, symbol$)
    IF symbolpos > 0 THEN  'this is an operator
      symbolonstack$ = peekat$(operators())
      IF INSTR(hierarchy$, symbolonstack$) > symbolpos OR symbolonstack$ = "" OR symbol$ = "(" THEN
        pusho operators(), symbol$
      ELSEIF symbol$ = ")" THEN
        DO
          symbolonstack$ = popo$(operators())
          IF INSTR("^*/+-", symbolonstack$) > 0 THEN
            op2# = popn#(operands#())
            op1# = popn#(operands#())
          END IF
          SELECT CASE symbolonstack$
            CASE "^": pushn operands#(), op1# ^ op2#
            CASE "*": pushn operands#(), op1# * op2#
            CASE "/": pushn operands#(), op1# / op2#
            CASE "+": pushn operands#(), op1# + op2#
            CASE "-": pushn operands#(), op1# - op2#
          END SELECT
        LOOP UNTIL symbolonstack$ = "("
      ELSE
        symbolonstack$ = popo$(operators())
        IF INSTR("^*/+-", symbolonstack$) > 0 THEN
          op2# = popn#(operands#())
          op1# = popn#(operands#())
        END IF
        SELECT CASE symbolonstack$
          CASE "^": pushn operands#(), op1# ^ op2#
          CASE "*": pushn operands#(), op1# * op2#
          CASE "/": pushn operands#(), op1# / op2#
          CASE "+": pushn operands#(), op1# + op2#
          CASE "-": pushn operands#(), op1# - op2#
          CASE "(": pusho operators(), symbol$
        END SELECT
        pusho operators(), symbol$
      END IF
    ELSE
      pushn operands#(), VAL(symbol$)
    END IF
    symbolstart = symbolend + 1
  LOOP UNTIL symbolend >= lexpression
        WHILE operators(0)
          symbolonstack$ = popo$(operators())
          IF INSTR("^*/+-", symbolonstack$) > 0 THEN
            op2# = popn#(operands#())
            op1# = popn#(operands#())
          END IF
          SELECT CASE symbolonstack$
            CASE "^": pushn operands#(), op1# ^ op2#
            CASE "*": pushn operands#(), op1# * op2#
            CASE "/": pushn operands#(), op1# / op2#
            CASE "+": pushn operands#(), op1# + op2#
            CASE "-": pushn operands#(), op1# - op2#
          END SELECT
        WEND
  evaluate# = popn#(operands#())
END FUNCTION

SUB parse (BYREF expression$)
  lexpression = LEN(expression$)
  WasOperator = true
  texpression$ = ""
  FOR ct = 1 TO lexpression
    ch$ = MID$(expression$, ct, 1)
    SELECT CASE ch$
      CASE " "
      CASE "("
        texpression$ = texpression$ + ch$ + " "
        WasOperator = true
      CASE ")"
        texpression$ = texpression$ + " " + ch$
        WasOperator = false
      CASE "+", "-", "*", "/", "^"
        IF NOT WasOperator THEN
          texpression$ = texpression$ + " " + ch$ + " "
        ELSE
          texpression$ = texpression$ + ch$
        END IF
        WasOperator = NOT WasOperator
      CASE ELSE
        texpression$ = texpression$ + ch$
        WasOperator = false
    END SELECT
  NEXT
  expression$ = texpression$
END SUB

FUNCTION peekat$ (op() AS BYTE) AS STRING
  ct = op(0)
  IF ct THEN peekat$ = CHR$(op(ct)) ELSE peekat$ = ""
END FUNCTION

FUNCTION popn# (ops#() AS DOUBLE) AS DOUBLE
  popn# = ops#(ops#(0))
  ops#(0) = ops#(0) - 1
END FUNCTION

FUNCTION popo$ (op() AS BYTE) AS STRING
  ct = op(0)
  popo$ = CHR$(op(ct))
  ct = ct - 1
  op(0) = ct
END FUNCTION

SUB pushn (ops#() AS DOUBLE, op#)
  ops#(0) = ops#(0) + 1
  ops#(ops#(0)) = op#
END SUB

SUB pusho (op() AS BYTE, operator$)
  ct = op(0)
  ct = ct + 1
  op(0) = ct
  op(ct) = ASC(operator$)
END SUB
