add concat and examples

This commit is contained in:
JOLIMAITRE Matthieu 2024-05-21 07:27:23 +02:00
parent 78ceb46b2e
commit d063ebbac6
5 changed files with 158 additions and 3 deletions

89
examples/evalexpr.py Executable file
View 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()

View file

@ -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()

View file

@ -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

View 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

View 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