solution_222e.py

#!/usr/bin/python3
# ====================================================================
# evaluate a list of RPN tokens
# ====================================================================

import sys
from infix_stack import Stack
import user_interface as ui

explanation = '''
Entered a RPN expression as a string. The operands and operators
should be seperated by spaces or commas. For example: '1,2 + /,3'
'''
msg0 = 'error: badly formed RPN expression'
msg1 = 'error: not enough tokens on the stack for binary operator'

# --------------------------------------------------------------------
# ---- convert string to number (int or float)
# --------------------------------------------------------------------

def _number(string):

    tf,n = ui.is_integer(string)

    if tf is True: return(True,n)
    
    tf,n = ui.is_float(string)

    if tf is True: return(True,n)

    return (False,0)

# --------------------------------------------------------------------
# ---- evaluate RPN expression
# --------------------------------------------------------------------

def evaluate_rpn(tokens:list) -> tuple:

    # ---- convert the token list into a stack of operators
    # ---- and operands

    stack = Stack()

    for token in tokens:

        tf,n = _number(token[0])

        if tf:
            stack.push(n)        # operand
            continue
        
        op = token[0]            # operator

        print(f'op={op:<2} stack={stack.as_list()}')

        if op == '+':           # add
            a = stack.pop()
            b = stack.pop()
            stack.push(b+a)
            continue

        if op == '-':           # subtract
            a = stack.pop()
            b = stack.pop()
            stack.push(b-a)
            continue
        
        if op == '*':           # multiply
            a = stack.pop()
            b = stack.pop()
            stack.push(b*a)
            continue

        if op == '/':           # divide         
            a = stack.pop()
            b = stack.pop()
            stack.push(b/a)
            continue
        
        if op == '%':           # modulus (remandier)           
            a = stack.pop()
            b = stack.pop()
            stack.push(b%a)
            continue

        if op == '**':          # power
            a = stack.pop()
            b = stack.pop()
            stack.push(b**a)
            continue

        if op == '//':          # floor           
            a = stack.pop()
            b = stack.pop()
            stack.push(b//a)
            continue

        print()
        print(msg0)
        print(f'error: bad operator ({op})')
        break
            
    # ---- return results

    if stack.is_empty():
        print(msg0)
        print('error: stack is empty - no results')
        return(False,0)

    if stack.length() > 1:
        print(msg0)
        print('error: stack contains more than one value')
        print(f'stack: {stack.as_list()}')
        return(False,0)

    return(True,stack.tos())

# --------------------------------------------------------------------
# ---- main
# --------------------------------------------------------------------

if __name__ == '__main__':

    print(explanation)

    while True:
        
        # ---- ask the user to enter an RPM expression

        print()
        ex = ui.get_user_input('Enter RPN expression: ')

        if not ex: break

        # ---- convert expression string into a list of strings

        strings = ex.replace(',',' ').split()

        if len(strings) == 0:
            print()
            print('Error: empty expression')
            continue

        ##print()
        ##print(f'exp: {strings}')
        ##print()

        # ---- convert a list of strings to a list of tokens
        # ---- (add fake type/precedence)

        tokens = []        
        for tok in strings:
            tokens.append((tok,99))

        # ---- evaluate token list

        tf,value = evaluate_rpn(tokens)

        print()
        print(f'success   = {tf}')
        print(f'RPN value = {value}')