#!/bin/env -S python from dataclasses import dataclass from typing import TypeVar, Union from os.path import dirname import sys sys.path.append(f"{dirname(__file__)}/../src") from pyalibert import regex, Parser, just, Declare from pyalibert.result import ParseError # ast modeling @dataclass class OpAdd: def apply(self, left: float, right: float): return left + right @dataclass class OpSub: def apply(self, left: float, right: float): return left - right @dataclass class OpMul: def apply(self, left: float, right: float): return left * right @dataclass class OpDiv: def apply(self, left: float, right: float): return left / right @dataclass class BinOp: actual: Union[OpAdd, OpSub, OpMul, OpDiv] def apply(self, left: float, right: float): return self.actual.apply(left, right) @dataclass class Value: actual: float @dataclass class Math: first: "Expr" rest: list[tuple[BinOp, "Expr"]] def eval(self) -> float: value = self.first.eval() for (op, oth) in self.rest: value = op.apply(value, oth.eval()) return value @dataclass class Expr: actual: Union[Value, Math] def eval(self) -> float: if isinstance(self.actual, Value): return self.actual.actual else: return self.actual.eval() T = TypeVar("T") def lexeme(p: Parser[T]): return p << just(" ").repeat() none = just('') oper = lexeme(just("+").set(OpAdd()) | just("-").set(OpSub()) | just("*").set(OpMul()) | just("/").set(OpDiv())).map(BinOp) (lbrace, rbrace) = (lexeme(just('(')), lexeme(just(')'))) digit = regex("[0-9]") integer = digit + digit.repeat().join() signed = (just("-") | none) + integer decimal = just(".") + integer value = lexeme(signed + (decimal | none)).map(float).map(Value) math = Declare[Math]() expr = (value | math.parser()).map(Expr) math_inner = (expr & (oper & expr).repeat()).map(lambda r: Math(*r)) math.define(lbrace >> math_inner << rbrace) parser = math_inner def repl(): print("[ New super EvalExpr. ]") print("[ 100% composed and typed ]") print("> ", end="") sys.stdout.flush() for line in sys.stdin: try: result = parser.parse(line.strip()) print(result) print(f"= {result.eval()}") except ParseError as err: print(err) print("> ", end="") sys.stdout.flush() if __name__ == "__main__": repl()