89 lines
2.2 KiB
Python
Executable file
89 lines
2.2 KiB
Python
Executable file
#!/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
|
|
|
|
# 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:
|
|
result = parser.parse(line.strip())
|
|
print(result)
|
|
print(f"= {result.eval()}")
|
|
print("> ", end="")
|
|
sys.stdout.flush()
|
|
|
|
|
|
if __name__ == "__main__": repl()
|