from dataclasses import dataclass,make_dataclass from collections import deque from math import factorial @dataclass class Number: _: int ParenClose = make_dataclass('ParenClose',[]) ParenOpen = make_dataclass('ParenOpen',[]) AddOp = make_dataclass('AddOp',[]) SubOp = make_dataclass('SubOp',[]) MulOp = make_dataclass('MulOp',[]) DivOp = make_dataclass('DivOp',[]) PowOp = make_dataclass('PowOp',[]) NegOp = make_dataclass('NegOp',[]) ModOp = make_dataclass('ModOp',[]) FacOp = make_dataclass('FacOp',[]) AvgOp = make_dataclass('AvgOp',[]) MaxOp = make_dataclass('MaxOp',[]) MinOp = make_dataclass('MinOp',[]) Token = Number | AddOp | SubOp | MulOp | DivOp | PowOp | NegOp | ModOp | FacOp | AvgOp | MaxOp | MinOp | ParenClose | ParenOpen Characters = { '(': ParenOpen, ')': ParenClose, '+': AddOp, '-': SubOp, '*': MulOp, '/': DivOp, '^': PowOp, '~': NegOp, '%': ModOp, '!': FacOp, '@': AvgOp, '$': MaxOp, '&': MinOp } from tokenize import tokenize, untokenize, NUMBER, STRING, NAME, OP from io import BytesIO def Tokenize(s: str) -> [Token]: g = tokenize(BytesIO(s.encode('utf-8')).readline) r = [] for n, v, _, _, _ in g: match (n,v): case (2,x): r.append(Number(float(x))) case (54|60,x) if x in Characters: r.append(Characters[x]()) return r def Expression(e: [Token]) -> (int, [Token]): r, e = Term(e) while len(e) > 0: match e.popleft(): case AddOp(): x, e = Term(e) r += x case SubOp(): x, e = Term(e) r -= x case x: e.insert(0, x) break return r, e def Term(e: [Token]) -> (int, [Token]): r, e = Factor(e) while len(e) > 0: match e.popleft(): case ModOp(): x, e = Factor(e) r %= x case AvgOp(): x, e = Factor(e) r = (x + r) / 2 case MinOp(): x, e = Factor(e) r = min(x, r) case MaxOp(): x, e = Factor(e) r = max(x,r) case MulOp(): x, e = Factor(e) r *= x case DivOp(): x, e = Factor(e) r /= x case PowOp(): x, e = Factor(e) r **= x case x: e.insert(0, x) break return r,e def Factor(e: [Token]) -> (int, [Token]): assert len(e) > 0 match e.popleft(): case NegOp(): r, e = Factor(e) return -r, e case Number(n): if e and isinstance(e[0], FacOp): e.popleft() return float(factorial(int(n))), e return n, e case ParenOpen(): r, e = Expression(e) assert isinstance(e.popleft(), ParenClose) if e and isinstance(e[0], FacOp): e.popleft() r = float(factorial(int(r))) return r, e case e: raise SyntaxError(f"invalid syntax: {e}") # where is my compose Evaluate = lambda x: Expression(deque(Tokenize(x)))[0] if __name__ == '__main__': while True: try: print(Evaluate(input("> "))) except Exception as e: print(e)