adds tests

This commit is contained in:
JOLIMAITRE Matthieu 2024-05-21 05:51:28 +02:00
parent 23529950e9
commit d93ab67edc
11 changed files with 276 additions and 27 deletions

View file

@ -1,3 +1,4 @@
from dataclasses import dataclass
from typing import Generic, Optional, TypeVar from typing import Generic, Optional, TypeVar
from .transformer import Transformer from .transformer import Transformer
@ -6,6 +7,7 @@ from .parser import Parser
P = TypeVar("P") P = TypeVar("P")
@dataclass
class FutureTransform(Transformer[P]): class FutureTransform(Transformer[P]):
actual: Optional[Transformer[P]] actual: Optional[Transformer[P]]
def __init__(self) -> None: def __init__(self) -> None:
@ -24,16 +26,16 @@ class FutureTransform(Transformer[P]):
P = TypeVar("P") P = TypeVar("P")
class Declare(Generic[P]): class Declare(Generic[P]):
parser: Parser[P] parser_: Parser[P]
transform: FutureTransform[P] transform: FutureTransform[P]
def __init__(self) -> None: def __init__(self) -> None:
transform = FutureTransform[P]() transform = FutureTransform[P]()
self.parser = Parser(transform) self.parser_ = Parser(transform)
self.transform = transform self.transform = transform
def p(self): def parser(self):
return self.parser return self.parser_
def define(self, parser: Parser[P]): def define(self, parser: Parser[P]):
self.transform.define(parser.inner) self.transform.define(parser.inner)

View file

@ -9,9 +9,8 @@ L = TypeVar("L")
R = TypeVar("R") R = TypeVar("R")
@dataclass @dataclass
class AndTransform(Transformer[tuple[L, R]]): class AndTransform(Transformer[tuple[L, R]]):
def __init__(self, left: Transformer[L], right: Transformer[R]): left: Transformer[L]
self.left = left right: Transformer[R]
self.right = right
def parse(self, stream: str, at_index: int) -> Result[tuple[L, R]]: def parse(self, stream: str, at_index: int) -> Result[tuple[L, R]]:
result_left = self.left.parse(stream, at_index) result_left = self.left.parse(stream, at_index)
@ -21,3 +20,26 @@ class AndTransform(Transformer[tuple[L, R]]):
if isinstance(result_right, Failure): if isinstance(result_right, Failure):
return result_right return result_right
return Success((result_left.value, result_right.value), result_right.next_index) 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"]))

View file

@ -12,4 +12,26 @@ class EndTransform(Transformer[None]):
def parse(self, stream: str, at_index: int) -> Result[None]: def parse(self, stream: str, at_index: int) -> Result[None]:
if len(stream) == at_index: if len(stream) == at_index:
return Success(None, at_index) return Success(None, at_index)
return Failure(at_index, set("<end>")) return Failure(at_index, set(["<end>"]))
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(["<end>"]))

View file

@ -6,14 +6,33 @@ from ..transformer import Transformer
@dataclass @dataclass
class JustTransform(Transformer[str]): class JustTransform(Transformer[str]):
def __init__(self, word: str): word: str
self.word = word
self.word_len = len(self.word)
def parse(self, stream: str, at_index: int) -> Result[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] prefix = stream[at_index:end_index]
if prefix == self.word: if prefix == self.word:
return Success(self.word, end_index) return Success(self.word, end_index)
return Failure(at_index, set([self.word])) 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"]))

View file

@ -8,8 +8,7 @@ from ..transformer import Transformer
T = TypeVar("T") T = TypeVar("T")
@dataclass @dataclass
class ListTransform(Transformer[list[T]]): class ListTransform(Transformer[list[T]]):
def __init__(self, item: Transformer[T]): item: Transformer[T]
self.item = item
def parse(self, stream: str, at_index: int) -> Result[list[T]]: def parse(self, stream: str, at_index: int) -> Result[list[T]]:
values = list[T]() values = list[T]()
@ -22,3 +21,26 @@ class ListTransform(Transformer[list[T]]):
raise Exception("Parsing empty patterns repeatedly.") raise Exception("Parsing empty patterns repeatedly.")
at_index = result.next_index at_index = result.next_index
return Success(values, at_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)

View file

