Skip to content

Instantly share code, notes, and snippets.

@onelharrison
Last active June 19, 2021 14:05
Show Gist options
  • Save onelharrison/628a4d454da53c7a0bc1940df7d2dc09 to your computer and use it in GitHub Desktop.
Save onelharrison/628a4d454da53c7a0bc1940df7d2dc09 to your computer and use it in GitHub Desktop.
Python script for evaluating expressions in Reverse Polish Notation
#!/usr/bin/env python
import logging
import operator as op
import sys
from typing import Any, List, Union
logging.getLogger(__name__).setLevel("INFO")
supported_operators = {"+": op.add, "-": op.sub, "*": op.mul, "/": op.truediv}
Number = Union[int, float]
def tokenize(expr: str) -> List[str]:
"""Breaks expression `expr` into a list of tokens"""
return expr.split(" ")
def mpop(stack: List[Any], n: int = 1) -> List[Any]:
"""Pops and returns `n` items from a stack. Mutates `stack`"""
return [stack.pop() for _ in range(n)]
def to_num(x: Any) -> Number:
"""Converts a value to a its appropriate numeric type"""
n = float(x)
return int(n) if n.is_integer() else n
def consume_token(token: str, stack: List[Number]) -> List[Number]:
"""Consumes a token given the current stack and returns the updated stack"""
if token in supported_operators:
try:
num1, num2 = mpop(stack, 2)
except IndexError:
logging.error("SyntaxError: Malformed expression")
sys.exit(1)
result = supported_operators[token](num2, num1)
return [*stack, result]
else:
try:
return [*stack, to_num(token)]
except ValueError:
logging.error("SyntaxError: Unsupported token '%s'", token)
sys.exit(1)
def get_result_from_stack(stack: List[Number]) -> Number:
"""Gets the result from `stack`"""
result, *rest = mpop(stack, 1)
if rest:
logging.error("SyntaxError: Found extra tokens")
sys.exit(1)
return result
def evaluate_v1(tokens: List[str]) -> Number:
"""Evaluates a tokenized expression and returns the result"""
stack: List = []
for token in tokens:
stack = consume_token(token, stack)
return get_result_from_stack(stack)
def evaluate_v2(tokens: List[str]) -> Number:
"""Evaluates a tokenized expression and returns the result"""
def _evaluate(tokens: List[str], stack: List) -> Number:
if not tokens:
return get_result_from_stack(stack)
stack = consume_token(tokens[0], stack)
return _evaluate(tokens[1:], stack)
return _evaluate(tokens, [])
if __name__ == "__main__":
# Usage example
#
# echo "1 10 30 + *" | ./rpn_evaluator.py
# echo "1 10 30 + *" | python rpn_evaluator.py
print(evaluate_v2(tokenize(input())))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment