diff --git a/src/pyalibert/forward.py b/src/pyalibert/forward.py index 9e2e7a7..9742283 100644 --- a/src/pyalibert/forward.py +++ b/src/pyalibert/forward.py @@ -1,3 +1,4 @@ +from dataclasses import dataclass from typing import Generic, Optional, TypeVar from .transformer import Transformer @@ -6,6 +7,7 @@ from .parser import Parser P = TypeVar("P") +@dataclass class FutureTransform(Transformer[P]): actual: Optional[Transformer[P]] def __init__(self) -> None: @@ -24,16 +26,16 @@ class FutureTransform(Transformer[P]): P = TypeVar("P") class Declare(Generic[P]): - parser: Parser[P] + parser_: Parser[P] transform: FutureTransform[P] def __init__(self) -> None: transform = FutureTransform[P]() - self.parser = Parser(transform) + self.parser_ = Parser(transform) self.transform = transform - def p(self): - return self.parser + def parser(self): + return self.parser_ def define(self, parser: Parser[P]): self.transform.define(parser.inner) diff --git a/src/pyalibert/transformers/and_.py b/src/pyalibert/transformers/and_.py index 4b7dca2..08381dd 100644 --- a/src/pyalibert/transformers/and_.py +++ b/src/pyalibert/transformers/and_.py @@ -9,9 +9,8 @@ L = TypeVar("L") R = TypeVar("R") @dataclass class AndTransform(Transformer[tuple[L, R]]): - def __init__(self, left: Transformer[L], right: Transformer[R]): - self.left = left - self.right = right + left: Transformer[L] + right: Transformer[R] def parse(self, stream: str, at_index: int) -> Result[tuple[L, R]]: result_left = self.left.parse(stream, at_index) @@ -21,3 +20,26 @@ class AndTransform(Transformer[tuple[L, R]]): if isinstance(result_right, Failure): return result_right return Success((result_left.value, result_right.value), result_right.next_index) + + +from okipy.lib import test +from .just import JustTransform + + +@test() +def test_and_simple(ctx): + parser = AndTransform(JustTransform("ar"), JustTransform("bre")) + input = "arbre" + assert parser.parse(input, 0) == Success(("ar", "bre"), 5) + +@test() +def test_and_shifted(ctx): + parser = AndTransform(JustTransform("ar"), JustTransform("bre")) + input = "..arbre..." + assert parser.parse(input, 2) == Success(("ar", "bre"), 7) + +@test() +def test_and_none(ctx): + parser = AndTransform(JustTransform("ar"), JustTransform("bre")) + input = "......" + assert parser.parse(input, 2) == Failure(2, set(["ar"])) diff --git a/src/pyalibert/transformers/end.py b/src/pyalibert/transformers/end.py index cbab0bf..7f083f4 100644 --- a/src/pyalibert/transformers/end.py +++ b/src/pyalibert/transformers/end.py @@ -12,4 +12,26 @@ class EndTransform(Transformer[None]): def parse(self, stream: str, at_index: int) -> Result[None]: if len(stream) == at_index: return Success(None, at_index) - return Failure(at_index, set("")) + return Failure(at_index, set([""])) + + +from okipy.lib import test + + +@test() +def test_end_simple(ctx): + parser = EndTransform() + input = "" + assert parser.parse(input, 0) == Success(None, 0) + +@test() +def test_end_shifted(ctx): + parser = EndTransform() + input = ".." + assert parser.parse(input, 2) == Success(None, 2) + +@test() +def test_end_fails(ctx): + parser = EndTransform() + input = "arbre......" + assert parser.parse(input, 2) == Failure(2, set([""])) diff --git a/src/pyalibert/transformers/just.py b/src/pyalibert/transformers/just.py index b0b462b..5d66eed 100644 --- a/src/pyalibert/transformers/just.py +++ b/src/pyalibert/transformers/just.py @@ -6,14 +6,33 @@ from ..transformer import Transformer @dataclass class JustTransform(Transformer[str]): - def __init__(self, word: str): - self.word = word - self.word_len = len(self.word) + word: str def parse(self, stream: str, at_index: int) -> Result[str]: - end_index = at_index + self.word_len + end_index = at_index + len(self.word) prefix = stream[at_index:end_index] if prefix == self.word: return Success(self.word, end_index) return Failure(at_index, set([self.word])) + +from okipy.lib import test + + +@test() +def test_just_simple(ctx): + parser = JustTransform("arbre") + input = "arbre" + assert parser.parse(input, 0) == Success("arbre", 5) + +@test() +def test_just_shifted(ctx): + parser = JustTransform("arbre") + input = "..arbre..." + assert parser.parse(input, 2) == Success("arbre", 7) + +@test() +def test_just_fails(ctx): + parser = JustTransform("arbre") + input = "arbre......" + assert parser.parse(input, 2) == Failure(2, set(["arbre"])) diff --git a/src/pyalibert/transformers/list.py b/src/pyalibert/transformers/list.py index b9e870c..0171c7d 100644 --- a/src/pyalibert/transformers/list.py +++ b/src/pyalibert/transformers/list.py @@ -8,8 +8,7 @@ from ..transformer import Transformer T = TypeVar("T") @dataclass class ListTransform(Transformer[list[T]]): - def __init__(self, item: Transformer[T]): - self.item = item + item: Transformer[T] def parse(self, stream: str, at_index: int) -> Result[list[T]]: values = list[T]() @@ -22,3 +21,26 @@ class ListTransform(Transformer[list[T]]): raise Exception("Parsing empty patterns repeatedly.") at_index = result.next_index return Success(values, at_index) + + +from okipy.lib import test +from .just import JustTransform + + +@test() +def test_list_simple(ctx): + parser = ListTransform(JustTransform("arbre")) + input = "arbrearbrearbre" + assert parser.parse(input, 0) == Success(["arbre", "arbre", "arbre"], 15) + +@test() +def test_list_shifted(ctx): + parser = ListTransform(JustTransform("arbre")) + input = "..arbrearbre" + assert parser.parse(input, 2) == Success(["arbre", "arbre"], 12) + +@test() +def test_list_none(ctx): + parser = ListTransform(JustTransform("arbre")) + input = "......" + assert parser.parse(input, 2) == Success([], 2) diff --git a/src/pyalibert/transformers/map.py b/src/pyalibert/transformers/map.py index 37b0e03..ef9c87f 100644 --- a/src/pyalibert/transformers/map.py +++ b/src/pyalibert/transformers/map.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import Callable, TypeVar +from typing import Callable, Generic, TypeVar from ..result import Result, Success, Failure from ..transformer import Transformer @@ -8,8 +8,10 @@ from ..transformer import Transformer I = TypeVar("I") O = TypeVar("O") @dataclass -class MapTransform(Transformer[O]): - def __init__(self, value: Transformer[I], transform: Callable[[I], O]): +class MapTransform(Generic[I, O], Transformer[O]): + value: Transformer[I] + + def __init__(self, value: Transformer[I], transform: Callable[[I], O]) -> None: self.value = value self.transform = transform @@ -19,3 +21,26 @@ class MapTransform(Transformer[O]): return result transformed = self.transform(result.value) return Success(transformed, result.next_index) + + +from okipy.lib import test +from .just import JustTransform + + +@test() +def test_map_simple(ctx): + parser = MapTransform(JustTransform("arbre"), lambda a: (9, a)) + input = "arbre" + assert parser.parse(input, 0) == Success((9, "arbre"), 5) + +@test() +def test_map_shifted(ctx): + parser = MapTransform(JustTransform("arbre"), lambda a: (9, a)) + input = "..arbre....." + assert parser.parse(input, 2) == Success((9, "arbre"), 7) + +@test() +def test_map_failure(ctx): + parser = MapTransform(JustTransform("arbre"), lambda a: (9, a)) + input = "......" + assert parser.parse(input, 2) == Failure(2, set(["arbre"])) diff --git a/src/pyalibert/transformers/option.py b/src/pyalibert/transformers/option.py index 679fced..6652bf1 100644 --- a/src/pyalibert/transformers/option.py +++ b/src/pyalibert/transformers/option.py @@ -8,11 +8,39 @@ from ..transformer import Transformer T = TypeVar("T") @dataclass class OptionTransform(Transformer[Optional[T]]): - def __init__(self, value: Transformer[T]): - self.value = value + value: Transformer[T] def parse(self, stream: str, at_index: int) -> Result[Optional[T]]: result = self.value.parse(stream, at_index) if isinstance(result, Success): return Success(result.value, result.next_index) return Success(None, at_index) + + +from okipy.lib import test +from .just import JustTransform + + +@test() +def test_opt_simple(ctx): + parser = OptionTransform(JustTransform("arbre")) + input = "arbre" + assert parser.parse(input, 0) == Success("arbre", 5) + +@test() +def test_opt_none(ctx): + parser = OptionTransform(JustTransform("arbre")) + input = "brear" + assert parser.parse(input, 0) == Success(None, 0) + +@test() +def test_opt_shifted(ctx): + parser = OptionTransform(JustTransform("arbre")) + input = "..arbre..." + assert parser.parse(input, 2) == Success("arbre", 7) + +@test() +def test_opt_none_shifted(ctx): + parser = OptionTransform(JustTransform("arbre")) + input = "..rbrea..." + assert parser.parse(input, 2) == Success(None, 2) diff --git a/src/pyalibert/transformers/or_.py b/src/pyalibert/transformers/or_.py index 91ec158..59254c0 100644 --- a/src/pyalibert/transformers/or_.py +++ b/src/pyalibert/transformers/or_.py @@ -9,9 +9,8 @@ L = TypeVar("L") R = TypeVar("R") @dataclass class OrTransform(Transformer[Union[L, R]]): - def __init__(self, left: Transformer[L], right: Transformer[R]): - self.left = left - self.right = right + left: Transformer[L] + right: Transformer[R] def parse(self, stream: str, at_index: int) -> Result[Union[L, R]]: result_left = self.left.parse(stream, at_index) @@ -21,3 +20,32 @@ class OrTransform(Transformer[Union[L, R]]): if isinstance(result_right, Success): return Success(result_right.value, result_right.next_index) return Failure(at_index, result_left.expected.union(result_right.expected)) + + +from okipy.lib import test +from .just import JustTransform + + +@test() +def test_or_simple(ctx): + parser = OrTransform(JustTransform("ar"), JustTransform("bre")) + input = "arbre" + assert parser.parse(input, 0) == Success("ar", 2) + +@test() +def test_or_other(ctx): + parser = OrTransform(JustTransform("ar"), JustTransform("bre")) + input = "brear" + assert parser.parse(input, 0) == Success("bre", 3) + +@test() +def test_or_shifted(ctx): + parser = OrTransform(JustTransform("ar"), JustTransform("bre")) + input = "..arbre..." + assert parser.parse(input, 2) == Success("ar", 4) + +@test() +def test_or_fail(ctx): + parser = OrTransform(JustTransform("ar"), JustTransform("bre")) + input = "......" + assert parser.parse(input, 2) == Failure(2, set(["ar", "bre"])) diff --git a/src/pyalibert/transformers/regex.py b/src/pyalibert/transformers/regex.py index f55cbb2..a4952e5 100644 --- a/src/pyalibert/transformers/regex.py +++ b/src/pyalibert/transformers/regex.py @@ -7,13 +7,40 @@ from ..transformer import Transformer @dataclass class RegexTransform(Transformer[str]): - def __init__(self, pattern: str): - self.pattern = pattern + pattern: str def parse(self, stream: str, at_index: int) -> Result[str]: do_match = re.match(self.pattern, stream[at_index:]) if do_match is None: - return Failure(at_index, set(f"")) + return Failure(at_index, set([f""])) else: match = do_match[0] return Success(match, at_index + len(match)) + + +from okipy.lib import test + + +@test() +def test_regex_simple(ctx): + parser = RegexTransform("arbre") + input = "arbre" + assert parser.parse(input, 0) == Success("arbre", 5) + +@test() +def test_regex_shifted(ctx): + parser = RegexTransform("arbre") + input = "..arbre..." + assert parser.parse(input, 2) == Success("arbre", 7) + +@test() +def test_regex_fails(ctx): + parser = RegexTransform("arbre") + input = "arbre......" + assert parser.parse(input, 2) == Failure(2, set([""])) + +@test() +def test_regex_some(ctx): + parser = RegexTransform("[0-3]+") + input = "012345" + assert parser.parse(input, 0) == Success("0123", 4) diff --git a/src/pyalibert/transformers/sep_list.py b/src/pyalibert/transformers/sep_list.py index 4c1619a..47e9421 100644 --- a/src/pyalibert/transformers/sep_list.py +++ b/src/pyalibert/transformers/sep_list.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import Optional, TypeVar +from typing import Generic, Optional, TypeVar from ..result import Result from ..transformer import Transformer @@ -13,7 +13,10 @@ from .list import ListTransform T = TypeVar("T") S = TypeVar("S") @dataclass -class SepListTransform(Transformer[list[T]]): +class SepListTransform(Generic[T, S], Transformer[list[T]]): + items: Transformer[T] + sep: Transformer[S] + def __init__(self, items: Transformer[T], sep: Transformer[S]): self.items = items self.sep = sep @@ -33,3 +36,33 @@ class SepListTransform(Transformer[list[T]]): for (_sep, item) in value[1]: items.append(item) return items + + +from okipy.lib import test +from .just import JustTransform +from ..result import Result, Success, Failure + + +@test() +def test_sep_simple(ctx): + parser = SepListTransform(JustTransform("arbre"), JustTransform(",")) + input = "arbre" + assert parser.parse(input, 0) == Success(["arbre"], 5) + +@test() +def test_sep_alt(ctx): + parser = SepListTransform(JustTransform("arbre"), JustTransform(",")) + input = "arbre,arbre" + assert parser.parse(input, 0) == Success(["arbre", "arbre"], 11) + +@test() +def test_sep_none(ctx): + parser = SepListTransform(JustTransform("arbre"), JustTransform(",")) + input = "" + assert parser.parse(input, 2) == Success([], 2) + +@test() +def test_sep_missing_not_failure(ctx): + parser = SepListTransform(JustTransform("arbre"), JustTransform(",")) + input = "arbre," + assert parser.parse(input, 0) == Success(['arbre'], 5) diff --git a/src/pyalibert/transformers/value.py b/src/pyalibert/transformers/value.py index 724e88b..4a90099 100644 --- a/src/pyalibert/transformers/value.py +++ b/src/pyalibert/transformers/value.py @@ -11,8 +11,29 @@ I = TypeVar("I") T = TypeVar("T") @dataclass class ValueTransform(Transformer[T]): + value: T + def __init__(self, ignored: Transformer[I], value: T): + self.value = value self.actual = MapTransform(ignored, lambda _: value) def parse(self, stream: str, at_index: int) -> Result[T]: return self.actual.parse(stream, at_index) + + +from okipy.lib import test +from .just import JustTransform +from ..result import Result, Success, Failure + + +@test() +def test_value_simple(ctx): + parser = ValueTransform(JustTransform("arbre"), "Feur") + input = "arbre" + assert parser.parse(input, 0) == Success("Feur", 5) + +@test() +def test_value_failure(ctx): + parser = ValueTransform(JustTransform("arbre"), lambda a: (9, a)) + input = "......" + assert parser.parse(input, 2) == Failure(2, set(["arbre"]))