@ -1,5 +1,5 @@
from dataclasses import dataclass from dataclasses import dataclass
from typing import Callable, TypeVar from typing import Callable, Generic, TypeVar
from ..result import Result, Success, Failure from ..result import Result, Success, Failure
from ..transformer import Transformer from ..transformer import Transformer
@ -8,8 +8,10 @@ from ..transformer import Transformer
I = TypeVar("I") I = TypeVar("I")
O = TypeVar("O") O = TypeVar("O")
@dataclass @dataclass
class MapTransform(Transformer[O]): class MapTransform(Generic[I, O], Transformer[O]):
def __init__(self, value: Transformer[I], transform: Callable[[I], O]): value: Transformer[I]
def __init__(self, value: Transformer[I], transform: Callable[[I], O]) -> None:
self.value = value self.value = value
self.transform = transform self.transform = transform
@ -19,3 +21,26 @@ class MapTransform(Transformer[O]):
return result return result
transformed = self.transform(result.value) transformed = self.transform(result.value)
return Success(transformed, result.next_index) 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"]))

View file

@ -8,11 +8,39 @@ from ..transformer import Transformer
T = TypeVar("T") T = TypeVar("T")
@dataclass @dataclass
class OptionTransform(Transformer[Optional[T]]): class OptionTransform(Transformer[Optional[T]]):
def __init__(self, value: Transformer[T]): value: Transformer[T]
self.value = value
def parse(self, stream: str, at_index: int) -> Result[Optional[T]]: def parse(self, stream: str, at_index: int) -> Result[Optional[T]]:
result = self.value.parse(stream, at_index) result = self.value.parse(stream, at_index)
if isinstance(result, Success): if isinstance(result, Success):
return Success(result.value, result.next_index) return Success(result.value, result.next_index)
return Success(None, at_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)

View file

@ -9,9 +9,8 @@ L = TypeVar("L")
R = TypeVar("R") R = TypeVar("R")
@dataclass @dataclass
class OrTransform(Transformer[Union[L, R]]): class OrTransform(Transformer[Union[L, R]]):
def __init__(self, left: Transformer[L], right: Transformer[R]): left: Transformer[L]
self.left = left right: Transformer[R]
self.right = right
def parse(self, stream: str, at_index: int) -> Result[Union[L, R]]: def parse(self, stream: str, at_index: int) -> Result[Union[L, R]]:
result_left = self.left.parse(stream, at_index) result_left = self.left.parse(stream, at_index)
@ -21,3 +20,32 @@ class OrTransform(Transformer[Union[L, R]]):
if isinstance(result_right, Success): if isinstance(result_right, Success):
return Success(result_right.value, result_right.next_index) return Success(result_right.value, result_right.next_index)
return Failure(at_index, result_left.expected.union(result_right.expected)) 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"]))

View file

@ -7,13 +7,40 @@ from ..transformer import Transformer
@dataclass @dataclass
class RegexTransform(Transformer[str]): class RegexTransform(Transformer[str]):
def __init__(self, pattern: str): pattern: str
self.pattern = pattern
def parse(self, stream: str, at_index: int) -> Result[str]: def parse(self, stream: str, at_index: int) -> Result[str]:
do_match = re.match(self.pattern, stream[at_index:]) do_match = re.match(self.pattern, stream[at_index:])
if do_match is None: if do_match is None:
return Failure(at_index, set(f"<matching '{self.pattern}'>")) return Failure(at_index, set([f"<matching '{self.pattern}'>"]))
else: else:
match = do_match[0] match = do_match[0]
return Success(match, at_index + len(match)) 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(["<matching 'arbre'>"]))
@test()
def test_regex_some(ctx):
parser = RegexTransform("[0-3]+")
input = "012345"
assert parser.parse(input, 0) == Success("0123", 4)

View file

@ -1,5 +1,5 @@
from dataclasses import dataclass from dataclasses import dataclass
from typing import Optional, TypeVar from typing import Generic, Optional, TypeVar
from ..result import Result from ..result import Result
from ..transformer import Transformer from ..transformer import Transformer
@ -13,7 +13,10 @@ from .list import ListTransform
T = TypeVar("T") T = TypeVar("T")
S = TypeVar("S") S = TypeVar("S")
@dataclass @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]): def __init__(self, items: Transformer[T], sep: Transformer[S]):
self.items = items self.items = items
self.sep = sep self.sep = sep
@ -33,3 +36,33 @@ class SepListTransform(Transformer[list[T]]):
for (_sep, item) in value[1]: for (_sep, item) in value[1]:
items.append(item) items.append(item)
return items 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)

View file

@ -11,8 +11,29 @@ I = TypeVar("I")
T = TypeVar("T") T = TypeVar("T")
@dataclass @dataclass
class ValueTransform(Transformer[T]): class ValueTransform(Transformer[T]):
value: T
def __init__(self, ignored: Transformer[I], value: T): def __init__(self, ignored: Transformer[I], value: T):
self.value = value
self.actual = MapTransform(ignored, lambda _: value) self.actual = MapTransform(ignored, lambda _: value)
def parse(self, stream: str, at_index: int) -> Result[T]: def parse(self, stream: str, at_index: int) -> Result[T]:
return self.actual.parse(stream, at_index) 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"]))