syncraft 0.1.21__tar.gz → 0.1.22__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {syncraft-0.1.21 → syncraft-0.1.22}/PKG-INFO +1 -1
- {syncraft-0.1.21 → syncraft-0.1.22}/pyproject.toml +1 -1
- {syncraft-0.1.21 → syncraft-0.1.22}/syncraft/algebra.py +19 -191
- syncraft-0.1.22/syncraft/ast.py +345 -0
- syncraft-0.1.22/syncraft/constraint.py +189 -0
- {syncraft-0.1.21 → syncraft-0.1.22}/syncraft/diagnostic.py +5 -5
- syncraft-0.1.22/syncraft/finder.py +79 -0
- {syncraft-0.1.21 → syncraft-0.1.22}/syncraft/generator.py +12 -4
- {syncraft-0.1.21 → syncraft-0.1.22}/syncraft/parser.py +6 -3
- {syncraft-0.1.21 → syncraft-0.1.22}/syncraft/syntax.py +35 -41
- {syncraft-0.1.21 → syncraft-0.1.22}/syncraft.egg-info/PKG-INFO +1 -1
- {syncraft-0.1.21 → syncraft-0.1.22}/syncraft.egg-info/SOURCES.txt +1 -1
- {syncraft-0.1.21 → syncraft-0.1.22}/tests/test_bimap.py +29 -143
- {syncraft-0.1.21 → syncraft-0.1.22}/tests/test_parse.py +2 -1
- syncraft-0.1.21/syncraft/ast.py +0 -137
- syncraft-0.1.21/syncraft/cmd.py +0 -61
- syncraft-0.1.21/syncraft/finder.py +0 -149
- {syncraft-0.1.21 → syncraft-0.1.22}/LICENSE +0 -0
- {syncraft-0.1.21 → syncraft-0.1.22}/README.md +0 -0
- {syncraft-0.1.21 → syncraft-0.1.22}/setup.cfg +0 -0
- {syncraft-0.1.21 → syncraft-0.1.22}/syncraft/__init__.py +0 -0
- {syncraft-0.1.21 → syncraft-0.1.22}/syncraft/py.typed +0 -0
- {syncraft-0.1.21 → syncraft-0.1.22}/syncraft/sqlite3.py +0 -0
- {syncraft-0.1.21 → syncraft-0.1.22}/syncraft.egg-info/dependency_links.txt +0 -0
- {syncraft-0.1.21 → syncraft-0.1.22}/syncraft.egg-info/requires.txt +0 -0
- {syncraft-0.1.21 → syncraft-0.1.22}/syncraft.egg-info/top_level.txt +0 -0
- {syncraft-0.1.21 → syncraft-0.1.22}/tests/test_until.py +0 -0
|
@@ -5,200 +5,15 @@ from typing import (
|
|
|
5
5
|
)
|
|
6
6
|
|
|
7
7
|
import traceback
|
|
8
|
-
from dataclasses import dataclass, replace
|
|
8
|
+
from dataclasses import dataclass, replace, asdict
|
|
9
9
|
from weakref import WeakKeyDictionary
|
|
10
10
|
from abc import ABC
|
|
11
|
-
from
|
|
12
|
-
from functools import reduce
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
A = TypeVar('A')
|
|
16
|
-
B = TypeVar('B')
|
|
17
|
-
C = TypeVar('C')
|
|
18
|
-
S = TypeVar('S')
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
@dataclass(frozen=True)
|
|
22
|
-
class Bimap(Generic[S, A, B]):
|
|
23
|
-
forward: Callable[[S, A], Tuple[S, B]]
|
|
24
|
-
inverse: Callable[[S, B], Tuple[S, A]]
|
|
25
|
-
def __rshift__(self, other: Bimap[S, B, C]) -> Bimap[S, A, C]:
|
|
26
|
-
def fwd(s: S, a: A) -> Tuple[S, C]:
|
|
27
|
-
s1, b = self.forward(s, a)
|
|
28
|
-
return other.forward(s1, b)
|
|
29
|
-
def inv(s: S, c: C) -> Tuple[S, A]:
|
|
30
|
-
s1, b = other.inverse(s, c)
|
|
31
|
-
return self.inverse(s1, b)
|
|
32
|
-
return Bimap(
|
|
33
|
-
forward=fwd,
|
|
34
|
-
inverse=inv
|
|
35
|
-
)
|
|
36
|
-
@staticmethod
|
|
37
|
-
def identity()->Bimap[S, A, A]:
|
|
38
|
-
return Bimap(
|
|
39
|
-
forward=lambda s, x: (s, x),
|
|
40
|
-
inverse=lambda s, y: (s, y)
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
@staticmethod
|
|
44
|
-
def when(condition: Callable[..., bool],
|
|
45
|
-
then: Bimap[S, A, B],
|
|
46
|
-
otherwise: Optional[Bimap[S, A, B]] = None) -> Callable[..., Bimap[S, A, B]]:
|
|
47
|
-
def _when(*args:Any, **kwargs:Any) -> Bimap[S, A, B]:
|
|
48
|
-
return then if condition(*args, **kwargs) else (otherwise or Bimap.identity())
|
|
49
|
-
return _when
|
|
11
|
+
from syncraft.ast import ThenKind, ThenResult, ManyResult, OrResult, S
|
|
50
12
|
|
|
13
|
+
A = TypeVar('A') # Result type
|
|
14
|
+
B = TypeVar('B') # Mapped result type
|
|
15
|
+
|
|
51
16
|
|
|
52
|
-
|
|
53
|
-
class StructuralResult:
|
|
54
|
-
def bimap(self,
|
|
55
|
-
before: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity(),
|
|
56
|
-
after: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity()
|
|
57
|
-
) -> Bimap[Any, Any, Any]:
|
|
58
|
-
return Bimap.identity()
|
|
59
|
-
|
|
60
|
-
@dataclass(frozen=True)
|
|
61
|
-
class NamedResult(Generic[A], StructuralResult):
|
|
62
|
-
name: str
|
|
63
|
-
value: A
|
|
64
|
-
def bimap(self,
|
|
65
|
-
before: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity(),
|
|
66
|
-
after: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity()
|
|
67
|
-
) -> Bimap[Any, NamedResult[A], NamedResult[Any]]:
|
|
68
|
-
inner_b = self.value.bimap(before, after) if isinstance(self.value, StructuralResult) else before(self.value) >> after(self.value)
|
|
69
|
-
def fwd(s: S, a: NamedResult[A])-> Tuple[S, NamedResult[Any]]:
|
|
70
|
-
assert a == self, f"Expected {self}, got {a}"
|
|
71
|
-
inner_s, inner_v = inner_b.forward(s, a.value)
|
|
72
|
-
return (inner_s, replace(a, value=inner_v)) if not isinstance(inner_v, NamedResult) else (inner_s, inner_v)
|
|
73
|
-
|
|
74
|
-
def inv(s: S, a: NamedResult[Any]) -> Tuple[S, NamedResult[A]]:
|
|
75
|
-
assert isinstance(a, NamedResult), f"Expected NamedResult, got {type(a)}"
|
|
76
|
-
inner_s, inner_v = inner_b.inverse(s, a.value)
|
|
77
|
-
return (inner_s, replace(self, value=inner_v)) if not isinstance(inner_v, NamedResult) else (inner_s, replace(self, value=inner_v.value))
|
|
78
|
-
ret: Bimap[Any, Any, Any] = Bimap(
|
|
79
|
-
forward=fwd,
|
|
80
|
-
inverse=inv
|
|
81
|
-
)
|
|
82
|
-
return before(self) >> ret >> after(self)
|
|
83
|
-
@dataclass(eq=True, frozen=True)
|
|
84
|
-
class ManyResult(Generic[A], StructuralResult):
|
|
85
|
-
value: Tuple[A, ...]
|
|
86
|
-
def bimap(self,
|
|
87
|
-
before: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity(),
|
|
88
|
-
after: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity()
|
|
89
|
-
) -> Bimap[Any, ManyResult[A], List[A]]:
|
|
90
|
-
# We don't allow zero length ManyResult e.g. many(at_least >= 1) at the syntax level
|
|
91
|
-
# so inner_b has at least 1 element
|
|
92
|
-
assert len(self.value) > 0, "ManyResult must have at least one element"
|
|
93
|
-
inner_b = [v.bimap(before, after) if isinstance(v, StructuralResult) else before(v) >> after(v) for v in self.value]
|
|
94
|
-
def fwd(s: Any, a: ManyResult[A]) -> Tuple[Any, List[A]]:
|
|
95
|
-
assert a == self, f"Expected {self}, got {a}"
|
|
96
|
-
return s, [inner_b[i].forward(s, v)[1] for i, v in enumerate(a.value)]
|
|
97
|
-
|
|
98
|
-
def inv(s: Any, a: List[A]) -> Tuple[Any, ManyResult[A]]:
|
|
99
|
-
assert isinstance(a, list), f"Expected list, got {type(a)}"
|
|
100
|
-
ret = [inner_b[i].inverse(s, v)[1] for i, v in enumerate(a)]
|
|
101
|
-
if len(ret) <= len(inner_b):
|
|
102
|
-
return s, ManyResult(value=tuple(ret))
|
|
103
|
-
else:
|
|
104
|
-
extra = [inner_b[-1].inverse(s, v)[1] for v in a[len(inner_b):]]
|
|
105
|
-
return s, ManyResult(value=tuple(ret + extra))
|
|
106
|
-
|
|
107
|
-
ret = Bimap(
|
|
108
|
-
forward=fwd,
|
|
109
|
-
inverse=inv
|
|
110
|
-
)
|
|
111
|
-
return before(self) >> ret >> after(self)
|
|
112
|
-
|
|
113
|
-
@dataclass(eq=True, frozen=True)
|
|
114
|
-
class OrResult(Generic[A], StructuralResult):
|
|
115
|
-
value: A
|
|
116
|
-
def bimap(self,
|
|
117
|
-
before: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity(),
|
|
118
|
-
after: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity()
|
|
119
|
-
) -> Bimap[Any, OrResult[A], Any]:
|
|
120
|
-
inner_b = self.value.bimap(before, after) if isinstance(self.value, StructuralResult) else before(self.value) >> after(self.value)
|
|
121
|
-
def fwd(s: Any, a: OrResult[A]) -> Tuple[Any, Any]:
|
|
122
|
-
assert a == self, f"Expected {self}, got {a}"
|
|
123
|
-
return inner_b.forward(s, a.value)
|
|
124
|
-
|
|
125
|
-
def inv(s: Any, a: Any) -> Tuple[Any, OrResult[A]]:
|
|
126
|
-
inner_s, inner_v = inner_b.inverse(s, a)
|
|
127
|
-
return inner_s, OrResult(value=inner_v)
|
|
128
|
-
|
|
129
|
-
ret = Bimap(
|
|
130
|
-
forward=fwd,
|
|
131
|
-
inverse=inv
|
|
132
|
-
)
|
|
133
|
-
return before(self) >> ret >> after(self)
|
|
134
|
-
class ThenKind(Enum):
|
|
135
|
-
BOTH = '+'
|
|
136
|
-
LEFT = '//'
|
|
137
|
-
RIGHT = '>>'
|
|
138
|
-
|
|
139
|
-
@dataclass(eq=True, frozen=True)
|
|
140
|
-
class ThenResult(Generic[A, B], StructuralResult):
|
|
141
|
-
kind: ThenKind
|
|
142
|
-
left: A
|
|
143
|
-
right: B
|
|
144
|
-
def arity(self)->int:
|
|
145
|
-
if self.kind == ThenKind.LEFT:
|
|
146
|
-
return self.left.arity() if isinstance(self.left, ThenResult) else 1
|
|
147
|
-
elif self.kind == ThenKind.RIGHT:
|
|
148
|
-
return self.right.arity() if isinstance(self.right, ThenResult) else 1
|
|
149
|
-
elif self.kind == ThenKind.BOTH:
|
|
150
|
-
left_arity = self.left.arity() if isinstance(self.left, ThenResult) else 1
|
|
151
|
-
right_arity = self.right.arity() if isinstance(self.right, ThenResult) else 1
|
|
152
|
-
return left_arity + right_arity
|
|
153
|
-
else:
|
|
154
|
-
return 1
|
|
155
|
-
|
|
156
|
-
def bimap(self,
|
|
157
|
-
before: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity(),
|
|
158
|
-
after: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity()
|
|
159
|
-
) -> Bimap[Any, ThenResult[A, B], Tuple[Any, ...] | Any]:
|
|
160
|
-
kind = self.kind
|
|
161
|
-
lb = self.left.bimap(before, after) if isinstance(self.left, StructuralResult) else before(self.left) >> after(self.left)
|
|
162
|
-
rb = self.right.bimap(before, after) if isinstance(self.right, StructuralResult) else before(self.right) >> after(self.right)
|
|
163
|
-
left_size = self.left.arity() if isinstance(self.left, ThenResult) else 1
|
|
164
|
-
right_size = self.right.arity() if isinstance(self.right, ThenResult) else 1
|
|
165
|
-
def fwd(s : S, a : ThenResult[A, B]) -> Tuple[S, Tuple[Any, ...] | Any]:
|
|
166
|
-
assert a == self, f"Expected {self}, got {a}"
|
|
167
|
-
match kind:
|
|
168
|
-
case ThenKind.LEFT:
|
|
169
|
-
return lb.forward(s, a.left)
|
|
170
|
-
case ThenKind.RIGHT:
|
|
171
|
-
return rb.forward(s, a.right)
|
|
172
|
-
case ThenKind.BOTH:
|
|
173
|
-
s1, left_v = lb.forward(s, a.left)
|
|
174
|
-
s2, right_v = rb.forward(s1, a.right)
|
|
175
|
-
left_v = (left_v,) if not isinstance(a.left, ThenResult) else left_v
|
|
176
|
-
right_v = (right_v,) if not isinstance(a.right, ThenResult) else right_v
|
|
177
|
-
return s2, left_v + right_v
|
|
178
|
-
|
|
179
|
-
def inv(s: S, b: Tuple[Any, ...] | Any) -> Tuple[S, ThenResult[A, B]]:
|
|
180
|
-
match kind:
|
|
181
|
-
case ThenKind.LEFT:
|
|
182
|
-
s1, lv = lb.inverse(s, b)
|
|
183
|
-
return s1, replace(self, left=lv)
|
|
184
|
-
case ThenKind.RIGHT:
|
|
185
|
-
s1, rv = rb.inverse(s, b)
|
|
186
|
-
return s1, replace(self, right=rv)
|
|
187
|
-
case ThenKind.BOTH:
|
|
188
|
-
lraw = b[:left_size]
|
|
189
|
-
rraw = b[left_size:left_size + right_size]
|
|
190
|
-
lraw = lraw[0] if left_size == 1 else lraw
|
|
191
|
-
rraw = rraw[0] if right_size == 1 else rraw
|
|
192
|
-
s1, lv = lb.inverse(s, lraw)
|
|
193
|
-
s2, rv = rb.inverse(s1, rraw)
|
|
194
|
-
return s2, replace(self, left=lv, right=rv)
|
|
195
|
-
|
|
196
|
-
ret: Bimap[Any, Any, Any] = Bimap(
|
|
197
|
-
forward=fwd,
|
|
198
|
-
inverse=inv
|
|
199
|
-
)
|
|
200
|
-
return before(self) >> ret >> after(self)
|
|
201
|
-
|
|
202
17
|
InProgress = object() # Marker for in-progress state, used to prevent re-entrance in recursive calls
|
|
203
18
|
L = TypeVar('L') # Left type for combined results
|
|
204
19
|
R = TypeVar('R') # Right type for combined results
|
|
@@ -242,7 +57,14 @@ class Error:
|
|
|
242
57
|
state=state,
|
|
243
58
|
previous=self
|
|
244
59
|
)
|
|
245
|
-
|
|
60
|
+
def to_list(self)->List[Dict[str, Any]]:
|
|
61
|
+
lst = []
|
|
62
|
+
current: Optional[Error] = self
|
|
63
|
+
while current is not None:
|
|
64
|
+
d = asdict(current)
|
|
65
|
+
lst.append({k:v for k,v in d.items() if v is not None and k != 'previous'})
|
|
66
|
+
current = current.previous
|
|
67
|
+
return lst
|
|
246
68
|
|
|
247
69
|
|
|
248
70
|
@dataclass(frozen=True)
|
|
@@ -423,6 +245,12 @@ class Algebra(ABC, Generic[A, S]):
|
|
|
423
245
|
return cast(Either[Any, Tuple[B, S]], parsed)
|
|
424
246
|
return self.__class__(map_run, name=self.name) # type: ignore
|
|
425
247
|
|
|
248
|
+
def map_all(self, f: Callable[[Either[Any, Tuple[A, S]]], Either[Any, Tuple[B, S]]])->Algebra[B, S]:
|
|
249
|
+
def map_all_run(input: S, use_cache:bool) -> Either[Any, Tuple[B, S]]:
|
|
250
|
+
parsed = self.run(input, use_cache)
|
|
251
|
+
return f(parsed)
|
|
252
|
+
return self.__class__(map_all_run, name=self.name) # type: ignore
|
|
253
|
+
|
|
426
254
|
def map_error(self, f: Callable[[Optional[Any]], Any]) -> Algebra[A, S]:
|
|
427
255
|
def map_error_run(input: S, use_cache:bool) -> Either[Any, Tuple[A, S]]:
|
|
428
256
|
parsed = self.run(input, use_cache)
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
import re
|
|
5
|
+
from typing import (
|
|
6
|
+
Optional, Any, TypeVar, Tuple, runtime_checkable,
|
|
7
|
+
Dict, Generic, Callable, Union, cast, List, Protocol, Type
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
from dataclasses import dataclass, replace, is_dataclass, asdict
|
|
12
|
+
from enum import Enum
|
|
13
|
+
from functools import cached_property
|
|
14
|
+
from syncraft.constraint import Binding, Variable, Bindable
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
A = TypeVar('A')
|
|
19
|
+
B = TypeVar('B')
|
|
20
|
+
C = TypeVar('C')
|
|
21
|
+
S = TypeVar('S', bound=Bindable)
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True)
|
|
24
|
+
class Reducer(Generic[A, S]):
|
|
25
|
+
run_f: Callable[[A, S], S]
|
|
26
|
+
def __call__(self, a: A, s: S) -> S:
|
|
27
|
+
return self.run_f(a, s)
|
|
28
|
+
|
|
29
|
+
def map(self, f: Callable[[B], A]) -> Reducer[B, S]:
|
|
30
|
+
def map_run(b: B, s: S) -> S:
|
|
31
|
+
return self(f(b), s)
|
|
32
|
+
return Reducer(map_run)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass(frozen=True)
|
|
37
|
+
class Bimap(Generic[A, B]):
|
|
38
|
+
run_f: Callable[[A], Tuple[B, Callable[[B], A]]]
|
|
39
|
+
def __call__(self, a: A) -> Tuple[B, Callable[[B], A]]:
|
|
40
|
+
return self.run_f(a)
|
|
41
|
+
def __rshift__(self, other: Bimap[B, C]) -> Bimap[A, C]:
|
|
42
|
+
def then_run(a: A) -> Tuple[C, Callable[[C], A]]:
|
|
43
|
+
b, inv1 = self(a)
|
|
44
|
+
c, inv2 = other(b)
|
|
45
|
+
def inv(c2: C) -> A:
|
|
46
|
+
return inv1(inv2(c2))
|
|
47
|
+
return c, inv
|
|
48
|
+
return Bimap(then_run)
|
|
49
|
+
@staticmethod
|
|
50
|
+
def const(a: B)->Bimap[B, B]:
|
|
51
|
+
return Bimap(lambda _: (a, lambda b: b))
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def identity()->Bimap[Any, Any]:
|
|
55
|
+
return Bimap(lambda a: (a, lambda b: b))
|
|
56
|
+
|
|
57
|
+
@staticmethod
|
|
58
|
+
def when(cond: Callable[[A], bool],
|
|
59
|
+
then: Bimap[A, B],
|
|
60
|
+
otherwise: Optional[Bimap[A, C]] = None) -> Bimap[A, A | B | C]:
|
|
61
|
+
def when_run(a:A) -> Tuple[A | B | C, Callable[[A | B | C], A]]:
|
|
62
|
+
bimap = then if cond(a) else (otherwise if otherwise is not None else Bimap.identity())
|
|
63
|
+
abc, inv = bimap(a)
|
|
64
|
+
def inv_f(b: Any) -> A:
|
|
65
|
+
return inv(b)
|
|
66
|
+
return abc, inv_f
|
|
67
|
+
return Bimap(when_run)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass(frozen=True)
|
|
72
|
+
class Biarrow(Generic[S, A, B]):
|
|
73
|
+
forward: Callable[[S, A], Tuple[S, B]]
|
|
74
|
+
inverse: Callable[[S, B], Tuple[S, A]]
|
|
75
|
+
def __rshift__(self, other: Biarrow[S, B, C]) -> Biarrow[S, A, C]:
|
|
76
|
+
def fwd(s: S, a: A) -> Tuple[S, C]:
|
|
77
|
+
s1, b = self.forward(s, a)
|
|
78
|
+
return other.forward(s1, b)
|
|
79
|
+
def inv(s: S, c: C) -> Tuple[S, A]:
|
|
80
|
+
s1, b = other.inverse(s, c)
|
|
81
|
+
return self.inverse(s1, b)
|
|
82
|
+
return Biarrow(
|
|
83
|
+
forward=fwd,
|
|
84
|
+
inverse=inv
|
|
85
|
+
)
|
|
86
|
+
@staticmethod
|
|
87
|
+
def identity()->Biarrow[S, A, A]:
|
|
88
|
+
return Biarrow(
|
|
89
|
+
forward=lambda s, x: (s, x),
|
|
90
|
+
inverse=lambda s, y: (s, y)
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
@staticmethod
|
|
94
|
+
def when(condition: Callable[..., bool],
|
|
95
|
+
then: Biarrow[S, A, B],
|
|
96
|
+
otherwise: Optional[Biarrow[S, A, B]] = None) -> Callable[..., Biarrow[S, A, B]]:
|
|
97
|
+
def _when(*args:Any, **kwargs:Any) -> Biarrow[S, A, B]:
|
|
98
|
+
return then if condition(*args, **kwargs) else (otherwise or Biarrow.identity())
|
|
99
|
+
return _when
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class StructuralResult:
|
|
104
|
+
|
|
105
|
+
def bimap(self, f: Bimap[Any, Any]=Bimap.identity())->Tuple[Any, Callable[[Any], Any]]:
|
|
106
|
+
return f(self)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@dataclass(frozen=True)
|
|
110
|
+
class MarkedResult(Generic[A], StructuralResult):
|
|
111
|
+
name: str
|
|
112
|
+
value: A
|
|
113
|
+
def bimap(self, f: Bimap[A, B]=Bimap.identity())->Tuple[MarkedResult[B], Callable[[MarkedResult[B]], MarkedResult[A]]]:
|
|
114
|
+
b, inv = self.value.bimap(f) if isinstance(self.value, StructuralResult) else f(self.value)
|
|
115
|
+
return MarkedResult(name=self.name, value=b), lambda b: replace(self, value=inv(b.value))
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@dataclass(eq=True, frozen=True)
|
|
119
|
+
class ManyResult(Generic[A], StructuralResult):
|
|
120
|
+
value: Tuple[A, ...]
|
|
121
|
+
def bimap(self, f: Bimap[A, B]=Bimap.identity())->Tuple[List[B], Callable[[List[B]], ManyResult[A]]]:
|
|
122
|
+
assert self.value
|
|
123
|
+
forward = [v.bimap(f) if isinstance(v, StructuralResult) else f(v) for v in self.value]
|
|
124
|
+
def invf(b: List[B]) -> ManyResult[A]:
|
|
125
|
+
assert len(b) <= len(forward)
|
|
126
|
+
return replace(self, value=tuple([forward[i][1](bb) for i, bb in enumerate(b)]))
|
|
127
|
+
return [b for b, _ in forward], invf
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@dataclass(eq=True, frozen=True)
|
|
132
|
+
class OrResult(Generic[A], StructuralResult):
|
|
133
|
+
value: A
|
|
134
|
+
def bimap(self, f: Bimap[A, B]=Bimap.identity())->Tuple[B, Callable[[B], OrResult[A]]]:
|
|
135
|
+
b, inv = self.value.bimap(f) if isinstance(self.value, StructuralResult) else f(self.value)
|
|
136
|
+
return b, lambda b: replace(self, value=inv(b))
|
|
137
|
+
|
|
138
|
+
class ThenKind(Enum):
|
|
139
|
+
BOTH = '+'
|
|
140
|
+
LEFT = '//'
|
|
141
|
+
RIGHT = '>>'
|
|
142
|
+
|
|
143
|
+
FlatThen = Tuple[Any, ...]
|
|
144
|
+
MarkedThen = Tuple[Dict[str, Any] | Any, FlatThen]
|
|
145
|
+
|
|
146
|
+
@dataclass(eq=True, frozen=True)
|
|
147
|
+
class ThenResult(Generic[A, B], StructuralResult):
|
|
148
|
+
kind: ThenKind
|
|
149
|
+
left: A
|
|
150
|
+
right: B
|
|
151
|
+
@staticmethod
|
|
152
|
+
def collect_marked(a: FlatThen, f: Optional[Callable[..., Any]] = None)->Tuple[MarkedThen, Callable[[MarkedThen], FlatThen]]:
|
|
153
|
+
index: List[str | int] = []
|
|
154
|
+
named_count = 0
|
|
155
|
+
for i, v in enumerate(a):
|
|
156
|
+
if isinstance(v, MarkedResult):
|
|
157
|
+
index.append(v.name)
|
|
158
|
+
named_count += 1
|
|
159
|
+
else:
|
|
160
|
+
index.append(i - named_count)
|
|
161
|
+
named = {v.name: v.value for v in a if isinstance(v, MarkedResult)}
|
|
162
|
+
unnamed = [v for v in a if not isinstance(v, MarkedResult)]
|
|
163
|
+
if f is None:
|
|
164
|
+
ret = (named, tuple(unnamed))
|
|
165
|
+
else:
|
|
166
|
+
ret = (f(**named), tuple(unnamed))
|
|
167
|
+
def invf(b: MarkedThen) -> Tuple[Any, ...]:
|
|
168
|
+
named_value, unnamed_value = b
|
|
169
|
+
assert isinstance(named_value, dict) or is_dataclass(named_value), f"Expected dict or dataclass for named values, got {type(named_value)}"
|
|
170
|
+
if is_dataclass(named_value):
|
|
171
|
+
named_dict = named | asdict(cast(Any, named_value))
|
|
172
|
+
else:
|
|
173
|
+
named_dict = named | named_value
|
|
174
|
+
ret = []
|
|
175
|
+
for x in index:
|
|
176
|
+
if isinstance(x, str):
|
|
177
|
+
assert x in named_dict, f"Missing named value: {x}"
|
|
178
|
+
ret.append(named_dict[x])
|
|
179
|
+
else:
|
|
180
|
+
assert 0 <= x < len(unnamed_value), f"Missing unnamed value at index: {x}"
|
|
181
|
+
ret.append(unnamed_value[x])
|
|
182
|
+
return tuple(ret)
|
|
183
|
+
return ret, invf
|
|
184
|
+
|
|
185
|
+
def bimap(self, f: Bimap[Any, Any]=Bimap.identity()) -> Tuple[MarkedThen, Callable[[MarkedThen], ThenResult[A, B]]]:
|
|
186
|
+
match self.kind:
|
|
187
|
+
case ThenKind.LEFT:
|
|
188
|
+
lb, linv = self.left.bimap(f) if isinstance(self.left, StructuralResult) else f(self.left)
|
|
189
|
+
return lb, lambda b: replace(self, left=linv(b))
|
|
190
|
+
case ThenKind.RIGHT:
|
|
191
|
+
rb, rinv = self.right.bimap(f) if isinstance(self.right, StructuralResult) else f(self.right)
|
|
192
|
+
return rb, lambda b: replace(self, right=rinv(b))
|
|
193
|
+
case ThenKind.BOTH:
|
|
194
|
+
lb, linv = self.left.bimap(f) if isinstance(self.left, StructuralResult) else f(self.left)
|
|
195
|
+
rb, rinv = self.right.bimap(f) if isinstance(self.right, StructuralResult) else f(self.right)
|
|
196
|
+
left_v = (lb,) if not isinstance(self.left, ThenResult) else lb
|
|
197
|
+
right_v = (rb,) if not isinstance(self.right, ThenResult) else rb
|
|
198
|
+
def invf(b: Tuple[Any, ...]) -> ThenResult[A, B]:
|
|
199
|
+
left_size = self.left.arity() if isinstance(self.left, ThenResult) else 1
|
|
200
|
+
right_size = self.right.arity() if isinstance(self.right, ThenResult) else 1
|
|
201
|
+
lraw = b[:left_size]
|
|
202
|
+
rraw = b[left_size:left_size + right_size]
|
|
203
|
+
lraw = lraw[0] if left_size == 1 else lraw
|
|
204
|
+
rraw = rraw[0] if right_size == 1 else rraw
|
|
205
|
+
la = linv(lraw)
|
|
206
|
+
ra = rinv(rraw)
|
|
207
|
+
return replace(self, left=la, right=ra)
|
|
208
|
+
return left_v + right_v, invf
|
|
209
|
+
# data, func = ThenResult.collect_marked(left_v + right_v)
|
|
210
|
+
# return data, lambda d: invf(func(d))
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def arity(self)->int:
|
|
214
|
+
if self.kind == ThenKind.LEFT:
|
|
215
|
+
return self.left.arity() if isinstance(self.left, ThenResult) else 1
|
|
216
|
+
elif self.kind == ThenKind.RIGHT:
|
|
217
|
+
return self.right.arity() if isinstance(self.right, ThenResult) else 1
|
|
218
|
+
elif self.kind == ThenKind.BOTH:
|
|
219
|
+
left_arity = self.left.arity() if isinstance(self.left, ThenResult) else 1
|
|
220
|
+
right_arity = self.right.arity() if isinstance(self.right, ThenResult) else 1
|
|
221
|
+
return left_arity + right_arity
|
|
222
|
+
else:
|
|
223
|
+
return 1
|
|
224
|
+
|
|
225
|
+
@runtime_checkable
|
|
226
|
+
class TokenProtocol(Protocol):
|
|
227
|
+
@property
|
|
228
|
+
def token_type(self) -> Enum: ...
|
|
229
|
+
@property
|
|
230
|
+
def text(self) -> str: ...
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
@dataclass(frozen=True)
|
|
234
|
+
class Token:
|
|
235
|
+
token_type: Enum
|
|
236
|
+
text: str
|
|
237
|
+
def __str__(self) -> str:
|
|
238
|
+
return f"{self.token_type.name}({self.text})"
|
|
239
|
+
|
|
240
|
+
def __repr__(self) -> str:
|
|
241
|
+
return self.__str__()
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
@dataclass(frozen=True)
|
|
246
|
+
class TokenSpec:
|
|
247
|
+
token_type: Optional[Enum] = None
|
|
248
|
+
text: Optional[str] = None
|
|
249
|
+
case_sensitive: bool = False
|
|
250
|
+
regex: Optional[re.Pattern[str]] = None
|
|
251
|
+
|
|
252
|
+
def is_valid(self, token: TokenProtocol) -> bool:
|
|
253
|
+
type_match = self.token_type is None or token.token_type == self.token_type
|
|
254
|
+
value_match = self.text is None or (token.text.strip() == self.text.strip() if self.case_sensitive else
|
|
255
|
+
token.text.strip().upper() == self.text.strip().upper())
|
|
256
|
+
value_match = value_match or (self.regex is not None and self.regex.fullmatch(token.text) is not None)
|
|
257
|
+
return type_match and value_match
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
T = TypeVar('T', bound=TokenProtocol)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
ParseResult = Union[
|
|
266
|
+
ThenResult['ParseResult[T]', 'ParseResult[T]'],
|
|
267
|
+
MarkedResult['ParseResult[T]'],
|
|
268
|
+
ManyResult['ParseResult[T]'],
|
|
269
|
+
OrResult['ParseResult[T]'],
|
|
270
|
+
T,
|
|
271
|
+
]
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
@dataclass(frozen=True)
|
|
276
|
+
class AST(Generic[T]):
|
|
277
|
+
focus: ParseResult[T]
|
|
278
|
+
pruned: bool = False
|
|
279
|
+
parent: Optional[AST[T]] = None
|
|
280
|
+
|
|
281
|
+
def bimap(self)->Tuple[Any, Callable[[Any], AST[T]]]:
|
|
282
|
+
if isinstance(self.focus, StructuralResult):
|
|
283
|
+
data, invf = self.focus.bimap()
|
|
284
|
+
return data, lambda x: replace(self, focus=invf(x))
|
|
285
|
+
else:
|
|
286
|
+
return self.focus, lambda x: replace(self, focus=x)
|
|
287
|
+
|
|
288
|
+
def wrapper(self)-> Callable[[Any], Any]:
|
|
289
|
+
if isinstance(self.focus, MarkedResult):
|
|
290
|
+
focus = cast(MarkedResult[Any], self.focus)
|
|
291
|
+
return lambda x: MarkedResult(name = focus.name, value = x)
|
|
292
|
+
else:
|
|
293
|
+
return lambda x: x
|
|
294
|
+
|
|
295
|
+
def is_named(self) -> bool:
|
|
296
|
+
return isinstance(self.focus, MarkedResult)
|
|
297
|
+
|
|
298
|
+
def left(self) -> Optional[AST[T]]:
|
|
299
|
+
match self.focus:
|
|
300
|
+
case ThenResult(left=left, kind=kind):
|
|
301
|
+
return replace(self, focus=left, parent=self, pruned = self.pruned or kind == ThenKind.RIGHT)
|
|
302
|
+
case _:
|
|
303
|
+
raise TypeError(f"Invalid focus type({self.focus}) for left traversal")
|
|
304
|
+
|
|
305
|
+
def right(self) -> Optional[AST[T]]:
|
|
306
|
+
match self.focus:
|
|
307
|
+
case ThenResult(right=right, kind=kind):
|
|
308
|
+
return replace(self, focus=right, parent=self, pruned = self.pruned or kind == ThenKind.LEFT)
|
|
309
|
+
case _:
|
|
310
|
+
raise TypeError(f"Invalid focus type({self.focus}) for right traversal")
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def down(self, index: int) -> Optional[AST[T]]:
|
|
314
|
+
match self.focus:
|
|
315
|
+
case ManyResult(value=children):
|
|
316
|
+
if 0 <= index < len(children):
|
|
317
|
+
return replace(self, focus=children[index], parent=self, pruned=self.pruned)
|
|
318
|
+
else:
|
|
319
|
+
raise IndexError(f"Index {index} out of bounds for ManyResult with {len(children)} children")
|
|
320
|
+
case OrResult(value=value):
|
|
321
|
+
if index == 0:
|
|
322
|
+
return replace(self, focus=value, parent=self, pruned=self.pruned)
|
|
323
|
+
else:
|
|
324
|
+
raise IndexError(f"Index {index} out of bounds for OrResult")
|
|
325
|
+
case MarkedResult(value=value):
|
|
326
|
+
return replace(self, focus=value, parent=self, pruned=self.pruned)
|
|
327
|
+
case _:
|
|
328
|
+
raise TypeError(f"Invalid focus type({self.focus}) for down traversal")
|
|
329
|
+
|
|
330
|
+
def how_many(self)->int:
|
|
331
|
+
focus = self.focus.value if isinstance(self.focus, MarkedResult) else self.focus
|
|
332
|
+
match focus:
|
|
333
|
+
case ManyResult(value=children):
|
|
334
|
+
return len(children)
|
|
335
|
+
case _:
|
|
336
|
+
raise TypeError(f"Invalid focus type({self.focus}) for how_many")
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
@cached_property
|
|
341
|
+
def root(self) -> AST[T]:
|
|
342
|
+
while self.parent is not None:
|
|
343
|
+
self = self.parent
|
|
344
|
+
return self
|
|
345
|
+
|