syncraft 0.1.25__py3-none-any.whl → 0.1.26__py3-none-any.whl
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.
Potentially problematic release.
This version of syncraft might be problematic. Click here for more details.
- syncraft/algebra.py +40 -24
- syncraft/ast.py +23 -184
- syncraft/constraint.py +12 -4
- syncraft/finder.py +23 -45
- syncraft/generator.py +92 -94
- syncraft/parser.py +5 -7
- syncraft/syntax.py +41 -39
- {syncraft-0.1.25.dist-info → syncraft-0.1.26.dist-info}/METADATA +3 -3
- syncraft-0.1.26.dist-info/RECORD +16 -0
- syncraft-0.1.25.dist-info/RECORD +0 -16
- {syncraft-0.1.25.dist-info → syncraft-0.1.26.dist-info}/WHEEL +0 -0
- {syncraft-0.1.25.dist-info → syncraft-0.1.26.dist-info}/licenses/LICENSE +0 -0
- {syncraft-0.1.25.dist-info → syncraft-0.1.26.dist-info}/top_level.txt +0 -0
syncraft/algebra.py
CHANGED
|
@@ -8,7 +8,7 @@ import traceback
|
|
|
8
8
|
from dataclasses import dataclass, replace, asdict
|
|
9
9
|
from weakref import WeakKeyDictionary
|
|
10
10
|
from abc import ABC
|
|
11
|
-
from syncraft.ast import ThenKind,
|
|
11
|
+
from syncraft.ast import ThenKind, Then, S, Choice, Many, ChoiceKind
|
|
12
12
|
|
|
13
13
|
A = TypeVar('A') # Result type
|
|
14
14
|
B = TypeVar('B') # Mapped result type
|
|
@@ -235,15 +235,24 @@ class Algebra(ABC, Generic[A, S]):
|
|
|
235
235
|
return lazy_self
|
|
236
236
|
|
|
237
237
|
|
|
238
|
-
######################################################## fundamental combinators ############################################
|
|
239
|
-
def
|
|
240
|
-
def
|
|
238
|
+
######################################################## fundamental combinators ############################################
|
|
239
|
+
def fmap(self, f: Callable[[A], B]) -> Algebra[B, S]:
|
|
240
|
+
def fmap_run(input: S, use_cache:bool) -> Either[Any, Tuple[B, S]]:
|
|
241
241
|
parsed = self.run(input, use_cache)
|
|
242
242
|
if isinstance(parsed, Right):
|
|
243
243
|
return Right((f(parsed.value[0]), parsed.value[1]))
|
|
244
244
|
else:
|
|
245
245
|
return cast(Either[Any, Tuple[B, S]], parsed)
|
|
246
|
-
return self.__class__(
|
|
246
|
+
return self.__class__(fmap_run, name=self.name) # type: ignore
|
|
247
|
+
|
|
248
|
+
def imap(self, f: Callable[[B], A]) -> Algebra[A, S]:
|
|
249
|
+
return self.map_state(lambda s: s.map(f))
|
|
250
|
+
|
|
251
|
+
def map(self, f: Callable[[A], B]) -> Algebra[B, S]:
|
|
252
|
+
return self.fmap(f)
|
|
253
|
+
|
|
254
|
+
def bimap(self, f: Callable[[A], B], i: Callable[[B], A]) -> Algebra[A, S]:
|
|
255
|
+
return self.fmap(f).as_(Algebra[A, S]).imap(i)
|
|
247
256
|
|
|
248
257
|
def map_all(self, f: Callable[[Either[Any, Tuple[A, S]]], Either[Any, Tuple[B, S]]])->Algebra[B, S]:
|
|
249
258
|
def map_all_run(input: S, use_cache:bool) -> Either[Any, Tuple[B, S]]:
|
|
@@ -275,41 +284,48 @@ class Algebra(ABC, Generic[A, S]):
|
|
|
275
284
|
return self.__class__(flat_map_run, name=self.name) # type: ignore
|
|
276
285
|
|
|
277
286
|
|
|
278
|
-
def or_else(self: Algebra[A, S], other: Algebra[B, S]) -> Algebra[
|
|
279
|
-
def or_else_run(input: S, use_cache:bool) -> Either[Any, Tuple[
|
|
287
|
+
def or_else(self: Algebra[A, S], other: Algebra[B, S]) -> Algebra[Choice[A, B], S]:
|
|
288
|
+
def or_else_run(input: S, use_cache:bool) -> Either[Any, Tuple[Choice[A, B], S]]:
|
|
280
289
|
match self.run(input, use_cache):
|
|
281
290
|
case Right((value, state)):
|
|
282
|
-
return Right((
|
|
291
|
+
return Right((Choice(kind=ChoiceKind.LEFT, left=value, right=None), state))
|
|
283
292
|
case Left(err):
|
|
284
293
|
if isinstance(err, Error) and err.committed:
|
|
285
294
|
return Left(err)
|
|
286
295
|
match other.run(input, use_cache):
|
|
287
296
|
case Right((other_value, other_state)):
|
|
288
|
-
return Right((
|
|
297
|
+
return Right((Choice(kind=ChoiceKind.RIGHT, left=None, right=other_value), other_state))
|
|
289
298
|
case Left(other_err):
|
|
290
299
|
return Left(other_err)
|
|
291
300
|
raise TypeError(f"Unexpected result type from {other}")
|
|
292
301
|
raise TypeError(f"Unexpected result type from {self}")
|
|
293
302
|
return self.__class__(or_else_run, name=f'{self.name} | {other.name}') # type: ignore
|
|
294
303
|
|
|
295
|
-
def then_both(self, other: 'Algebra[B, S]') -> 'Algebra[
|
|
296
|
-
def then_both_f(a: A) -> Algebra[
|
|
297
|
-
def combine(b: B) ->
|
|
298
|
-
return
|
|
299
|
-
return other.
|
|
304
|
+
def then_both(self, other: 'Algebra[B, S]') -> 'Algebra[Then[A, B], S]':
|
|
305
|
+
def then_both_f(a: A) -> Algebra[Then[A, B], S]:
|
|
306
|
+
def combine(b: B) -> Then[A, B]:
|
|
307
|
+
return Then(left=a, right=b, kind=ThenKind.BOTH)
|
|
308
|
+
return other.fmap(combine)
|
|
300
309
|
return self.flat_map(then_both_f).named(f'{self.name} + {other.name}')
|
|
301
|
-
|
|
302
|
-
def then_left(self, other: Algebra[B, S]) -> Algebra[ThenResult[A, B], S]:
|
|
303
|
-
return self.then_both(other).map(lambda b: replace(b, kind = ThenKind.LEFT)).named(f'{self.name} // {other.name}')
|
|
304
|
-
|
|
305
|
-
def then_right(self, other: Algebra[B, S]) -> Algebra[ThenResult[A, B], S]:
|
|
306
|
-
return self.then_both(other).map(lambda b: replace(b, kind=ThenKind.RIGHT)).named(f'{self.name} >> {other.name}')
|
|
307
|
-
|
|
308
310
|
|
|
309
|
-
def
|
|
311
|
+
def then_left(self, other: Algebra[B, S]) -> 'Algebra[Then[A, B], S]':
|
|
312
|
+
def then_left_f(a: A) -> Algebra[Then[A, B], S]:
|
|
313
|
+
def combine(b: B) -> Then[A, B]:
|
|
314
|
+
return Then(left=a, right=b, kind=ThenKind.LEFT)
|
|
315
|
+
return other.fmap(combine)
|
|
316
|
+
return self.flat_map(then_left_f).named(f'{self.name} // {other.name}')
|
|
317
|
+
|
|
318
|
+
def then_right(self, other: Algebra[B, S]) -> 'Algebra[Then[A, B], S]':
|
|
319
|
+
def then_right_f(a: A) -> Algebra[Then[A, B], S]:
|
|
320
|
+
def combine(b: B) -> Then[A, B]:
|
|
321
|
+
return Then(left=a, right=b, kind=ThenKind.RIGHT)
|
|
322
|
+
return other.fmap(combine)
|
|
323
|
+
return self.flat_map(then_right_f).named(f'{self.name} >> {other.name}')
|
|
324
|
+
|
|
325
|
+
def many(self, *, at_least: int, at_most: Optional[int]) -> Algebra[Many[A], S]:
|
|
310
326
|
assert at_least > 0, "at_least must be greater than 0"
|
|
311
327
|
assert at_most is None or at_least <= at_most, "at_least must be less than or equal to at_most"
|
|
312
|
-
def many_run(input: S, use_cache:bool) -> Either[Any, Tuple[
|
|
328
|
+
def many_run(input: S, use_cache:bool) -> Either[Any, Tuple[Many[A], S]]:
|
|
313
329
|
ret: List[A] = []
|
|
314
330
|
current_input = input
|
|
315
331
|
while True:
|
|
@@ -333,7 +349,7 @@ class Algebra(ABC, Generic[A, S]):
|
|
|
333
349
|
this=self,
|
|
334
350
|
state=current_input
|
|
335
351
|
))
|
|
336
|
-
return Right((
|
|
352
|
+
return Right((Many(value=tuple(ret)), current_input))
|
|
337
353
|
return self.__class__(many_run, name=f'*({self.name})') # type: ignore
|
|
338
354
|
|
|
339
355
|
|
syncraft/ast.py
CHANGED
|
@@ -10,7 +10,7 @@ from typing import (
|
|
|
10
10
|
|
|
11
11
|
from dataclasses import dataclass, replace, is_dataclass, asdict
|
|
12
12
|
from enum import Enum
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
from syncraft.constraint import Binding, Variable, Bindable
|
|
15
15
|
|
|
16
16
|
|
|
@@ -99,42 +99,28 @@ class Biarrow(Generic[S, A, B]):
|
|
|
99
99
|
return _when
|
|
100
100
|
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
class
|
|
104
|
-
|
|
105
|
-
def bimap(self, f: Bimap[Any, Any]=Bimap.identity())->Tuple[Any, Callable[[Any], Any]]:
|
|
106
|
-
return f(self)
|
|
102
|
+
@dataclass(frozen=True)
|
|
103
|
+
class AST:
|
|
104
|
+
pass
|
|
107
105
|
|
|
108
|
-
|
|
106
|
+
class ChoiceKind(Enum):
|
|
107
|
+
LEFT = 'left'
|
|
108
|
+
RIGHT = 'right'
|
|
109
109
|
@dataclass(frozen=True)
|
|
110
|
-
class
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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))
|
|
110
|
+
class Choice(Generic[A, B], AST):
|
|
111
|
+
kind: ChoiceKind
|
|
112
|
+
left: Optional[A]
|
|
113
|
+
right: Optional[B]
|
|
116
114
|
|
|
117
115
|
|
|
118
|
-
@dataclass(
|
|
119
|
-
class
|
|
116
|
+
@dataclass(frozen=True)
|
|
117
|
+
class Many(Generic[A], AST):
|
|
120
118
|
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
119
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
class OrResult(Generic[A], StructuralResult):
|
|
120
|
+
@dataclass(frozen=True)
|
|
121
|
+
class Marked(Generic[A], AST):
|
|
122
|
+
name: str
|
|
133
123
|
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
124
|
class ThenKind(Enum):
|
|
139
125
|
BOTH = '+'
|
|
140
126
|
LEFT = '//'
|
|
@@ -144,86 +130,10 @@ FlatThen = Tuple[Any, ...]
|
|
|
144
130
|
MarkedThen = Tuple[Dict[str, Any] | Any, FlatThen]
|
|
145
131
|
|
|
146
132
|
@dataclass(eq=True, frozen=True)
|
|
147
|
-
class
|
|
133
|
+
class Then(Generic[A, B], AST):
|
|
148
134
|
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[FlatThen, Callable[[FlatThen], 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
|
-
|
|
210
|
-
def bimap_collected(self, f: Bimap[Any, Any]=Bimap.identity()) -> Tuple[MarkedThen, Callable[[MarkedThen], ThenResult[A, B]]]:
|
|
211
|
-
data, invf = self.bimap(f)
|
|
212
|
-
data, func = ThenResult.collect_marked(data)
|
|
213
|
-
return data, lambda d: invf(func(d))
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
def arity(self)->int:
|
|
217
|
-
if self.kind == ThenKind.LEFT:
|
|
218
|
-
return self.left.arity() if isinstance(self.left, ThenResult) else 1
|
|
219
|
-
elif self.kind == ThenKind.RIGHT:
|
|
220
|
-
return self.right.arity() if isinstance(self.right, ThenResult) else 1
|
|
221
|
-
elif self.kind == ThenKind.BOTH:
|
|
222
|
-
left_arity = self.left.arity() if isinstance(self.left, ThenResult) else 1
|
|
223
|
-
right_arity = self.right.arity() if isinstance(self.right, ThenResult) else 1
|
|
224
|
-
return left_arity + right_arity
|
|
225
|
-
else:
|
|
226
|
-
return 1
|
|
135
|
+
left: Optional[A]
|
|
136
|
+
right: Optional[B]
|
|
227
137
|
|
|
228
138
|
@runtime_checkable
|
|
229
139
|
class TokenProtocol(Protocol):
|
|
@@ -266,83 +176,12 @@ T = TypeVar('T', bound=TokenProtocol)
|
|
|
266
176
|
|
|
267
177
|
|
|
268
178
|
ParseResult = Union[
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
179
|
+
Then['ParseResult[T]', 'ParseResult[T]'],
|
|
180
|
+
Marked['ParseResult[T]'],
|
|
181
|
+
Choice['ParseResult[T]', 'ParseResult[T]'],
|
|
182
|
+
Many['ParseResult[T]'],
|
|
273
183
|
T,
|
|
274
184
|
]
|
|
275
185
|
|
|
276
186
|
|
|
277
187
|
|
|
278
|
-
@dataclass(frozen=True)
|
|
279
|
-
class AST(Generic[T]):
|
|
280
|
-
focus: ParseResult[T]
|
|
281
|
-
pruned: bool = False
|
|
282
|
-
parent: Optional[AST[T]] = None
|
|
283
|
-
|
|
284
|
-
def bimap(self)->Tuple[Any, Callable[[Any], AST[T]]]:
|
|
285
|
-
if isinstance(self.focus, StructuralResult):
|
|
286
|
-
data, invf = self.focus.bimap()
|
|
287
|
-
return data, lambda x: replace(self, focus=invf(x))
|
|
288
|
-
else:
|
|
289
|
-
return self.focus, lambda x: replace(self, focus=x)
|
|
290
|
-
|
|
291
|
-
def wrapper(self)-> Callable[[Any], Any]:
|
|
292
|
-
if isinstance(self.focus, MarkedResult):
|
|
293
|
-
focus = cast(MarkedResult[Any], self.focus)
|
|
294
|
-
return lambda x: MarkedResult(name = focus.name, value = x)
|
|
295
|
-
else:
|
|
296
|
-
return lambda x: x
|
|
297
|
-
|
|
298
|
-
def is_named(self) -> bool:
|
|
299
|
-
return isinstance(self.focus, MarkedResult)
|
|
300
|
-
|
|
301
|
-
def left(self) -> Optional[AST[T]]:
|
|
302
|
-
match self.focus:
|
|
303
|
-
case ThenResult(left=left, kind=kind):
|
|
304
|
-
return replace(self, focus=left, parent=self, pruned = self.pruned or kind == ThenKind.RIGHT)
|
|
305
|
-
case _:
|
|
306
|
-
raise TypeError(f"Invalid focus type({self.focus}) for left traversal")
|
|
307
|
-
|
|
308
|
-
def right(self) -> Optional[AST[T]]:
|
|
309
|
-
match self.focus:
|
|
310
|
-
case ThenResult(right=right, kind=kind):
|
|
311
|
-
return replace(self, focus=right, parent=self, pruned = self.pruned or kind == ThenKind.LEFT)
|
|
312
|
-
case _:
|
|
313
|
-
raise TypeError(f"Invalid focus type({self.focus}) for right traversal")
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
def down(self, index: int) -> Optional[AST[T]]:
|
|
317
|
-
match self.focus:
|
|
318
|
-
case ManyResult(value=children):
|
|
319
|
-
if 0 <= index < len(children):
|
|
320
|
-
return replace(self, focus=children[index], parent=self, pruned=self.pruned)
|
|
321
|
-
else:
|
|
322
|
-
raise IndexError(f"Index {index} out of bounds for ManyResult with {len(children)} children")
|
|
323
|
-
case OrResult(value=value):
|
|
324
|
-
if index == 0:
|
|
325
|
-
return replace(self, focus=value, parent=self, pruned=self.pruned)
|
|
326
|
-
else:
|
|
327
|
-
raise IndexError(f"Index {index} out of bounds for OrResult")
|
|
328
|
-
case MarkedResult(value=value):
|
|
329
|
-
return replace(self, focus=value, parent=self, pruned=self.pruned)
|
|
330
|
-
case _:
|
|
331
|
-
raise TypeError(f"Invalid focus type({self.focus}) for down traversal")
|
|
332
|
-
|
|
333
|
-
def how_many(self)->int:
|
|
334
|
-
focus = self.focus.value if isinstance(self.focus, MarkedResult) else self.focus
|
|
335
|
-
match focus:
|
|
336
|
-
case ManyResult(value=children):
|
|
337
|
-
return len(children)
|
|
338
|
-
case _:
|
|
339
|
-
raise TypeError(f"Invalid focus type({self.focus}) for how_many")
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
@cached_property
|
|
344
|
-
def root(self) -> AST[T]:
|
|
345
|
-
while self.parent is not None:
|
|
346
|
-
self = self.parent
|
|
347
|
-
return self
|
|
348
|
-
|
syncraft/constraint.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
from typing import Callable, Generic, Tuple, TypeVar, Optional, Any,
|
|
2
|
+
from typing import Callable, Generic, Tuple, TypeVar, Optional, Any, Self
|
|
3
3
|
from enum import Enum
|
|
4
4
|
from dataclasses import dataclass, field, replace
|
|
5
5
|
import collections.abc
|
|
@@ -109,9 +109,17 @@ class Binding:
|
|
|
109
109
|
ret[var].append(node)
|
|
110
110
|
return FrozenDict({k: tuple(vs) for k, vs in ret.items()})
|
|
111
111
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
|
|
113
|
+
A = TypeVar('A')
|
|
114
|
+
@dataclass(frozen=True)
|
|
115
|
+
class Bindable:
|
|
116
|
+
binding: Binding = field(default_factory=Binding)
|
|
117
|
+
|
|
118
|
+
def map(self, f: Callable[[Any], Any])->Self:
|
|
119
|
+
return self
|
|
120
|
+
|
|
121
|
+
def bind(self, var: Variable, node:Any)->Self:
|
|
122
|
+
return replace(self, binding=self.binding.bind(var, node))
|
|
115
123
|
|
|
116
124
|
|
|
117
125
|
class Quantifier(Enum):
|
syncraft/finder.py
CHANGED
|
@@ -6,10 +6,9 @@ from typing import (
|
|
|
6
6
|
from dataclasses import dataclass, replace
|
|
7
7
|
from syncraft.algebra import (
|
|
8
8
|
Algebra, Either, Right,
|
|
9
|
-
OrResult, ManyResult, ThenResult, MarkedResult
|
|
10
9
|
)
|
|
10
|
+
from syncraft.ast import T, ParseResult, Choice, Many, Then, Marked
|
|
11
11
|
|
|
12
|
-
from syncraft.ast import T, ParseResult, AST
|
|
13
12
|
from syncraft.generator import GenState, Generator
|
|
14
13
|
from sqlglot import TokenType
|
|
15
14
|
from syncraft.syntax import Syntax
|
|
@@ -17,63 +16,42 @@ import re
|
|
|
17
16
|
|
|
18
17
|
|
|
19
18
|
@dataclass(frozen=True)
|
|
20
|
-
class Finder(Generator[T]):
|
|
21
|
-
def many(self, *, at_least: int, at_most: Optional[int]) -> Algebra[ManyResult[ParseResult[T]], GenState[T]]:
|
|
22
|
-
assert at_least > 0, "at_least must be greater than 0"
|
|
23
|
-
assert at_most is None or at_least <= at_most, "at_least must be less than or equal to at_most"
|
|
24
|
-
return self.map_state(lambda s: replace(s, is_pruned = False)).many(at_least=at_least, at_most=at_most)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def or_else(self, # type: ignore
|
|
28
|
-
other: Algebra[ParseResult[T], GenState[T]]
|
|
29
|
-
) -> Algebra[OrResult[ParseResult[T]], GenState[T]]:
|
|
30
|
-
return self.map_state(lambda s: replace(s, is_pruned = False)).or_else(other)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
@classmethod
|
|
34
|
-
def token(cls,
|
|
35
|
-
token_type: Optional[TokenType] = None,
|
|
36
|
-
text: Optional[str] = None,
|
|
37
|
-
case_sensitive: bool = False,
|
|
38
|
-
regex: Optional[re.Pattern[str]] = None
|
|
39
|
-
)-> Algebra[ParseResult[T], GenState[T]]:
|
|
40
|
-
return super().token(token_type=token_type,
|
|
41
|
-
text=text,
|
|
42
|
-
case_sensitive=case_sensitive,
|
|
43
|
-
regex=regex).map_state(lambda s: replace(s, is_pruned = False)) # type: ignore
|
|
44
|
-
|
|
45
|
-
|
|
19
|
+
class Finder(Generator[T]):
|
|
46
20
|
@classmethod
|
|
47
|
-
def anything(cls)->Algebra[
|
|
48
|
-
def anything_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[
|
|
49
|
-
|
|
50
|
-
return Right((wrapper(input.focus), input))
|
|
21
|
+
def anything(cls)->Algebra[Any, GenState[T]]:
|
|
22
|
+
def anything_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[Any, GenState[T]]]:
|
|
23
|
+
return Right((input.ast, input))
|
|
51
24
|
return cls(anything_run, name=cls.__name__ + '.anything()')
|
|
52
25
|
|
|
53
26
|
|
|
54
27
|
|
|
55
28
|
anything = Syntax(lambda cls: cls.factory('anything')).describe(name="anything", fixity='infix')
|
|
56
29
|
|
|
57
|
-
def matches(syntax: Syntax[Any, Any], data:
|
|
30
|
+
def matches(syntax: Syntax[Any, Any], data: ParseResult[T])-> bool:
|
|
58
31
|
gen = syntax(Finder)
|
|
59
|
-
state = GenState.from_ast(data)
|
|
32
|
+
state = GenState.from_ast(ast = data, restore_pruned=True)
|
|
60
33
|
result = gen.run(state, use_cache=True)
|
|
61
34
|
return isinstance(result, Right)
|
|
62
35
|
|
|
63
36
|
|
|
64
|
-
def find(syntax: Syntax[Any, Any], data:
|
|
37
|
+
def find(syntax: Syntax[Any, Any], data: ParseResult[T]) -> YieldGen[ParseResult[T], None, None]:
|
|
65
38
|
if matches(syntax, data):
|
|
66
39
|
yield data
|
|
67
|
-
match data
|
|
68
|
-
case
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
40
|
+
match data:
|
|
41
|
+
case Then(left=left, right=right):
|
|
42
|
+
if left is not None:
|
|
43
|
+
yield from find(syntax, left)
|
|
44
|
+
if right is not None:
|
|
45
|
+
yield from find(syntax, right)
|
|
46
|
+
case Many(value = value):
|
|
72
47
|
for e in value:
|
|
73
|
-
yield from find(syntax,
|
|
74
|
-
case
|
|
75
|
-
yield from find(syntax,
|
|
76
|
-
case
|
|
77
|
-
|
|
48
|
+
yield from find(syntax, e)
|
|
49
|
+
case Marked(value=value):
|
|
50
|
+
yield from find(syntax, value)
|
|
51
|
+
case Choice(left=left, right=right):
|
|
52
|
+
if left is not None:
|
|
53
|
+
yield from find(syntax, left)
|
|
54
|
+
if right is not None:
|
|
55
|
+
yield from find(syntax, right)
|
|
78
56
|
case _:
|
|
79
57
|
pass
|
syncraft/generator.py
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from typing import (
|
|
4
|
-
Any, TypeVar, Tuple, Optional, Callable, Generic,
|
|
4
|
+
Any, TypeVar, Tuple, Optional, Callable, Generic, cast,
|
|
5
5
|
List,
|
|
6
6
|
)
|
|
7
7
|
from functools import cached_property
|
|
8
8
|
from dataclasses import dataclass, replace
|
|
9
9
|
from syncraft.algebra import (
|
|
10
|
-
Algebra, Either, Left, Right, Error
|
|
11
|
-
OrResult, ManyResult
|
|
10
|
+
Algebra, Either, Left, Right, Error
|
|
12
11
|
)
|
|
13
12
|
|
|
14
|
-
from syncraft.ast import
|
|
13
|
+
from syncraft.ast import (
|
|
14
|
+
T, ParseResult, AST, Token, TokenSpec, Binding, Variable,
|
|
15
|
+
Bindable,
|
|
16
|
+
Choice, Many, ChoiceKind,
|
|
17
|
+
Then, ThenKind, Marked
|
|
18
|
+
)
|
|
15
19
|
|
|
16
20
|
from syncraft.syntax import Syntax
|
|
17
21
|
from sqlglot import TokenType
|
|
@@ -25,78 +29,56 @@ B = TypeVar('B')
|
|
|
25
29
|
|
|
26
30
|
@dataclass(frozen=True)
|
|
27
31
|
class GenState(Bindable, Generic[T]):
|
|
28
|
-
ast: Optional[
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
ast: Optional[ParseResult[T]] = None
|
|
33
|
+
restore_pruned: bool = False
|
|
34
|
+
seed: int = 0
|
|
35
|
+
def map(self, f: Callable[[Any], Any]) -> GenState[T]:
|
|
36
|
+
return replace(self, ast=f(self.ast))
|
|
37
|
+
|
|
38
|
+
def inject(self, a: Any) -> GenState[T]:
|
|
39
|
+
return self.map(lambda _: a)
|
|
40
|
+
|
|
35
41
|
def fork(self, tag: Any) -> GenState[T]:
|
|
36
42
|
return replace(self, seed=hash((self.seed, tag)))
|
|
37
43
|
|
|
38
44
|
def rng(self, tag: Any = None) -> random.Random:
|
|
39
45
|
return random.Random(self.seed if tag is None else hash((self.seed, tag)))
|
|
40
46
|
|
|
41
|
-
|
|
42
|
-
return f"GenState(current={self.focus})"
|
|
47
|
+
|
|
43
48
|
|
|
44
49
|
@cached_property
|
|
45
50
|
def pruned(self)->bool:
|
|
46
|
-
|
|
47
|
-
return self.ast is None or self.ast.pruned
|
|
48
|
-
else:
|
|
49
|
-
return self.is_pruned
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
@property
|
|
53
|
-
def focus(self) -> Optional[ParseResult[T]]:
|
|
54
|
-
if self.ast is None:
|
|
55
|
-
return None
|
|
56
|
-
return self.ast.focus
|
|
57
|
-
|
|
58
|
-
@property
|
|
59
|
-
def is_named(self)->bool:
|
|
60
|
-
return self.ast is not None and self.ast.is_named()
|
|
51
|
+
return self.ast is None
|
|
61
52
|
|
|
62
|
-
def wrapper(self)->Callable[[Any], Any]:
|
|
63
|
-
if self.ast is not None:
|
|
64
|
-
return self.ast.wrapper()
|
|
65
|
-
else:
|
|
66
|
-
return lambda x: x
|
|
67
|
-
|
|
68
53
|
def left(self)-> GenState[T]:
|
|
69
54
|
if self.ast is None:
|
|
70
55
|
return self
|
|
71
|
-
|
|
56
|
+
if isinstance(self.ast, Then) and (self.ast.kind != ThenKind.RIGHT or self.restore_pruned):
|
|
57
|
+
return replace(self, ast=self.ast.left)
|
|
58
|
+
return replace(self, ast=None)
|
|
72
59
|
|
|
73
60
|
def right(self) -> GenState[T]:
|
|
74
61
|
if self.ast is None:
|
|
75
62
|
return self
|
|
76
|
-
|
|
63
|
+
if isinstance(self.ast, Then) and (self.ast.kind != ThenKind.LEFT or self.restore_pruned):
|
|
64
|
+
return replace(self, ast=self.ast.right)
|
|
65
|
+
return replace(self, ast=None)
|
|
77
66
|
|
|
78
67
|
|
|
79
68
|
|
|
80
69
|
def down(self, index: int) -> GenState[T]:
|
|
81
70
|
if self.ast is None:
|
|
82
|
-
return self
|
|
83
|
-
|
|
71
|
+
return self
|
|
72
|
+
match self.ast:
|
|
73
|
+
case Marked(value=value):
|
|
74
|
+
return replace(self, ast=value)
|
|
75
|
+
case _:
|
|
76
|
+
raise TypeError(f"Invalid AST type({self.ast}) for down traversal")
|
|
84
77
|
|
|
85
|
-
@cached_property
|
|
86
|
-
def how_many(self) -> int:
|
|
87
|
-
if self.ast is None:
|
|
88
|
-
return 0
|
|
89
|
-
return self.ast.how_many()
|
|
90
|
-
|
|
91
78
|
@classmethod
|
|
92
|
-
def from_ast(cls, ast: Optional[
|
|
93
|
-
return cls(ast=ast, seed=seed)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
@classmethod
|
|
97
|
-
def from_parse_result(cls, parse_result: Optional[ParseResult[T]], seed: int = 0) -> GenState[T]:
|
|
98
|
-
return cls.from_ast(AST(parse_result) if parse_result else None, seed)
|
|
99
|
-
|
|
79
|
+
def from_ast(cls, *, ast: Optional[ParseResult[T]], seed: int = 0, restore_pruned:bool=False) -> GenState[T]:
|
|
80
|
+
return cls(ast=ast, seed=seed, restore_pruned=restore_pruned)
|
|
81
|
+
|
|
100
82
|
|
|
101
83
|
|
|
102
84
|
|
|
@@ -152,10 +134,12 @@ class TokenGen(TokenSpec):
|
|
|
152
134
|
@dataclass(frozen=True)
|
|
153
135
|
class Generator(Algebra[ParseResult[T], GenState[T]]):
|
|
154
136
|
def flat_map(self, f: Callable[[ParseResult[T]], Algebra[B, GenState[T]]]) -> Algebra[B, GenState[T]]:
|
|
155
|
-
def flat_map_run(
|
|
156
|
-
wrapper = original.wrapper()
|
|
157
|
-
input = original if not original.is_named else original.down(0) # If the input is named, we need to go down to the first child
|
|
137
|
+
def flat_map_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[B, GenState[T]]]:
|
|
158
138
|
try:
|
|
139
|
+
if not isinstance(input.ast, Then):
|
|
140
|
+
return Left(Error(this=self,
|
|
141
|
+
message=f"Expect Then got {input.ast}",
|
|
142
|
+
state=input))
|
|
159
143
|
lft = input.left()
|
|
160
144
|
match self.run(lft, use_cache=use_cache):
|
|
161
145
|
case Left(error):
|
|
@@ -166,47 +150,49 @@ class Generator(Algebra[ParseResult[T], GenState[T]]):
|
|
|
166
150
|
case Left(e):
|
|
167
151
|
return Left(e)
|
|
168
152
|
case Right((result, next_input)):
|
|
169
|
-
return Right((
|
|
153
|
+
return Right((result, next_input))
|
|
170
154
|
raise ValueError("flat_map should always return a value or an error.")
|
|
171
155
|
except Exception as e:
|
|
172
156
|
return Left(Error(
|
|
173
157
|
message=str(e),
|
|
174
158
|
this=self,
|
|
175
|
-
state=
|
|
159
|
+
state=input,
|
|
176
160
|
error=e
|
|
177
161
|
))
|
|
178
162
|
return self.__class__(run_f = flat_map_run, name=self.name) # type: ignore
|
|
179
163
|
|
|
180
164
|
|
|
181
|
-
def many(self, *, at_least: int, at_most: Optional[int]) -> Algebra[
|
|
165
|
+
def many(self, *, at_least: int, at_most: Optional[int]) -> Algebra[Many[ParseResult[T]], GenState[T]]:
|
|
182
166
|
assert at_least > 0, "at_least must be greater than 0"
|
|
183
167
|
assert at_most is None or at_least <= at_most, "at_least must be less than or equal to at_most"
|
|
184
|
-
def many_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[
|
|
185
|
-
wrapper = input.wrapper()
|
|
186
|
-
input = input if not input.is_named else input.down(0) # If the input is named, we need to go down to the first child
|
|
168
|
+
def many_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[Many[ParseResult[T]], GenState[T]]]:
|
|
187
169
|
if input.pruned:
|
|
188
170
|
upper = at_most if at_most is not None else at_least + 2
|
|
189
171
|
count = input.rng("many").randint(at_least, upper)
|
|
190
172
|
ret: List[Any] = []
|
|
191
173
|
for i in range(count):
|
|
192
|
-
forked_input = input.
|
|
174
|
+
forked_input = input.fork(tag=len(ret))
|
|
193
175
|
match self.run(forked_input, use_cache):
|
|
194
176
|
case Right((value, next_input)):
|
|
195
177
|
ret.append(value)
|
|
196
178
|
case Left(_):
|
|
197
179
|
pass
|
|
198
|
-
return Right((
|
|
180
|
+
return Right((Many(value=tuple(ret)), input))
|
|
199
181
|
else:
|
|
182
|
+
if not isinstance(input.ast, Many):
|
|
183
|
+
return Left(Error(this=self,
|
|
184
|
+
message=f"Expect Many got {input.ast}",
|
|
185
|
+
state=input))
|
|
200
186
|
ret = []
|
|
201
|
-
for
|
|
202
|
-
match self.run(input.
|
|
187
|
+
for x in input.ast.value:
|
|
188
|
+
match self.run(input.inject(x), use_cache):
|
|
203
189
|
case Right((value, next_input)):
|
|
204
190
|
ret.append(value)
|
|
205
191
|
if at_most is not None and len(ret) > at_most:
|
|
206
192
|
return Left(Error(
|
|
207
193
|
message=f"Expected at most {at_most} matches, got {len(ret)}",
|
|
208
194
|
this=self,
|
|
209
|
-
state=input.
|
|
195
|
+
state=input.inject(x)
|
|
210
196
|
))
|
|
211
197
|
case Left(_):
|
|
212
198
|
pass
|
|
@@ -214,36 +200,47 @@ class Generator(Algebra[ParseResult[T], GenState[T]]):
|
|
|
214
200
|
return Left(Error(
|
|
215
201
|
message=f"Expected at least {at_least} matches, got {len(ret)}",
|
|
216
202
|
this=self,
|
|
217
|
-
state=input.
|
|
203
|
+
state=input.inject(x)
|
|
218
204
|
))
|
|
219
|
-
return Right((
|
|
205
|
+
return Right((Many(value=tuple(ret)), input))
|
|
220
206
|
return self.__class__(many_run, name=f"many({self.name})") # type: ignore
|
|
221
207
|
|
|
222
208
|
|
|
223
209
|
def or_else(self, # type: ignore
|
|
224
210
|
other: Algebra[ParseResult[T], GenState[T]]
|
|
225
|
-
) -> Algebra[
|
|
226
|
-
def or_else_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
return Right((wrapper(OrResult(value)), next_input))
|
|
234
|
-
case Left(error):
|
|
235
|
-
return Left(error)
|
|
236
|
-
else:
|
|
237
|
-
match self.run(input.down(0), use_cache):
|
|
238
|
-
case Right((value, next_input)):
|
|
239
|
-
return Right((wrapper(OrResult(value)), next_input))
|
|
240
|
-
case Left(error):
|
|
241
|
-
match other.run(input.down(0), use_cache):
|
|
211
|
+
) -> Algebra[Choice[ParseResult[T], ParseResult[T]], GenState[T]]:
|
|
212
|
+
def or_else_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[Choice[ParseResult[T], ParseResult[T]], GenState[T]]]:
|
|
213
|
+
def exec(kind: ChoiceKind,
|
|
214
|
+
left: GenState[T],
|
|
215
|
+
right: GenState[T])->Either[Any, Tuple[Choice[ParseResult[T], ParseResult[T]], GenState[T]]]:
|
|
216
|
+
match kind:
|
|
217
|
+
case ChoiceKind.LEFT:
|
|
218
|
+
match self.run(left, use_cache):
|
|
242
219
|
case Right((value, next_input)):
|
|
243
|
-
return Right((
|
|
220
|
+
return Right((Choice(kind=ChoiceKind.LEFT, left=value, right=None), next_input))
|
|
244
221
|
case Left(error):
|
|
245
222
|
return Left(error)
|
|
246
|
-
|
|
223
|
+
case ChoiceKind.RIGHT:
|
|
224
|
+
match other.run(right, use_cache):
|
|
225
|
+
case Right((value, next_input)):
|
|
226
|
+
return Right((Choice(kind=ChoiceKind.RIGHT, left=None, right=value), next_input))
|
|
227
|
+
case Left(error):
|
|
228
|
+
return Left(error)
|
|
229
|
+
raise ValueError(f"Invalid ChoiceKind: {kind}")
|
|
230
|
+
|
|
231
|
+
if input.pruned:
|
|
232
|
+
forked_input = input.fork(tag="or_else")
|
|
233
|
+
which = forked_input.rng("or_else").choice((ChoiceKind.LEFT, ChoiceKind.RIGHT))
|
|
234
|
+
return exec(which, forked_input, forked_input)
|
|
235
|
+
else:
|
|
236
|
+
if isinstance(input.ast, Choice):
|
|
237
|
+
return exec(input.ast.kind,
|
|
238
|
+
input.inject(input.ast.left),
|
|
239
|
+
input.inject(input.ast.right))
|
|
240
|
+
else:
|
|
241
|
+
return Left(Error(this=self,
|
|
242
|
+
message=f"Expect Choice got {input.ast}",
|
|
243
|
+
state=input))
|
|
247
244
|
return self.__class__(or_else_run, name=f"or_else({self.name} | {other.name})") # type: ignore
|
|
248
245
|
|
|
249
246
|
@classmethod
|
|
@@ -256,28 +253,29 @@ class Generator(Algebra[ParseResult[T], GenState[T]]):
|
|
|
256
253
|
gen = TokenGen(token_type=token_type, text=text, case_sensitive=case_sensitive, regex=regex)
|
|
257
254
|
lazy_self: Algebra[ParseResult[T], GenState[T]]
|
|
258
255
|
def token_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[ParseResult[Token], GenState[T]]]:
|
|
259
|
-
wrapper = input.wrapper()
|
|
260
|
-
input = input if not input.is_named else input.down(0) # If the input is named, we need to go down to the first child
|
|
261
256
|
if input.pruned:
|
|
262
257
|
return Right((gen.gen(), input))
|
|
263
258
|
else:
|
|
264
|
-
current = input.
|
|
259
|
+
current = input.ast
|
|
265
260
|
if not isinstance(current, Token) or not gen.is_valid(current):
|
|
266
261
|
return Left(Error(None,
|
|
267
262
|
message=f"Expected a Token, but got {type(current)}.",
|
|
268
263
|
state=input))
|
|
269
|
-
return Right((
|
|
264
|
+
return Right((current, input))
|
|
270
265
|
lazy_self = cls(token_run, name=cls.__name__ + f'.token({token_type or text or regex})') # type: ignore
|
|
271
266
|
return lazy_self
|
|
272
267
|
|
|
273
268
|
|
|
274
269
|
|
|
275
|
-
def generate(syntax: Syntax[Any, Any],
|
|
270
|
+
def generate(syntax: Syntax[Any, Any],
|
|
271
|
+
data: Optional[ParseResult[Any]] = None,
|
|
272
|
+
seed: int = 0,
|
|
273
|
+
restore_pruned: bool = False) -> AST | Any:
|
|
276
274
|
gen = syntax(Generator)
|
|
277
|
-
state = GenState.from_ast(data, seed)
|
|
275
|
+
state = GenState.from_ast(ast=data, seed=seed, restore_pruned=restore_pruned)
|
|
278
276
|
result = gen.run(state, use_cache=False)
|
|
279
277
|
if isinstance(result, Right):
|
|
280
|
-
return
|
|
278
|
+
return result.value[0]
|
|
281
279
|
assert isinstance(result, Left), "Generator must return Either[Any, Tuple[Any, Any]]"
|
|
282
280
|
return result.value
|
|
283
281
|
|
syncraft/parser.py
CHANGED
|
@@ -3,7 +3,7 @@ import re
|
|
|
3
3
|
from sqlglot import tokenize, TokenType, Parser as GlotParser, exp
|
|
4
4
|
from typing import (
|
|
5
5
|
Optional, List, Any, Tuple,
|
|
6
|
-
Generic
|
|
6
|
+
Generic
|
|
7
7
|
)
|
|
8
8
|
from syncraft.algebra import (
|
|
9
9
|
Either, Left, Right, Error, Algebra
|
|
@@ -13,16 +13,14 @@ from enum import Enum
|
|
|
13
13
|
from functools import reduce
|
|
14
14
|
from syncraft.syntax import Syntax
|
|
15
15
|
|
|
16
|
-
from syncraft.ast import Token, TokenSpec, AST, T,
|
|
16
|
+
from syncraft.ast import Token, TokenSpec, AST, T, Bindable
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
@dataclass(frozen=True)
|
|
20
20
|
class ParserState(Bindable, Generic[T]):
|
|
21
21
|
input: Tuple[T, ...] = field(default_factory=tuple)
|
|
22
22
|
index: int = 0
|
|
23
|
-
|
|
24
|
-
def bind(self, var: Variable, node:ParseResult[T])->ParserState[T]:
|
|
25
|
-
return replace(self, binding=self.binding.bind(var, node))
|
|
23
|
+
|
|
26
24
|
|
|
27
25
|
def token_sample_string(self)-> str:
|
|
28
26
|
def encode_tokens(*tokens:T) -> str:
|
|
@@ -184,12 +182,12 @@ def sqlglot(parser: Syntax[Any, Any],
|
|
|
184
182
|
|
|
185
183
|
def parse(syntax: Syntax[Any, Any],
|
|
186
184
|
sql: str,
|
|
187
|
-
dialect: str) -> AST
|
|
185
|
+
dialect: str) -> AST | Any:
|
|
188
186
|
parser = syntax(Parser)
|
|
189
187
|
input: ParserState[Token] = token_state(sql, dialect=dialect)
|
|
190
188
|
result = parser.run(input, True)
|
|
191
189
|
if isinstance(result, Right):
|
|
192
|
-
return
|
|
190
|
+
return result.value[0]
|
|
193
191
|
assert isinstance(result, Left), "Parser must return Either[E, Tuple[A, S]]"
|
|
194
192
|
return result.value
|
|
195
193
|
|
syncraft/syntax.py
CHANGED
|
@@ -8,7 +8,7 @@ from dataclasses import dataclass, field, replace
|
|
|
8
8
|
from functools import reduce
|
|
9
9
|
from syncraft.algebra import Algebra, Error, Either, Right
|
|
10
10
|
from syncraft.constraint import Variable, Bindable
|
|
11
|
-
from syncraft.ast import
|
|
11
|
+
from syncraft.ast import Then, ThenKind, Marked
|
|
12
12
|
from types import MethodType, FunctionType
|
|
13
13
|
|
|
14
14
|
|
|
@@ -27,14 +27,14 @@ class Description:
|
|
|
27
27
|
name: Optional[str] = None
|
|
28
28
|
newline: Optional[str] = None
|
|
29
29
|
fixity: Literal['infix', 'prefix', 'postfix'] = 'infix'
|
|
30
|
-
parameter:
|
|
30
|
+
parameter: Tuple[Any, ...] = field(default_factory=tuple)
|
|
31
31
|
|
|
32
32
|
def update(self,
|
|
33
33
|
*,
|
|
34
34
|
newline: Optional[str] = None,
|
|
35
35
|
name: Optional[str] = None,
|
|
36
36
|
fixity: Optional[Literal['infix', 'prefix', 'postfix']] = None,
|
|
37
|
-
parameter: Optional[
|
|
37
|
+
parameter: Optional[Tuple[Any, ...]] = None) -> 'Description':
|
|
38
38
|
return Description(
|
|
39
39
|
name=name if name is not None else self.name,
|
|
40
40
|
newline= newline if newline is not None else self.newline,
|
|
@@ -118,7 +118,7 @@ class Syntax(Generic[A, S]):
|
|
|
118
118
|
newline: Optional[str] = None,
|
|
119
119
|
name: Optional[str] = None,
|
|
120
120
|
fixity: Optional[Literal['infix', 'prefix', 'postfix']] = None,
|
|
121
|
-
parameter: Optional[
|
|
121
|
+
parameter: Optional[Tuple[Syntax[Any, S], ...]] = None) -> Syntax[A, S]:
|
|
122
122
|
return self.__class__(alg=self.alg,
|
|
123
123
|
meta=self.meta.update(name=name,
|
|
124
124
|
newline=newline,
|
|
@@ -129,11 +129,17 @@ class Syntax(Generic[A, S]):
|
|
|
129
129
|
return self.describe(newline=info)
|
|
130
130
|
|
|
131
131
|
def terminal(self, name: str)->Syntax[A, S]:
|
|
132
|
-
return self.describe(name=name, fixity='prefix'
|
|
132
|
+
return self.describe(name=name, fixity='prefix')
|
|
133
133
|
|
|
134
134
|
######################################################## value transformation ########################################################
|
|
135
135
|
def map(self, f: Callable[[A], B]) -> Syntax[B, S]:
|
|
136
136
|
return self.__class__(lambda cls: self.alg(cls).map(f), meta = self.meta) # type: ignore
|
|
137
|
+
|
|
138
|
+
def imap(self, f: Callable[[B], A]) -> Syntax[A, S]:
|
|
139
|
+
return self.__class__(lambda cls: self.alg(cls).imap(f), meta=self.meta)
|
|
140
|
+
|
|
141
|
+
def bimap(self, f: Callable[[A], B], i: Callable[[B], A]) -> Syntax[A, S]:
|
|
142
|
+
return self.__class__(lambda cls: self.alg(cls).bimap(f, i), meta=self.meta)
|
|
137
143
|
|
|
138
144
|
def map_all(self, f: Callable[[Either[Any, Tuple[A, S]]], Either[Any, Tuple[B, S]]]) -> Syntax[B, S]:
|
|
139
145
|
return self.__class__(lambda cls: self.alg(cls).map_all(f), meta=self.meta) # type: ignore
|
|
@@ -148,30 +154,29 @@ class Syntax(Generic[A, S]):
|
|
|
148
154
|
def flat_map(self, f: Callable[[A], Algebra[B, S]]) -> Syntax[B, S]:
|
|
149
155
|
return self.__class__(lambda cls: self.alg(cls).flat_map(f)) # type: ignore
|
|
150
156
|
|
|
151
|
-
def many(self, *, at_least: int = 1, at_most: Optional[int] = None) -> Syntax[
|
|
157
|
+
def many(self, *, at_least: int = 1, at_most: Optional[int] = None) -> Syntax[Tuple[A, ...], S]:
|
|
152
158
|
return self.__class__(lambda cls:self.alg(cls).many(at_least=at_least, at_most=at_most)).describe(name='*', # type: ignore
|
|
153
159
|
fixity='prefix',
|
|
154
|
-
parameter=
|
|
160
|
+
parameter=(self,))
|
|
155
161
|
|
|
156
162
|
################################################ facility combinators ############################################################
|
|
157
163
|
|
|
158
164
|
|
|
159
165
|
|
|
160
|
-
def between(self, left: Syntax[Any, S], right: Syntax[Any, S]) -> Syntax[
|
|
166
|
+
def between(self, left: Syntax[Any, S], right: Syntax[Any, S]) -> Syntax[Then[Any, Then[A, Any]], S]:
|
|
161
167
|
return left >> self // right
|
|
162
168
|
|
|
163
|
-
def sep_by(self, sep: Syntax[Any, S]) -> Syntax[
|
|
164
|
-
return (self + (sep >> self).many()).describe(
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
)
|
|
169
|
+
def sep_by(self, sep: Syntax[Any, S]) -> Syntax[Tuple[A, ...], S]:
|
|
170
|
+
return (self + (sep >> self).many().optional()).describe( # type: ignore
|
|
171
|
+
name='sep_by',
|
|
172
|
+
fixity='prefix',
|
|
173
|
+
parameter=(self, sep))
|
|
169
174
|
|
|
170
175
|
def parens(self, sep: Syntax[Any, S], open: Syntax[Any, S], close: Syntax[Any, S]) -> Syntax[Any, S]:
|
|
171
176
|
return self.sep_by(sep=sep).between(left=open, right=close)
|
|
172
177
|
|
|
173
178
|
def optional(self, default: Optional[B] = None) -> Syntax[Optional[A | B], S]:
|
|
174
|
-
return (self | success(default)).describe(name='~', fixity='prefix', parameter=
|
|
179
|
+
return (self | success(default)).describe(name='~', fixity='prefix', parameter=(self,))
|
|
175
180
|
|
|
176
181
|
|
|
177
182
|
def cut(self) -> Syntax[A, S]:
|
|
@@ -179,45 +184,37 @@ class Syntax(Generic[A, S]):
|
|
|
179
184
|
|
|
180
185
|
|
|
181
186
|
####################################################### operator overloading #############################################
|
|
182
|
-
def
|
|
183
|
-
return self.flat_map(f).describe(name='>=', fixity='infix', parameter=[self])
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
def __gt__(self, other: Callable[[A], B])->Syntax[B, S]:
|
|
187
|
-
return self.map(other)
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
def __floordiv__(self, other: Syntax[B, S]) -> Syntax[ThenResult[A, None], S]:
|
|
187
|
+
def __floordiv__(self, other: Syntax[B, S]) -> Syntax[Then[A, None], S]:
|
|
191
188
|
other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
|
|
192
189
|
return self.__class__(
|
|
193
190
|
lambda cls: self.alg(cls).then_left(other.alg(cls)) # type: ignore
|
|
194
|
-
).describe(name=ThenKind.LEFT.value, fixity='infix', parameter=
|
|
191
|
+
).describe(name=ThenKind.LEFT.value, fixity='infix', parameter=(self, other)).as_(Syntax[Then[A, None], S])
|
|
195
192
|
|
|
196
|
-
def __rfloordiv__(self, other: Syntax[B, S]) -> Syntax[
|
|
193
|
+
def __rfloordiv__(self, other: Syntax[B, S]) -> Syntax[Then[B, None], S]:
|
|
197
194
|
other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
|
|
198
195
|
return other.__floordiv__(self)
|
|
199
196
|
|
|
200
197
|
def __invert__(self) -> Syntax[A | None, S]:
|
|
201
198
|
return self.optional()
|
|
202
199
|
|
|
203
|
-
def __radd__(self, other: Syntax[B, S]) -> Syntax[
|
|
200
|
+
def __radd__(self, other: Syntax[B, S]) -> Syntax[Then[B, A], S]:
|
|
204
201
|
other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
|
|
205
202
|
return other.__add__(self)
|
|
206
203
|
|
|
207
|
-
def __add__(self, other: Syntax[B, S]) -> Syntax[
|
|
204
|
+
def __add__(self, other: Syntax[B, S]) -> Syntax[Then[A, B], S]:
|
|
208
205
|
other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
|
|
209
206
|
return self.__class__(
|
|
210
207
|
lambda cls: self.alg(cls).then_both(other.alg(cls)) # type: ignore
|
|
211
|
-
).describe(name=ThenKind.BOTH.value, fixity='infix', parameter=
|
|
208
|
+
).describe(name=ThenKind.BOTH.value, fixity='infix', parameter=(self, other))
|
|
212
209
|
|
|
213
|
-
def __rshift__(self, other: Syntax[B, S]) -> Syntax[
|
|
210
|
+
def __rshift__(self, other: Syntax[B, S]) -> Syntax[Then[None, B], S]:
|
|
214
211
|
other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
|
|
215
212
|
return self.__class__(
|
|
216
213
|
lambda cls: self.alg(cls).then_right(other.alg(cls)) # type: ignore
|
|
217
|
-
).describe(name=ThenKind.RIGHT.value, fixity='infix', parameter=
|
|
214
|
+
).describe(name=ThenKind.RIGHT.value, fixity='infix', parameter=(self, other)).as_(Syntax[Then[None, B], S])
|
|
218
215
|
|
|
219
216
|
|
|
220
|
-
def __rrshift__(self, other: Syntax[B, S]) -> Syntax[
|
|
217
|
+
def __rrshift__(self, other: Syntax[B, S]) -> Syntax[Then[None, A], S]:
|
|
221
218
|
other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
|
|
222
219
|
return other.__rshift__(self)
|
|
223
220
|
|
|
@@ -239,15 +236,20 @@ class Syntax(Generic[A, S]):
|
|
|
239
236
|
value, state = result.value
|
|
240
237
|
return Right((value, state.bind(var, value)))
|
|
241
238
|
return result
|
|
242
|
-
|
|
239
|
+
|
|
240
|
+
ret = self.mark(var.name).map_all(bind_v) if var.name else self.map_all(bind_v) # type: ignore
|
|
241
|
+
return ret.describe(name=f'bind({var.name})', fixity='postfix', parameter=(self,))
|
|
243
242
|
|
|
244
|
-
def mark(self, var: str) -> Syntax[
|
|
245
|
-
def bind_s(value: A) ->
|
|
246
|
-
if isinstance(value,
|
|
243
|
+
def mark(self, var: str) -> Syntax[A, S]:
|
|
244
|
+
def bind_s(value: A) -> Marked[A]:
|
|
245
|
+
if isinstance(value, Marked):
|
|
247
246
|
return replace(value, name=var)
|
|
248
247
|
else:
|
|
249
|
-
return
|
|
250
|
-
|
|
248
|
+
return Marked(name=var, value=value)
|
|
249
|
+
def ibind_s(m : Marked[A]) -> A:
|
|
250
|
+
return m.value if isinstance(m, Marked) else m
|
|
251
|
+
|
|
252
|
+
return self.bimap(bind_s, ibind_s).describe(name=f'bind("{var}")', fixity='postfix', parameter=(self,))
|
|
251
253
|
|
|
252
254
|
|
|
253
255
|
|
|
@@ -279,7 +281,7 @@ def choice(*parsers: Syntax[Any, S]) -> Syntax[Any, S]:
|
|
|
279
281
|
return reduce(lambda a, b: a | b, parsers) if len(parsers) > 0 else success(None)
|
|
280
282
|
|
|
281
283
|
|
|
282
|
-
def all(*parsers: Syntax[Any, S]) -> Syntax[
|
|
284
|
+
def all(*parsers: Syntax[Any, S]) -> Syntax[Then[Any, Any], S]:
|
|
283
285
|
return reduce(lambda a, b: a + b, parsers) if len(parsers) > 0 else success(None)
|
|
284
286
|
|
|
285
287
|
def first(*parsers: Syntax[Any, S]) -> Syntax[Any, S]:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: syncraft
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.26
|
|
4
4
|
Summary: Parser combinator library
|
|
5
5
|
Author-email: Michael Afmokt <michael@esacca.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -31,7 +31,7 @@ pip install syncraft
|
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
## TODO
|
|
34
|
-
- [ ]
|
|
35
|
-
- [ ]
|
|
34
|
+
- [ ] simplify the result of then_left and then_right by bimap the result in syntax.
|
|
35
|
+
- [ ] simplify the result of sep_by and between by bimap the result in syntax
|
|
36
36
|
- [ ] Try the parsing, generation, and data processing machinery on SQLite3 syntax. So that I can have direct feedback on the usability of this library and a fully functional SQLite3 library.
|
|
37
37
|
- [ ] Make the library as fast as possible and feasible.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
syncraft/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
syncraft/algebra.py,sha256=FBy0cCl4RGnsi1uxoIIZPRcHgfHxWwUal9iLV_Mtc6I,14791
|
|
3
|
+
syncraft/ast.py,sha256=WwkA02H00BUNrotsMbvypzqHzJb8HiWROVb3qBDSR-8,5113
|
|
4
|
+
syncraft/constraint.py,sha256=uT-ELzvv8J-s-Y1VYkXePUezseqCLhqzYUzFBYs0HsE,6418
|
|
5
|
+
syncraft/diagnostic.py,sha256=cgwcQnCcgrCRX3h-oGTDb5rcJAtitPV3LfH9eLvO93E,2837
|
|
6
|
+
syncraft/finder.py,sha256=qrBlPzQ3hK0rsLIfTD0QdypbrPegDKKCMZriWu-qHBA,1870
|
|
7
|
+
syncraft/generator.py,sha256=BiwwFTjPkk7itXK1wAbB7SJhHi8npd0Wx_3bxR6t1Ww,11775
|
|
8
|
+
syncraft/parser.py,sha256=UpNtTckygIwWG8cX2ZvdEcUi3Il8gpC7aZAzPKx2crg,11289
|
|
9
|
+
syncraft/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
syncraft/sqlite3.py,sha256=Pq09IHZOwuWg5W82l9D1flzd36QV0TOHQpTJ5U02V8g,34701
|
|
11
|
+
syncraft/syntax.py,sha256=aMXnAuGD12_WQxv0TIEvVw-7EO8Bupv0AD1hy6nMJMw,15011
|
|
12
|
+
syncraft-0.1.26.dist-info/licenses/LICENSE,sha256=wHSV424U5csa3339dy1AZbsz2xsd0hrkMx2QK48CcUk,1062
|
|
13
|
+
syncraft-0.1.26.dist-info/METADATA,sha256=h5K3SkaNZtfxw3hD5uc7Ac3D92XJMYCll8cMigf8rvs,1199
|
|
14
|
+
syncraft-0.1.26.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
15
|
+
syncraft-0.1.26.dist-info/top_level.txt,sha256=Kq3t8ESXB2xW1Xt3uPmkENFc-c4f2pamNmaURBk7zc8,9
|
|
16
|
+
syncraft-0.1.26.dist-info/RECORD,,
|
syncraft-0.1.25.dist-info/RECORD
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
syncraft/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
syncraft/algebra.py,sha256=Um8iMgVwlWcseQDkB_GqY-xmo0hR7zSw5j7oRRxrZhE,14122
|
|
3
|
-
syncraft/ast.py,sha256=dpkHAkFBQKbpUyhBJBxZWESNDdqmgh7uytMO1Kvkjb4,13144
|
|
4
|
-
syncraft/constraint.py,sha256=4io1HM_2y5SJzMoeVowAr0a-SDE2jR-7uhCcZpa_FlM,6219
|
|
5
|
-
syncraft/diagnostic.py,sha256=cgwcQnCcgrCRX3h-oGTDb5rcJAtitPV3LfH9eLvO93E,2837
|
|
6
|
-
syncraft/finder.py,sha256=QySZcIjWZFMbRxGJVlJDxK9cmIuNA5vnoE-vYwLJiqA,3005
|
|
7
|
-
syncraft/generator.py,sha256=6jfkSODx952OiN-oOJdNubvU2TzYhLzh3wibZV-ImbI,11625
|
|
8
|
-
syncraft/parser.py,sha256=nSQInixB4j7H22HV_9YdOXfRB5aCPnhQYvMC_N5jnlo,11538
|
|
9
|
-
syncraft/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
syncraft/sqlite3.py,sha256=Pq09IHZOwuWg5W82l9D1flzd36QV0TOHQpTJ5U02V8g,34701
|
|
11
|
-
syncraft/syntax.py,sha256=wwMPAfae5mH4ip3Q2JOkvFkccPfoVDkb-Tk5o0U-ecI,14843
|
|
12
|
-
syncraft-0.1.25.dist-info/licenses/LICENSE,sha256=wHSV424U5csa3339dy1AZbsz2xsd0hrkMx2QK48CcUk,1062
|
|
13
|
-
syncraft-0.1.25.dist-info/METADATA,sha256=ybYAYa8aTHtmeLlmzNfXTycB3Mi13yAHL7_dIx354rk,1381
|
|
14
|
-
syncraft-0.1.25.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
15
|
-
syncraft-0.1.25.dist-info/top_level.txt,sha256=Kq3t8ESXB2xW1Xt3uPmkENFc-c4f2pamNmaURBk7zc8,9
|
|
16
|
-
syncraft-0.1.25.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|