add concat and examples
This commit is contained in:
parent
78ceb46b2e
commit
d063ebbac6
5 changed files with 158 additions and 3 deletions
89
examples/evalexpr.py
Executable file
89
examples/evalexpr.py
Executable file
|
@ -0,0 +1,89 @@
|
||||||
|
#!/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()
|
|
@ -1,5 +1,5 @@
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Callable, Generic, TypeVar
|
from typing import Callable, Generic, Optional, TypeVar, Union
|
||||||
|
|
||||||
from .result import ParseError, Failure
|
from .result import ParseError, Failure
|
||||||
from .transformer import Transformer
|
from .transformer import Transformer
|
||||||
|
@ -13,7 +13,9 @@ from .transformers import (
|
||||||
SepListTransform,
|
SepListTransform,
|
||||||
OptionTransform,
|
OptionTransform,
|
||||||
MapTransform,
|
MapTransform,
|
||||||
ValueTransform
|
ValueTransform,
|
||||||
|
JoinTransform,
|
||||||
|
ConcatTransform,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -64,6 +66,12 @@ class Parser(Generic[P]):
|
||||||
def sep_by(self, sep: "Parser[T]"):
|
def sep_by(self, sep: "Parser[T]"):
|
||||||
return Parser(SepListTransform(self.inner, sep.inner))
|
return Parser(SepListTransform(self.inner, sep.inner))
|
||||||
|
|
||||||
|
def join(self: "Parser[list[str]]", join: str = ""):
|
||||||
|
return Parser(JoinTransform(self.inner, join))
|
||||||
|
|
||||||
|
def concat(self: "Parser[tuple[str,str]]", join: str = ""):
|
||||||
|
return Parser(ConcatTransform(self.inner, join))
|
||||||
|
|
||||||
# |
|
# |
|
||||||
def __or__(self, other: "Parser[T]"):
|
def __or__(self, other: "Parser[T]"):
|
||||||
return self.or_(other)
|
return self.or_(other)
|
||||||
|
@ -79,3 +87,7 @@ class Parser(Generic[P]):
|
||||||
# <<
|
# <<
|
||||||
def __lshift__(self, other: "Parser[T]"):
|
def __lshift__(self, other: "Parser[T]"):
|
||||||
return self.and_(other).map(lambda v: v[0])
|
return self.and_(other).map(lambda v: v[0])
|
||||||
|
|
||||||
|
# +
|
||||||
|
def __add__(self: "Parser[str]", other: "Parser[str]"):
|
||||||
|
return self.and_(other).concat()
|
||||||
|
|
|
@ -10,4 +10,7 @@ from .sep_list import SepListTransform
|
||||||
from .option import OptionTransform
|
from .option import OptionTransform
|
||||||
|
|
||||||
from .map import MapTransform
|
from .map import MapTransform
|
||||||
from .value import ValueTransform
|
from .value import ValueTransform
|
||||||
|
|
||||||
|
from .concat import ConcatTransform
|
||||||
|
from .join import JoinTransform
|
||||||
|
|
27
src/pyalibert/transformers/concat.py
Normal file
27
src/pyalibert/transformers/concat.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from ..result import Result
|
||||||
|
from ..transformer import Transformer
|
||||||
|
from .map import MapTransform
|
||||||
|
|
||||||
|
|
||||||
|
ConcatInput = Transformer[str] | tuple["ConcatInput", "ConcatInput"]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ConcatTransform(Transformer[str]):
|
||||||
|
from_: Transformer[tuple[str, str]]
|
||||||
|
join: str
|
||||||
|
|
||||||
|
def __init__(self, from_: Transformer[tuple[str, str]], join: str) -> None:
|
||||||
|
self.from_ = from_
|
||||||
|
self.join = join
|
||||||
|
self.actual = MapTransform(self.from_, lambda l: join.join(l))
|
||||||
|
|
||||||
|
def parse(self, stream: str, at_index: int) -> Result[str]:
|
||||||
|
return self.actual.parse(stream, at_index)
|
||||||
|
|
||||||
|
|
||||||
|
from okipy.lib import test
|
||||||
|
|
||||||
|
# TOTEST
|
24
src/pyalibert/transformers/join.py
Normal file
24
src/pyalibert/transformers/join.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from ..result import Result
|
||||||
|
from ..transformer import Transformer
|
||||||
|
from .map import MapTransform
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class JoinTransform(Transformer[str]):
|
||||||
|
items: Transformer[list[str]]
|
||||||
|
join: str
|
||||||
|
|
||||||
|
def __init__(self, items: Transformer[list[str]], join: str) -> None:
|
||||||
|
self.items = items
|
||||||
|
self.join = ""
|
||||||
|
self.actual = MapTransform(items, lambda l: join.join(l))
|
||||||
|
|
||||||
|
def parse(self, stream: str, at_index: int) -> Result[str]:
|
||||||
|
return self.actual.parse(stream, at_index)
|
||||||
|
|
||||||
|
|
||||||
|
from okipy.lib import test
|
||||||
|
|
||||||
|
# TOTEST
|
Loading…
Add table
Add a link
Reference in a new issue