def rpn_parse(text: str) -> List[expr.Expr]: """Parse text in reverse Polish notation into a list of expressions (exactly one if the expression is balanced). Example: rpn_parse("5 3 + 4 * 7") => [ Times(Plus(IntConst(5), IntConst(3)), IntConst(4)))), IntConst(7) ] May raise: IndexError (imbalanced expression), lex.LexicalError. """ tokens = lex.TokenStream(io.StringIO(text)) stack = [] while tokens.has_more(): tok = tokens.take() if tok.kind == lex.TokenCat.INT: stack.append(expr.IntConst(int(tok.value))) elif tok.kind in BINOPS: binop_class = BINOPS[tok.kind] right = stack.pop() left = stack.pop() stack.append(binop_class(left, right)) elif tok.kind in UNOPS: unop_class = UNOPS[tok.kind] left = stack.pop() stack.append(unop_class(left)) elif tok.kind == lex.TokenCat.ASSIGN: right = stack.pop() left = stack.pop() # Reverse left and right stack.append(expr.Assign(right, left)) elif tok.kind == lex.TokenCat.VAR: stack.append(expr.Var(tok.value)) else: print('oops') return stack
def calc(text: str): """Read and evaluate a single line formula.""" try: tokens = lex.TokenStream(io.StringIO(text)) stack = [] while tokens.has_more(): tok = tokens.take() if tok.kind == lex.TokenCat.INT: stack.append(expr.IntConst(int(tok.value))) elif tok.kind == lex.TokenCat.PLUS: right = stack.pop() left = stack.pop() stack.append(expr.Plus(left, right)) except lex.LexicalError as e: print(f"*** Lexical error {e}") return except IndexError: # Stack underflow means the expression was imbalanced print(f"*** Imbalanced RPN expression, missing operand at {tok.value}") return if len(stack) == 0: print("(No expression)") else: # For a balanced expression there will be one Expr object # on the stack, but if there are more we'll just print # each of them for exp in stack: print(f"{exp} => {exp.eval()}")
def _primary(stream: TokenStream) -> expr.Expr: """Unary operations, Constants, Variables, input, and parenthesized expressions""" log.debug(f"Parsing primary with starting token {stream.peek()}") token = stream.take() if token.kind is TokenCat.INT: log.debug(f"Returning IntConst node from token {token}") return expr.IntConst(int(token.value)) elif token.kind is TokenCat.VAR: log.debug(f"Variable {token.value}") return expr.Var(token.value) elif token.kind is TokenCat.READ: log.debug("Read") return expr.Read() elif token.kind is TokenCat.ABS: operand = _primary(stream) return expr.Abs(operand) elif token.kind is TokenCat.NEG: operand = _primary(stream) return expr.Neg(operand) elif token.kind is TokenCat.LPAREN: nested = _expr(stream) require(stream, TokenCat.RPAREN, consume=True) return nested else: raise InputError(f"Confused about {token} in expression")
def rpn_parse(text: str) -> List[expr.Expr]: """Parse text in reverse Polish notation into a list of expressions (exactly one if the expression is balanced). Example: rpn_parse("5 3 + 4 * 7") => [ Times(Plus(IntConst(5), IntConst(3)), IntConst(4)))), IntConst(7) ] May raise: IndexError (imbalanced expression), lex.LexicalError. """ BINOPS = { lex.TokenCat.PLUS: expr.Plus, lex.TokenCat.TIMES: expr.Times, lex.TokenCat.DIV: expr.Div, lex.TokenCat.MINUS: expr.Minus } UNOPS = {lex.TokenCat.NEG: expr.Neg, lex.TokenCat.ABS: expr.Abs} tokens = lex.TokenStream(io.StringIO(text)) # print (tokens) stack = [] while tokens.has_more(): tok = tokens.take() if tok.kind == lex.TokenCat.INT: stack.append(expr.IntConst(int(tok.value))) print("int", stack) elif tok.kind in BINOPS: binop_class = BINOPS[tok.kind] right = stack.pop() left = stack.pop() stack.append(binop_class(left, right)) print("binop", stack) elif tok.kind in UNOPS: unop_class = UNOPS[tok.kind] left = stack.pop() stack.append(unop_class(left)) print("unop", stack) elif tok.kind == lex.TokenCat.VAR: stack.append(expr.Var(str(tok.value))) print("var", stack) elif tok.kind == lex.TokenCat.ASSIGN: right = stack.pop() print("right", right) left = stack.pop() print("left", left) # Reverse left and right stack.append(expr.Assign(right, left)) print(stack) return stack