syncraft 0.1.27__py3-none-any.whl → 0.1.29__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 +42 -27
- syncraft/ast.py +264 -82
- syncraft/finder.py +13 -13
- syncraft/generator.py +53 -30
- syncraft/parser.py +4 -2
- syncraft/syntax.py +82 -62
- {syncraft-0.1.27.dist-info → syncraft-0.1.29.dist-info}/METADATA +3 -3
- syncraft-0.1.29.dist-info/RECORD +16 -0
- syncraft-0.1.27.dist-info/RECORD +0 -16
- {syncraft-0.1.27.dist-info → syncraft-0.1.29.dist-info}/WHEEL +0 -0
- {syncraft-0.1.27.dist-info → syncraft-0.1.29.dist-info}/licenses/LICENSE +0 -0
- {syncraft-0.1.27.dist-info → syncraft-0.1.29.dist-info}/top_level.txt +0 -0
syncraft/algebra.py
CHANGED
|
@@ -8,7 +8,10 @@ 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, Then,
|
|
11
|
+
from syncraft.ast import ThenKind, Then, Choice, Many, ChoiceKind
|
|
12
|
+
from syncraft.constraint import Bindable
|
|
13
|
+
|
|
14
|
+
S = TypeVar('S', bound=Bindable)
|
|
12
15
|
|
|
13
16
|
A = TypeVar('A') # Result type
|
|
14
17
|
B = TypeVar('B') # Mapped result type
|
|
@@ -128,10 +131,7 @@ class Algebra(ABC, Generic[A, S]):
|
|
|
128
131
|
def lazy_run(input: S, use_cache:bool) -> Either[Any, Tuple[A, S]]:
|
|
129
132
|
return thunk().run(input, use_cache)
|
|
130
133
|
return cls(lazy_run, name=cls.__name__ + '.lazy')
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
134
|
+
|
|
135
135
|
@classmethod
|
|
136
136
|
def fail(cls, error: Any) -> Algebra[Any, S]:
|
|
137
137
|
def fail_run(input: S, use_cache:bool) -> Either[Any, Tuple[Any, S]]:
|
|
@@ -141,6 +141,7 @@ class Algebra(ABC, Generic[A, S]):
|
|
|
141
141
|
state=input
|
|
142
142
|
))
|
|
143
143
|
return cls(fail_run, name=cls.__name__ + '.fail')
|
|
144
|
+
|
|
144
145
|
@classmethod
|
|
145
146
|
def success(cls, value: Any) -> Algebra[Any, S]:
|
|
146
147
|
def success_run(input: S, use_cache:bool) -> Either[Any, Tuple[Any, S]]:
|
|
@@ -234,7 +235,35 @@ class Algebra(ABC, Generic[A, S]):
|
|
|
234
235
|
lazy_self = self.__class__(debug_run, name=label)
|
|
235
236
|
return lazy_self
|
|
236
237
|
|
|
238
|
+
######################################################## map on state ###########################################
|
|
239
|
+
def post_state(self, f: Callable[[S], S]) -> Algebra[A, S]:
|
|
240
|
+
def post_state_run(input: S, use_cache:bool) -> Either[Any, Tuple[A, S]]:
|
|
241
|
+
match self.run(input, use_cache):
|
|
242
|
+
case Right((value, state)):
|
|
243
|
+
return Right((value, f(state)))
|
|
244
|
+
case Left(err):
|
|
245
|
+
return Left(err)
|
|
246
|
+
case x:
|
|
247
|
+
raise ValueError(f"Unexpected result from self.run {x}")
|
|
248
|
+
return self.__class__(post_state_run, name=self.name)
|
|
249
|
+
|
|
250
|
+
def pre_state(self, f: Callable[[S], S]) -> Algebra[A, S]:
|
|
251
|
+
def pre_state_run(state: S, use_cache:bool) -> Either[Any, Tuple[A, S]]:
|
|
252
|
+
return self.run(f(state), use_cache)
|
|
253
|
+
return self.__class__(pre_state_run, name=self.name)
|
|
254
|
+
|
|
237
255
|
|
|
256
|
+
def map_all(self, f: Callable[[A, S], Tuple[B, S]]) -> Algebra[B, S]:
|
|
257
|
+
def map_all_run(input: S, use_cache:bool) -> Either[Any, Tuple[B, S]]:
|
|
258
|
+
match self.run(input, use_cache):
|
|
259
|
+
case Right((value, state)):
|
|
260
|
+
new_value, new_state = f(value, state)
|
|
261
|
+
return Right((new_value, new_state))
|
|
262
|
+
case Left(err):
|
|
263
|
+
return Left(err)
|
|
264
|
+
case x:
|
|
265
|
+
raise ValueError(f"Unexpected result from self.run {x}")
|
|
266
|
+
return self.__class__(map_all_run, name=self.name) # type: ignore
|
|
238
267
|
######################################################## fundamental combinators ############################################
|
|
239
268
|
def fmap(self, f: Callable[[A], B]) -> Algebra[B, S]:
|
|
240
269
|
def fmap_run(input: S, use_cache:bool) -> Either[Any, Tuple[B, S]]:
|
|
@@ -245,20 +274,12 @@ class Algebra(ABC, Generic[A, S]):
|
|
|
245
274
|
return cast(Either[Any, Tuple[B, S]], parsed)
|
|
246
275
|
return self.__class__(fmap_run, name=self.name) # type: ignore
|
|
247
276
|
|
|
248
|
-
def imap(self, f: Callable[[B], A]) -> Algebra[A, S]:
|
|
249
|
-
return self.map_state(lambda s: s.map(f))
|
|
250
277
|
|
|
251
278
|
def map(self, f: Callable[[A], B]) -> Algebra[B, S]:
|
|
252
279
|
return self.fmap(f)
|
|
253
280
|
|
|
254
|
-
def bimap(self, f: Callable[[A], B], i: Callable[[B], A]) -> Algebra[
|
|
255
|
-
return self.fmap(f).
|
|
256
|
-
|
|
257
|
-
def map_all(self, f: Callable[[Either[Any, Tuple[A, S]]], Either[Any, Tuple[B, S]]])->Algebra[B, S]:
|
|
258
|
-
def map_all_run(input: S, use_cache:bool) -> Either[Any, Tuple[B, S]]:
|
|
259
|
-
parsed = self.run(input, use_cache)
|
|
260
|
-
return f(parsed)
|
|
261
|
-
return self.__class__(map_all_run, name=self.name) # type: ignore
|
|
281
|
+
def bimap(self, f: Callable[[A], B], i: Callable[[B], A]) -> Algebra[B, S]:
|
|
282
|
+
return self.fmap(f).pre_state(lambda s: s.map(i))
|
|
262
283
|
|
|
263
284
|
def map_error(self, f: Callable[[Optional[Any]], Any]) -> Algebra[A, S]:
|
|
264
285
|
def map_error_run(input: S, use_cache:bool) -> Either[Any, Tuple[A, S]]:
|
|
@@ -268,12 +289,6 @@ class Algebra(ABC, Generic[A, S]):
|
|
|
268
289
|
return parsed
|
|
269
290
|
return self.__class__(map_error_run, name=self.name)
|
|
270
291
|
|
|
271
|
-
def map_state(self, f: Callable[[S], S]) -> Algebra[A, S]:
|
|
272
|
-
def map_state_run(state: S, use_cache:bool) -> Either[Any, Tuple[A, S]]:
|
|
273
|
-
return self.run(f(state), use_cache)
|
|
274
|
-
return self.__class__(map_state_run, name=self.name)
|
|
275
|
-
|
|
276
|
-
|
|
277
292
|
def flat_map(self, f: Callable[[A], Algebra[B, S]]) -> Algebra[B, S]:
|
|
278
293
|
def flat_map_run(input: S, use_cache:bool) -> Either[Any, Tuple[B, S]]:
|
|
279
294
|
parsed = self.run(input, use_cache)
|
|
@@ -288,34 +303,34 @@ class Algebra(ABC, Generic[A, S]):
|
|
|
288
303
|
def or_else_run(input: S, use_cache:bool) -> Either[Any, Tuple[Choice[A, B], S]]:
|
|
289
304
|
match self.run(input, use_cache):
|
|
290
305
|
case Right((value, state)):
|
|
291
|
-
return Right((Choice(kind=ChoiceKind.LEFT,
|
|
306
|
+
return Right((Choice(kind=ChoiceKind.LEFT, value=value), state))
|
|
292
307
|
case Left(err):
|
|
293
308
|
if isinstance(err, Error) and err.committed:
|
|
294
|
-
return Left(err)
|
|
309
|
+
return Left(replace(err, committed=False))
|
|
295
310
|
match other.run(input, use_cache):
|
|
296
311
|
case Right((other_value, other_state)):
|
|
297
|
-
return Right((Choice(kind=ChoiceKind.RIGHT,
|
|
312
|
+
return Right((Choice(kind=ChoiceKind.RIGHT, value=other_value), other_state))
|
|
298
313
|
case Left(other_err):
|
|
299
314
|
return Left(other_err)
|
|
300
315
|
raise TypeError(f"Unexpected result type from {other}")
|
|
301
316
|
raise TypeError(f"Unexpected result type from {self}")
|
|
302
317
|
return self.__class__(or_else_run, name=f'{self.name} | {other.name}') # type: ignore
|
|
303
318
|
|
|
304
|
-
def then_both(self, other:
|
|
319
|
+
def then_both(self, other: Algebra[B, S]) -> Algebra[Then[A, B], S]:
|
|
305
320
|
def then_both_f(a: A) -> Algebra[Then[A, B], S]:
|
|
306
321
|
def combine(b: B) -> Then[A, B]:
|
|
307
322
|
return Then(left=a, right=b, kind=ThenKind.BOTH)
|
|
308
323
|
return other.fmap(combine)
|
|
309
324
|
return self.flat_map(then_both_f).named(f'{self.name} + {other.name}')
|
|
310
325
|
|
|
311
|
-
def then_left(self, other: Algebra[B, S]) ->
|
|
326
|
+
def then_left(self, other: Algebra[B, S]) -> Algebra[Then[A, B], S]:
|
|
312
327
|
def then_left_f(a: A) -> Algebra[Then[A, B], S]:
|
|
313
328
|
def combine(b: B) -> Then[A, B]:
|
|
314
329
|
return Then(left=a, right=b, kind=ThenKind.LEFT)
|
|
315
330
|
return other.fmap(combine)
|
|
316
331
|
return self.flat_map(then_left_f).named(f'{self.name} // {other.name}')
|
|
317
332
|
|
|
318
|
-
def then_right(self, other: Algebra[B, S]) ->
|
|
333
|
+
def then_right(self, other: Algebra[B, S]) -> Algebra[Then[A, B], S]:
|
|
319
334
|
def then_right_f(a: A) -> Algebra[Then[A, B], S]:
|
|
320
335
|
def combine(b: B) -> Then[A, B]:
|
|
321
336
|
return Then(left=a, right=b, kind=ThenKind.RIGHT)
|
syncraft/ast.py
CHANGED
|
@@ -3,54 +3,138 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
import re
|
|
5
5
|
from typing import (
|
|
6
|
-
Optional, Any, TypeVar, Tuple, runtime_checkable,
|
|
7
|
-
|
|
6
|
+
Optional, Any, TypeVar, Tuple, runtime_checkable, cast,
|
|
7
|
+
Generic, Callable, Union, Protocol, Type, List, ClassVar
|
|
8
8
|
)
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
from dataclasses import dataclass
|
|
11
|
+
from dataclasses import dataclass, replace, is_dataclass, asdict, fields
|
|
12
12
|
from enum import Enum
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
A = TypeVar('A')
|
|
18
18
|
B = TypeVar('B')
|
|
19
19
|
C = TypeVar('C')
|
|
20
|
-
|
|
20
|
+
D = TypeVar('D')
|
|
21
|
+
S = TypeVar('S')
|
|
22
|
+
S1 = TypeVar('S1')
|
|
23
|
+
|
|
21
24
|
|
|
22
25
|
@dataclass(frozen=True)
|
|
23
|
-
class
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
class Biarrow(Generic[A, B]):
|
|
27
|
+
forward: Callable[[A], B]
|
|
28
|
+
inverse: Callable[[B], A]
|
|
29
|
+
def __rshift__(self, other: Biarrow[B, C]) -> Biarrow[A, C]:
|
|
30
|
+
def fwd(a: A) -> C:
|
|
31
|
+
b = self.forward(a)
|
|
32
|
+
return other.forward(b)
|
|
33
|
+
def inv(c: C) -> A:
|
|
34
|
+
b = other.inverse(c)
|
|
35
|
+
return self.inverse(b)
|
|
36
|
+
return Biarrow(
|
|
37
|
+
forward=fwd,
|
|
38
|
+
inverse=inv
|
|
39
|
+
)
|
|
40
|
+
@staticmethod
|
|
41
|
+
def identity()->Biarrow[A, A]:
|
|
42
|
+
return Biarrow(
|
|
43
|
+
forward=lambda x: x,
|
|
44
|
+
inverse=lambda y: y
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def when(condition: Callable[..., bool],
|
|
49
|
+
then: Biarrow[A, B],
|
|
50
|
+
otherwise: Optional[Biarrow[A, B]] = None) -> Callable[..., Biarrow[A, B]]:
|
|
51
|
+
def _when(*args:Any, **kwargs:Any) -> Biarrow[A, B]:
|
|
52
|
+
return then if condition(*args, **kwargs) else (otherwise or Biarrow.identity())
|
|
53
|
+
return _when
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass(frozen=True)
|
|
57
|
+
class Lens(Generic[C, A]):
|
|
58
|
+
get: Callable[[C], A]
|
|
59
|
+
set: Callable[[C, A], C]
|
|
60
|
+
|
|
61
|
+
def modify(self, source: C, f: Callable[[A], A]) -> C:
|
|
62
|
+
return self.set(source, f(self.get(source)))
|
|
32
63
|
|
|
64
|
+
def bimap(self, ff: Callable[[A], B], bf: Callable[[B], A]) -> Lens[C, B]:
|
|
65
|
+
def getf(data: C) -> B:
|
|
66
|
+
return ff(self.get(data))
|
|
33
67
|
|
|
68
|
+
def setf(data: C, value: B) -> C:
|
|
69
|
+
return self.set(data, bf(value))
|
|
70
|
+
|
|
71
|
+
return Lens(get=getf, set=setf)
|
|
72
|
+
|
|
73
|
+
def __truediv__(self, other: Lens[A, B]) -> Lens[C, B]:
|
|
74
|
+
def get_composed(obj: C) -> B:
|
|
75
|
+
return other.get(self.get(obj))
|
|
76
|
+
def set_composed(obj: C, value: B) -> C:
|
|
77
|
+
return self.set(obj, other.set(self.get(obj), value))
|
|
78
|
+
return Lens(get=get_composed, set=set_composed)
|
|
34
79
|
|
|
80
|
+
def __rtruediv__(self, other: Lens[B, C])->Lens[B, A]:
|
|
81
|
+
return other.__truediv__(self)
|
|
82
|
+
|
|
83
|
+
|
|
35
84
|
@dataclass(frozen=True)
|
|
36
85
|
class Bimap(Generic[A, B]):
|
|
37
86
|
run_f: Callable[[A], Tuple[B, Callable[[B], A]]]
|
|
38
87
|
def __call__(self, a: A) -> Tuple[B, Callable[[B], A]]:
|
|
39
88
|
return self.run_f(a)
|
|
40
|
-
def __rshift__(self, other: Bimap[B, C]) -> Bimap[A, C]:
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
89
|
+
def __rshift__(self, other: Bimap[B, C] | Biarrow[B, C]) -> Bimap[A, C]:
|
|
90
|
+
if isinstance(other, Biarrow):
|
|
91
|
+
def biarrow_then_run(a: A) -> Tuple[C, Callable[[C], A]]:
|
|
92
|
+
b, inv1 = self(a)
|
|
93
|
+
c = other.forward(b)
|
|
94
|
+
def inv(c2: C) -> A:
|
|
95
|
+
b2 = other.inverse(c2)
|
|
96
|
+
return inv1(b2)
|
|
97
|
+
return c, inv
|
|
98
|
+
return Bimap(biarrow_then_run)
|
|
99
|
+
elif isinstance(other, Bimap):
|
|
100
|
+
def bimap_then_run(a: A) -> Tuple[C, Callable[[C], A]]:
|
|
101
|
+
b, inv1 = self(a)
|
|
102
|
+
c, inv2 = other(b)
|
|
103
|
+
def inv(c2: C) -> A:
|
|
104
|
+
return inv1(inv2(c2))
|
|
105
|
+
return c, inv
|
|
106
|
+
return Bimap(bimap_then_run)
|
|
107
|
+
else:
|
|
108
|
+
raise TypeError(f"Unsupported type for Bimap >>: {type(other)}")
|
|
109
|
+
def __rrshift__(self, other: Bimap[C, A] | Biarrow[C, A]) -> Bimap[C, B]:
|
|
110
|
+
if isinstance(other, Biarrow):
|
|
111
|
+
def biarrow_then_run(c: C) -> Tuple[B, Callable[[B], C]]:
|
|
112
|
+
a = other.forward(c)
|
|
113
|
+
b2, inv1 = self(a)
|
|
114
|
+
def inv(a2: B) -> C:
|
|
115
|
+
a3 = inv1(a2)
|
|
116
|
+
return other.inverse(a3)
|
|
117
|
+
return b2, inv
|
|
118
|
+
return Bimap(biarrow_then_run)
|
|
119
|
+
elif isinstance(other, Bimap):
|
|
120
|
+
def bimap_then_run(c: C)->Tuple[B, Callable[[B], C]]:
|
|
121
|
+
a, a2c = other(c)
|
|
122
|
+
b2, b2a = self(a)
|
|
123
|
+
def inv(b3: B) -> C:
|
|
124
|
+
a2 = b2a(b3)
|
|
125
|
+
return a2c(a2)
|
|
126
|
+
return b2, inv
|
|
127
|
+
return Bimap(bimap_then_run)
|
|
128
|
+
else:
|
|
129
|
+
raise TypeError(f"Unsupported type for Bimap <<: {type(other)}")
|
|
130
|
+
|
|
131
|
+
|
|
48
132
|
@staticmethod
|
|
49
133
|
def const(a: B)->Bimap[B, B]:
|
|
50
134
|
return Bimap(lambda _: (a, lambda b: b))
|
|
51
135
|
|
|
52
136
|
@staticmethod
|
|
53
|
-
def identity()->Bimap[
|
|
137
|
+
def identity()->Bimap[A, A]:
|
|
54
138
|
return Bimap(lambda a: (a, lambda b: b))
|
|
55
139
|
|
|
56
140
|
@staticmethod
|
|
@@ -65,86 +149,179 @@ class Bimap(Generic[A, B]):
|
|
|
65
149
|
return abc, inv_f
|
|
66
150
|
return Bimap(when_run)
|
|
67
151
|
|
|
68
|
-
|
|
69
152
|
|
|
70
153
|
@dataclass(frozen=True)
|
|
71
|
-
class
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
inverse=lambda s, y: (s, y)
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
@staticmethod
|
|
93
|
-
def when(condition: Callable[..., bool],
|
|
94
|
-
then: Biarrow[S, A, B],
|
|
95
|
-
otherwise: Optional[Biarrow[S, A, B]] = None) -> Callable[..., Biarrow[S, A, B]]:
|
|
96
|
-
def _when(*args:Any, **kwargs:Any) -> Biarrow[S, A, B]:
|
|
97
|
-
return then if condition(*args, **kwargs) else (otherwise or Biarrow.identity())
|
|
98
|
-
return _when
|
|
154
|
+
class Reducer(Generic[A, S]):
|
|
155
|
+
run_f: Callable[[A, S], S]
|
|
156
|
+
def __call__(self, a: A, s: S) -> S:
|
|
157
|
+
return self.run_f(a, s)
|
|
158
|
+
|
|
159
|
+
def map(self, f: Callable[[B], A]) -> Reducer[B, S]:
|
|
160
|
+
def map_run(b: B, s: S) -> S:
|
|
161
|
+
return self(f(b), s)
|
|
162
|
+
return Reducer(map_run)
|
|
163
|
+
|
|
164
|
+
def __rshift__(self, other: Reducer[A, S]) -> Reducer[A, S]:
|
|
165
|
+
return Reducer(lambda a, s: other(a, self(a, s)))
|
|
166
|
+
|
|
167
|
+
def zip(self, other: Reducer[A, S1])-> Reducer[A, Tuple[S, S1]]:
|
|
168
|
+
return Reducer(lambda a, s: (self(a, s[0]), other(a, s[1])))
|
|
169
|
+
|
|
170
|
+
def diff(self, other: Reducer[B, S]) -> Reducer[Tuple[A, B], S]:
|
|
171
|
+
return Reducer(lambda ab, s: other(ab[1], self(ab[0], s)))
|
|
99
172
|
|
|
173
|
+
def filter(self, f: Callable[[A, S], bool]) -> Reducer[A, S]:
|
|
174
|
+
return Reducer(lambda a, s: self(a, s) if f(a, s) else s)
|
|
175
|
+
|
|
176
|
+
|
|
100
177
|
|
|
101
178
|
@dataclass(frozen=True)
|
|
102
179
|
class AST:
|
|
103
|
-
|
|
180
|
+
def bimap(self, r: Bimap[Any, Any]=Bimap.identity()) -> Tuple[Any, Callable[[Any], Any]]:
|
|
181
|
+
return r(self)
|
|
182
|
+
|
|
183
|
+
@dataclass(frozen=True)
|
|
184
|
+
class Nothing(AST):
|
|
185
|
+
def __str__(self)->str:
|
|
186
|
+
return self.__class__.__name__
|
|
187
|
+
def __repr__(self)->str:
|
|
188
|
+
return self.__str__()
|
|
104
189
|
|
|
190
|
+
|
|
191
|
+
@dataclass(frozen=True)
|
|
192
|
+
class Marked(Generic[A], AST):
|
|
193
|
+
name: str
|
|
194
|
+
value: A
|
|
195
|
+
def bimap(self, r: Bimap[A, B]=Bimap.identity()) -> Tuple[Marked[B], Callable[[Marked[B]], Marked[A]]]:
|
|
196
|
+
v, inner_f = self.value.bimap(r) if isinstance(self.value, AST) else r(self.value)
|
|
197
|
+
return Marked(name=self.name, value=v), lambda b: Marked(name = b.name, value=inner_f(b.value))
|
|
198
|
+
|
|
105
199
|
class ChoiceKind(Enum):
|
|
106
200
|
LEFT = 'left'
|
|
107
201
|
RIGHT = 'right'
|
|
108
|
-
|
|
202
|
+
|
|
109
203
|
@dataclass(frozen=True)
|
|
110
204
|
class Choice(Generic[A, B], AST):
|
|
111
|
-
kind: ChoiceKind
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
205
|
+
kind: Optional[ChoiceKind]
|
|
206
|
+
value: Optional[A | B] = None
|
|
207
|
+
def bimap(self, r: Bimap[A | B, C]=Bimap.identity()) -> Tuple[Optional[C], Callable[[Optional[C]], Choice[A, B]]]:
|
|
208
|
+
if self.value is None:
|
|
209
|
+
return None, lambda c: replace(self, value=None, kind=None)
|
|
210
|
+
else:
|
|
211
|
+
v, inv = self.value.bimap(r) if isinstance(self.value, AST) else r(self.value)
|
|
212
|
+
return v, lambda c: replace(self, value=inv(c) if c is not None else None, kind=None)
|
|
115
213
|
|
|
116
214
|
@dataclass(frozen=True)
|
|
117
215
|
class Many(Generic[A], AST):
|
|
118
216
|
value: Tuple[A, ...]
|
|
217
|
+
def bimap(self, r: Bimap[A, B]=Bimap.identity()) -> Tuple[List[B], Callable[[List[B]], Many[A]]]:
|
|
218
|
+
ret = [v.bimap(r) if isinstance(v, AST) else r(v) for v in self.value]
|
|
219
|
+
def inv(bs: List[B]) -> Many[A]:
|
|
220
|
+
if len(bs) <= len(ret):
|
|
221
|
+
return Many(value = tuple(ret[i][1](bs[i]) for i in range(len(bs))))
|
|
222
|
+
else:
|
|
223
|
+
half = [ret[i][1](bs[i]) for i in range(len(bs))]
|
|
224
|
+
tmp = [ret[-1][1](bs[i]) for i in range(len(ret)-1, len(bs))]
|
|
225
|
+
return Many(value = tuple(half + tmp))
|
|
226
|
+
return [v[0] for v in ret], inv
|
|
119
227
|
|
|
120
|
-
@dataclass(frozen=True)
|
|
121
|
-
class Marked(Generic[A], AST):
|
|
122
|
-
name: str
|
|
123
|
-
value: A
|
|
124
228
|
class ThenKind(Enum):
|
|
125
229
|
BOTH = '+'
|
|
126
230
|
LEFT = '//'
|
|
127
231
|
RIGHT = '>>'
|
|
128
232
|
|
|
129
|
-
FlatThen = Tuple[Any, ...]
|
|
130
|
-
MarkedThen = Tuple[Dict[str, Any] | Any, FlatThen]
|
|
131
|
-
|
|
132
233
|
@dataclass(eq=True, frozen=True)
|
|
133
234
|
class Then(Generic[A, B], AST):
|
|
134
235
|
kind: ThenKind
|
|
135
|
-
left:
|
|
136
|
-
right:
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
236
|
+
left: A
|
|
237
|
+
right: B
|
|
238
|
+
def arity(self)->int:
|
|
239
|
+
if self.kind == ThenKind.LEFT:
|
|
240
|
+
return self.left.arity() if isinstance(self.left, Then) else 1
|
|
241
|
+
elif self.kind == ThenKind.RIGHT:
|
|
242
|
+
return self.right.arity() if isinstance(self.right, Then) else 1
|
|
243
|
+
elif self.kind == ThenKind.BOTH:
|
|
244
|
+
left_arity = self.left.arity() if isinstance(self.left, Then) else 1
|
|
245
|
+
right_arity = self.right.arity() if isinstance(self.right, Then) else 1
|
|
246
|
+
return left_arity + right_arity
|
|
247
|
+
else:
|
|
248
|
+
return 1
|
|
249
|
+
|
|
250
|
+
def bimap(self, r: Bimap[A|B, Any]=Bimap.identity()) -> Tuple[Any | Tuple[Any, ...], Callable[[Any | Tuple[Any, ...]], Then[A, B]]]:
|
|
251
|
+
def need_wrap(x: Any) -> bool:
|
|
252
|
+
return not (isinstance(x, Then) and x.kind == ThenKind.BOTH)
|
|
253
|
+
match self.kind:
|
|
254
|
+
case ThenKind.LEFT:
|
|
255
|
+
lb, linv = self.left.bimap(r) if isinstance(self.left, AST) else r(self.left)
|
|
256
|
+
return lb, lambda c: replace(self, left=cast(A, linv(c)))
|
|
257
|
+
case ThenKind.RIGHT:
|
|
258
|
+
rb, rinv = self.right.bimap(r) if isinstance(self.right, AST) else r(self.right)
|
|
259
|
+
return rb, lambda c: replace(self, right=cast(B, rinv(c)))
|
|
260
|
+
case ThenKind.BOTH:
|
|
261
|
+
lb, linv = self.left.bimap(r) if isinstance(self.left, AST) else r(self.left)
|
|
262
|
+
rb, rinv = self.right.bimap(r) if isinstance(self.right, AST) else r(self.right)
|
|
263
|
+
left_v = (lb,) if need_wrap(self.left) else lb
|
|
264
|
+
right_v = (rb,) if need_wrap(self.right) else rb
|
|
265
|
+
def invf(b: Tuple[C, ...]) -> Then[A, B]:
|
|
266
|
+
left_size = self.left.arity() if isinstance(self.left, Then) else 1
|
|
267
|
+
right_size = self.right.arity() if isinstance(self.right, Then) else 1
|
|
268
|
+
lraw: Tuple[Any, ...] = b[:left_size]
|
|
269
|
+
rraw: Tuple[Any, ...] = b[left_size:left_size + right_size]
|
|
270
|
+
lraw = lraw[0] if left_size == 1 else lraw
|
|
271
|
+
rraw = rraw[0] if right_size == 1 else rraw
|
|
272
|
+
la = linv(lraw)
|
|
273
|
+
ra = rinv(rraw)
|
|
274
|
+
return replace(self, left=cast(A, la), right=cast(B, ra))
|
|
275
|
+
return left_v + right_v, invf
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
class DataclassInstance(Protocol):
|
|
279
|
+
__dataclass_fields__: ClassVar[dict[str, Any]]
|
|
145
280
|
|
|
281
|
+
|
|
282
|
+
E = TypeVar("E", bound=DataclassInstance)
|
|
283
|
+
|
|
284
|
+
Collector = Type[E] | Callable[..., E]
|
|
146
285
|
@dataclass(frozen=True)
|
|
147
|
-
class
|
|
286
|
+
class Collect(Generic[A, E], AST):
|
|
287
|
+
collector: Collector
|
|
288
|
+
value: A
|
|
289
|
+
def bimap(self, r: Bimap[A, B]=Bimap.identity()) -> Tuple[B | E, Callable[[B | E], Collect[A, E]]]:
|
|
290
|
+
b, inner_f = r(self.value)
|
|
291
|
+
if isinstance(self.value, Then) and self.value.kind == ThenKind.BOTH:
|
|
292
|
+
assert isinstance(b, tuple), f"Expected tuple from Then.BOTH combinator, got {type(b)}"
|
|
293
|
+
index: List[str | int] = []
|
|
294
|
+
named_count = 0
|
|
295
|
+
for i, v in enumerate(b):
|
|
296
|
+
if isinstance(v, Marked):
|
|
297
|
+
index.append(v.name)
|
|
298
|
+
named_count += 1
|
|
299
|
+
else:
|
|
300
|
+
index.append(i - named_count)
|
|
301
|
+
named = {v.name: v.value for v in b if isinstance(v, Marked)}
|
|
302
|
+
unnamed = [v for v in b if not isinstance(v, Marked)]
|
|
303
|
+
ret: E = self.collector(*unnamed, **named)
|
|
304
|
+
def invf(e: E) -> Tuple[Any, ...]:
|
|
305
|
+
assert is_dataclass(e), f"Expected dataclass instance for collector inverse, got {type(e)}"
|
|
306
|
+
named_dict = asdict(e)
|
|
307
|
+
unnamed = []
|
|
308
|
+
for f in fields(e):
|
|
309
|
+
if f.name not in named:
|
|
310
|
+
unnamed.append(named_dict[f.name])
|
|
311
|
+
tmp = []
|
|
312
|
+
for x in index:
|
|
313
|
+
if isinstance(x, str):
|
|
314
|
+
tmp.append(Marked(name=x, value=named_dict[x]))
|
|
315
|
+
else:
|
|
316
|
+
tmp.append(unnamed[x])
|
|
317
|
+
return tuple(tmp)
|
|
318
|
+
return ret, lambda e: replace(self, value=inner_f(invf(e))) # type: ignore
|
|
319
|
+
else:
|
|
320
|
+
return b, lambda e: replace(self, value=inner_f(e)) # type: ignore
|
|
321
|
+
|
|
322
|
+
#########################################################################################################################
|
|
323
|
+
@dataclass(frozen=True)
|
|
324
|
+
class Token(AST):
|
|
148
325
|
token_type: Enum
|
|
149
326
|
text: str
|
|
150
327
|
def __str__(self) -> str:
|
|
@@ -152,8 +329,16 @@ class Token:
|
|
|
152
329
|
|
|
153
330
|
def __repr__(self) -> str:
|
|
154
331
|
return self.__str__()
|
|
332
|
+
|
|
333
|
+
@runtime_checkable
|
|
334
|
+
class TokenProtocol(Protocol):
|
|
335
|
+
@property
|
|
336
|
+
def token_type(self) -> Enum: ...
|
|
337
|
+
@property
|
|
338
|
+
def text(self) -> str: ...
|
|
339
|
+
|
|
340
|
+
T = TypeVar('T', bound=TokenProtocol)
|
|
155
341
|
|
|
156
|
-
|
|
157
342
|
|
|
158
343
|
@dataclass(frozen=True)
|
|
159
344
|
class TokenSpec:
|
|
@@ -170,16 +355,13 @@ class TokenSpec:
|
|
|
170
355
|
return type_match and value_match
|
|
171
356
|
|
|
172
357
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
T = TypeVar('T', bound=TokenProtocol)
|
|
176
|
-
|
|
177
|
-
|
|
178
358
|
ParseResult = Union[
|
|
179
359
|
Then['ParseResult[T]', 'ParseResult[T]'],
|
|
180
360
|
Marked['ParseResult[T]'],
|
|
181
361
|
Choice['ParseResult[T]', 'ParseResult[T]'],
|
|
182
362
|
Many['ParseResult[T]'],
|
|
363
|
+
Collect['ParseResult[T]', Any],
|
|
364
|
+
Nothing,
|
|
183
365
|
T,
|
|
184
366
|
]
|
|
185
367
|
|
syncraft/finder.py
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from typing import (
|
|
4
|
-
Any, Tuple, Generator as YieldGen
|
|
4
|
+
Any, Tuple, Generator as YieldGen, TypeVar
|
|
5
5
|
)
|
|
6
6
|
from dataclasses import dataclass
|
|
7
7
|
from syncraft.algebra import (
|
|
8
8
|
Algebra, Either, Right,
|
|
9
9
|
)
|
|
10
|
-
from syncraft.ast import
|
|
10
|
+
from syncraft.ast import TokenProtocol, ParseResult, Choice, Many, Then, Marked, Collect
|
|
11
11
|
|
|
12
12
|
from syncraft.generator import GenState, Generator
|
|
13
13
|
|
|
14
14
|
from syncraft.syntax import Syntax
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
T=TypeVar('T', bound=TokenProtocol)
|
|
18
18
|
@dataclass(frozen=True)
|
|
19
19
|
class Finder(Generator[T]):
|
|
20
20
|
@classmethod
|
|
21
21
|
def anything(cls)->Algebra[Any, GenState[T]]:
|
|
22
22
|
def anything_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[Any, GenState[T]]]:
|
|
23
23
|
return Right((input.ast, input))
|
|
24
|
-
return cls(anything_run, name=cls.__name__ + '.anything
|
|
24
|
+
return cls(anything_run, name=cls.__name__ + '.anything')
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
anything = Syntax(lambda cls: cls.factory('anything')).describe(name="
|
|
28
|
+
anything = Syntax(lambda cls: cls.factory('anything')).describe(name="Anything", fixity='infix')
|
|
29
29
|
|
|
30
|
-
def matches(syntax: Syntax[Any, Any], data: ParseResult[
|
|
30
|
+
def matches(syntax: Syntax[Any, Any], data: ParseResult[Any])-> bool:
|
|
31
31
|
gen = syntax(Finder)
|
|
32
|
-
state = GenState.from_ast(ast = data, restore_pruned=True)
|
|
32
|
+
state = GenState[Any].from_ast(ast = data, restore_pruned=True)
|
|
33
33
|
result = gen.run(state, use_cache=True)
|
|
34
34
|
return isinstance(result, Right)
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
def find(syntax: Syntax[Any, Any], data: ParseResult[
|
|
37
|
+
def find(syntax: Syntax[Any, Any], data: ParseResult[Any]) -> YieldGen[ParseResult[Any], None, None]:
|
|
38
38
|
if matches(syntax, data):
|
|
39
39
|
yield data
|
|
40
40
|
match data:
|
|
@@ -48,10 +48,10 @@ def find(syntax: Syntax[Any, Any], data: ParseResult[T]) -> YieldGen[ParseResult
|
|
|
48
48
|
yield from find(syntax, e)
|
|
49
49
|
case Marked(value=value):
|
|
50
50
|
yield from find(syntax, value)
|
|
51
|
-
case Choice(
|
|
52
|
-
if
|
|
53
|
-
yield from find(syntax,
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
case Choice(value=value):
|
|
52
|
+
if value is not None:
|
|
53
|
+
yield from find(syntax, value)
|
|
54
|
+
case Collect(value=value):
|
|
55
|
+
yield from find(syntax, value)
|
|
56
56
|
case _:
|
|
57
57
|
pass
|
syncraft/generator.py
CHANGED
|
@@ -11,8 +11,8 @@ from syncraft.algebra import (
|
|
|
11
11
|
)
|
|
12
12
|
|
|
13
13
|
from syncraft.ast import (
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
ParseResult, AST, Token, TokenSpec,
|
|
15
|
+
Nothing, TokenProtocol,
|
|
16
16
|
Choice, Many, ChoiceKind,
|
|
17
17
|
Then, ThenKind, Marked
|
|
18
18
|
)
|
|
@@ -23,6 +23,14 @@ import re
|
|
|
23
23
|
import rstr
|
|
24
24
|
from functools import lru_cache
|
|
25
25
|
import random
|
|
26
|
+
from rich import print
|
|
27
|
+
from syncraft.constraint import Bindable
|
|
28
|
+
|
|
29
|
+
T = TypeVar('T', bound=TokenProtocol)
|
|
30
|
+
|
|
31
|
+
S = TypeVar('S', bound=Bindable)
|
|
32
|
+
|
|
33
|
+
|
|
26
34
|
|
|
27
35
|
B = TypeVar('B')
|
|
28
36
|
|
|
@@ -63,9 +71,7 @@ class GenState(Bindable, Generic[T]):
|
|
|
63
71
|
if isinstance(self.ast, Then) and (self.ast.kind != ThenKind.LEFT or self.restore_pruned):
|
|
64
72
|
return replace(self, ast=self.ast.right)
|
|
65
73
|
return replace(self, ast=None)
|
|
66
|
-
|
|
67
74
|
|
|
68
|
-
|
|
69
75
|
def down(self, index: int) -> GenState[T]:
|
|
70
76
|
if self.ast is None:
|
|
71
77
|
return self
|
|
@@ -76,19 +82,27 @@ class GenState(Bindable, Generic[T]):
|
|
|
76
82
|
raise TypeError(f"Invalid AST type({self.ast}) for down traversal")
|
|
77
83
|
|
|
78
84
|
@classmethod
|
|
79
|
-
def from_ast(cls,
|
|
85
|
+
def from_ast(cls,
|
|
86
|
+
*,
|
|
87
|
+
ast: Optional[ParseResult[T]],
|
|
88
|
+
seed: int = 0,
|
|
89
|
+
restore_pruned:bool=False) -> GenState[T]:
|
|
80
90
|
return cls(ast=ast, seed=seed, restore_pruned=restore_pruned)
|
|
81
91
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
92
|
@lru_cache(maxsize=None)
|
|
87
|
-
def token_type_from_string(token_type: Optional[TokenType],
|
|
93
|
+
def token_type_from_string(token_type: Optional[TokenType],
|
|
94
|
+
text: str,
|
|
95
|
+
case_sensitive:bool = False)-> TokenType:
|
|
88
96
|
if not isinstance(token_type, TokenType) or token_type == TokenType.VAR:
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
97
|
+
if case_sensitive:
|
|
98
|
+
for t in TokenType:
|
|
99
|
+
if t.value == text:
|
|
100
|
+
return t
|
|
101
|
+
else:
|
|
102
|
+
text = text.lower()
|
|
103
|
+
for t in TokenType:
|
|
104
|
+
if t.value == text or str(t.value).lower() == text:
|
|
105
|
+
return t
|
|
92
106
|
return TokenType.VAR
|
|
93
107
|
return token_type
|
|
94
108
|
|
|
@@ -121,13 +135,10 @@ class TokenGen(TokenSpec):
|
|
|
121
135
|
else:
|
|
122
136
|
text = "VALUE"
|
|
123
137
|
|
|
124
|
-
return Token(token_type=
|
|
125
|
-
text,
|
|
126
|
-
self.case_sensitive),
|
|
127
|
-
text=text)
|
|
138
|
+
return Token(token_type=token_type_from_string(self.token_type, text, case_sensitive=False), text=text)
|
|
128
139
|
|
|
129
140
|
@staticmethod
|
|
130
|
-
def from_string(string: str)->Token:
|
|
141
|
+
def from_string(string: str) -> Token:
|
|
131
142
|
return Token(token_type=token_type_from_string(None, string, case_sensitive=False), text=string)
|
|
132
143
|
|
|
133
144
|
|
|
@@ -136,7 +147,7 @@ class Generator(Algebra[ParseResult[T], GenState[T]]):
|
|
|
136
147
|
def flat_map(self, f: Callable[[ParseResult[T]], Algebra[B, GenState[T]]]) -> Algebra[B, GenState[T]]:
|
|
137
148
|
def flat_map_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[B, GenState[T]]]:
|
|
138
149
|
try:
|
|
139
|
-
if not isinstance(input.ast, Then):
|
|
150
|
+
if not isinstance(input.ast, Then) or isinstance(input.ast, Nothing):
|
|
140
151
|
return Left(Error(this=self,
|
|
141
152
|
message=f"Expect Then got {input.ast}",
|
|
142
153
|
state=input))
|
|
@@ -179,7 +190,7 @@ class Generator(Algebra[ParseResult[T], GenState[T]]):
|
|
|
179
190
|
pass
|
|
180
191
|
return Right((Many(value=tuple(ret)), input))
|
|
181
192
|
else:
|
|
182
|
-
if not isinstance(input.ast, Many):
|
|
193
|
+
if not isinstance(input.ast, Many) or isinstance(input.ast, Nothing):
|
|
183
194
|
return Left(Error(this=self,
|
|
184
195
|
message=f"Expect Many got {input.ast}",
|
|
185
196
|
state=input))
|
|
@@ -194,7 +205,7 @@ class Generator(Algebra[ParseResult[T], GenState[T]]):
|
|
|
194
205
|
this=self,
|
|
195
206
|
state=input.inject(x)
|
|
196
207
|
))
|
|
197
|
-
case Left(
|
|
208
|
+
case Left(e):
|
|
198
209
|
pass
|
|
199
210
|
if len(ret) < at_least:
|
|
200
211
|
return Left(Error(
|
|
@@ -210,22 +221,34 @@ class Generator(Algebra[ParseResult[T], GenState[T]]):
|
|
|
210
221
|
other: Algebra[ParseResult[T], GenState[T]]
|
|
211
222
|
) -> Algebra[Choice[ParseResult[T], ParseResult[T]], GenState[T]]:
|
|
212
223
|
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,
|
|
224
|
+
def exec(kind: ChoiceKind | None,
|
|
214
225
|
left: GenState[T],
|
|
215
226
|
right: GenState[T])->Either[Any, Tuple[Choice[ParseResult[T], ParseResult[T]], GenState[T]]]:
|
|
216
227
|
match kind:
|
|
217
228
|
case ChoiceKind.LEFT:
|
|
218
229
|
match self.run(left, use_cache):
|
|
219
230
|
case Right((value, next_input)):
|
|
220
|
-
return Right((Choice(kind=ChoiceKind.LEFT,
|
|
231
|
+
return Right((Choice(kind=ChoiceKind.LEFT, value=value), next_input))
|
|
221
232
|
case Left(error):
|
|
222
233
|
return Left(error)
|
|
223
234
|
case ChoiceKind.RIGHT:
|
|
224
235
|
match other.run(right, use_cache):
|
|
225
236
|
case Right((value, next_input)):
|
|
226
|
-
return Right((Choice(kind=ChoiceKind.RIGHT,
|
|
237
|
+
return Right((Choice(kind=ChoiceKind.RIGHT, value=value), next_input))
|
|
227
238
|
case Left(error):
|
|
228
239
|
return Left(error)
|
|
240
|
+
case None:
|
|
241
|
+
match self.run(left, use_cache):
|
|
242
|
+
case Right((value, next_input)):
|
|
243
|
+
return Right((Choice(kind=ChoiceKind.LEFT, value=value), next_input))
|
|
244
|
+
case Left(error):
|
|
245
|
+
if isinstance(error, Error) and error.committed:
|
|
246
|
+
return Left(replace(error, committed=False))
|
|
247
|
+
match other.run(left, use_cache):
|
|
248
|
+
case Right((value, next_input)):
|
|
249
|
+
return Right((Choice(kind=ChoiceKind.RIGHT, value=value), next_input))
|
|
250
|
+
case Left(error):
|
|
251
|
+
return Left(error)
|
|
229
252
|
raise ValueError(f"Invalid ChoiceKind: {kind}")
|
|
230
253
|
|
|
231
254
|
if input.pruned:
|
|
@@ -233,14 +256,14 @@ class Generator(Algebra[ParseResult[T], GenState[T]]):
|
|
|
233
256
|
which = forked_input.rng("or_else").choice((ChoiceKind.LEFT, ChoiceKind.RIGHT))
|
|
234
257
|
return exec(which, forked_input, forked_input)
|
|
235
258
|
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:
|
|
259
|
+
if not isinstance(input.ast, Choice) or isinstance(input.ast, Nothing):
|
|
241
260
|
return Left(Error(this=self,
|
|
242
261
|
message=f"Expect Choice got {input.ast}",
|
|
243
262
|
state=input))
|
|
263
|
+
else:
|
|
264
|
+
return exec(input.ast.kind,
|
|
265
|
+
input.inject(input.ast.value),
|
|
266
|
+
input.inject(input.ast.value))
|
|
244
267
|
return self.__class__(or_else_run, name=f"or_else({self.name} | {other.name})") # type: ignore
|
|
245
268
|
|
|
246
269
|
@classmethod
|
|
@@ -259,7 +282,7 @@ class Generator(Algebra[ParseResult[T], GenState[T]]):
|
|
|
259
282
|
current = input.ast
|
|
260
283
|
if not isinstance(current, Token) or not gen.is_valid(current):
|
|
261
284
|
return Left(Error(None,
|
|
262
|
-
message=f"Expected a Token, but got {
|
|
285
|
+
message=f"Expected a Token({gen.text}), but got {current}.",
|
|
263
286
|
state=input))
|
|
264
287
|
return Right((current, input))
|
|
265
288
|
lazy_self = cls(token_run, name=cls.__name__ + f'.token({token_type or text or regex})') # type: ignore
|
syncraft/parser.py
CHANGED
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
import re
|
|
3
3
|
from sqlglot import tokenize, TokenType, Parser as GlotParser, exp
|
|
4
4
|
from typing import (
|
|
5
|
-
Optional, List, Any, Tuple,
|
|
5
|
+
Optional, List, Any, Tuple, TypeVar,
|
|
6
6
|
Generic
|
|
7
7
|
)
|
|
8
8
|
from syncraft.algebra import (
|
|
@@ -13,9 +13,11 @@ 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,
|
|
16
|
+
from syncraft.ast import Token, TokenSpec, AST, TokenProtocol
|
|
17
|
+
from syncraft.constraint import Bindable
|
|
17
18
|
|
|
18
19
|
|
|
20
|
+
T = TypeVar('T', bound=TokenProtocol)
|
|
19
21
|
@dataclass(frozen=True)
|
|
20
22
|
class ParserState(Bindable, Generic[T]):
|
|
21
23
|
input: Tuple[T, ...] = field(default_factory=tuple)
|
syncraft/syntax.py
CHANGED
|
@@ -8,20 +8,22 @@ 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 Then, ThenKind, Marked
|
|
11
|
+
from syncraft.ast import Then, ThenKind, Marked, Choice, Many, ChoiceKind, Nothing, Collect, E, Collector
|
|
12
12
|
from types import MethodType, FunctionType
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
from rich import print
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
A = TypeVar('A') # Result type
|
|
18
18
|
B = TypeVar('B') # Result type for mapping
|
|
19
19
|
C = TypeVar('C') # Result type for else branch
|
|
20
|
+
D = TypeVar('D') # Result type for else branch
|
|
20
21
|
S = TypeVar('S', bound=Bindable) # State type
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
|
|
26
|
+
|
|
25
27
|
@dataclass(frozen=True)
|
|
26
28
|
class Description:
|
|
27
29
|
name: Optional[str] = None
|
|
@@ -134,49 +136,50 @@ class Syntax(Generic[A, S]):
|
|
|
134
136
|
######################################################## value transformation ########################################################
|
|
135
137
|
def map(self, f: Callable[[A], B]) -> Syntax[B, S]:
|
|
136
138
|
return self.__class__(lambda cls: self.alg(cls).map(f), meta = self.meta) # type: ignore
|
|
137
|
-
|
|
138
|
-
def
|
|
139
|
-
return self.__class__(lambda cls: self.alg(cls).
|
|
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)
|
|
139
|
+
|
|
140
|
+
def bimap(self, f: Callable[[A], B], i: Callable[[B], A]) -> Syntax[B, S]:
|
|
141
|
+
return self.__class__(lambda cls: self.alg(cls).bimap(f, i), meta=self.meta) # type: ignore
|
|
143
142
|
|
|
144
|
-
def map_all(self, f: Callable[[
|
|
143
|
+
def map_all(self, f: Callable[[A, S], Tuple[B, S]]) -> Syntax[B, S]:
|
|
145
144
|
return self.__class__(lambda cls: self.alg(cls).map_all(f), meta=self.meta) # type: ignore
|
|
146
|
-
|
|
145
|
+
|
|
147
146
|
def map_error(self, f: Callable[[Optional[Any]], Any]) -> Syntax[A, S]:
|
|
148
147
|
return self.__class__(lambda cls: self.alg(cls).map_error(f), meta=self.meta)
|
|
149
148
|
|
|
150
|
-
def
|
|
151
|
-
return self.__class__(lambda cls: self.alg(cls).
|
|
149
|
+
def pre_state(self, f: Callable[[S], S]) -> Syntax[A, S]:
|
|
150
|
+
return self.__class__(lambda cls: self.alg(cls).pre_state(f), meta=self.meta)
|
|
152
151
|
|
|
152
|
+
def post_state(self, f: Callable[[S], S]) -> Syntax[A, S]:
|
|
153
|
+
return self.__class__(lambda cls: self.alg(cls).post_state(f), meta=self.meta)
|
|
153
154
|
|
|
154
155
|
def flat_map(self, f: Callable[[A], Algebra[B, S]]) -> Syntax[B, S]:
|
|
155
156
|
return self.__class__(lambda cls: self.alg(cls).flat_map(f)) # type: ignore
|
|
156
157
|
|
|
157
|
-
def many(self, *, at_least: int = 1, at_most: Optional[int] = None) -> Syntax[
|
|
158
|
+
def many(self, *, at_least: int = 1, at_most: Optional[int] = None) -> Syntax[Many[A], S]:
|
|
158
159
|
return self.__class__(lambda cls:self.alg(cls).many(at_least=at_least, at_most=at_most)).describe(name='*', # type: ignore
|
|
159
160
|
fixity='prefix',
|
|
160
161
|
parameter=(self,))
|
|
161
162
|
|
|
162
163
|
################################################ facility combinators ############################################################
|
|
163
164
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
def between(self, left: Syntax[Any, S], right: Syntax[Any, S]) -> Syntax[Then[Any, Then[A, Any]], S]:
|
|
165
|
+
def between(self, left: Syntax[B, S], right: Syntax[C, S]) -> Syntax[Then[B, Then[A, C]], S]:
|
|
167
166
|
return left >> self // right
|
|
168
167
|
|
|
169
|
-
def sep_by(self, sep: Syntax[
|
|
170
|
-
|
|
168
|
+
def sep_by(self, sep: Syntax[B, S]) -> Syntax[Then[A, Choice[Many[Then[B, A]], Optional[Nothing]]], S]:
|
|
169
|
+
ret: Syntax[Then[A, Choice[Many[Then[B, A]], Optional[Nothing]]], S] = (self + (sep >> self).many().optional())
|
|
170
|
+
return ret.describe(
|
|
171
171
|
name='sep_by',
|
|
172
172
|
fixity='prefix',
|
|
173
173
|
parameter=(self, sep))
|
|
174
|
-
|
|
175
|
-
def parens(self,
|
|
174
|
+
|
|
175
|
+
def parens(self,
|
|
176
|
+
sep: Syntax[C, S],
|
|
177
|
+
open: Syntax[B, S],
|
|
178
|
+
close: Syntax[D, S]) -> Syntax[Then[B, Then[Then[A, Choice[Many[Then[C, A]], Optional[Nothing]]], D]], S]:
|
|
176
179
|
return self.sep_by(sep=sep).between(left=open, right=close)
|
|
177
180
|
|
|
178
|
-
def optional(self
|
|
179
|
-
return (self | success(
|
|
181
|
+
def optional(self) -> Syntax[Choice[A, Optional[Nothing]], S]:
|
|
182
|
+
return (self | success(Nothing())).describe(name='~', fixity='prefix', parameter=(self,))
|
|
180
183
|
|
|
181
184
|
|
|
182
185
|
def cut(self) -> Syntax[A, S]:
|
|
@@ -184,63 +187,80 @@ class Syntax(Generic[A, S]):
|
|
|
184
187
|
|
|
185
188
|
|
|
186
189
|
####################################################### operator overloading #############################################
|
|
187
|
-
def __floordiv__(self, other: Syntax[B, S]) -> Syntax[Then[A,
|
|
190
|
+
def __floordiv__(self, other: Syntax[B, S]) -> Syntax[Then[A, B], S]:
|
|
188
191
|
other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
+
ret: Syntax[Then[A, B], S] = self.__class__(lambda cls: self.alg(cls).then_left(other.alg(cls))) # type: ignore
|
|
193
|
+
return ret.describe(name=ThenKind.LEFT.value, fixity='infix', parameter=(self, other)).as_(Syntax[Then[A, B], S])
|
|
194
|
+
|
|
195
|
+
def __lshift__(self, other: Syntax[B, S]) -> Syntax[Then[A, B], S]:
|
|
196
|
+
return self.__floordiv__(other)
|
|
192
197
|
|
|
193
|
-
def __rfloordiv__(self, other: Syntax[B, S]) -> Syntax[Then[B,
|
|
198
|
+
def __rfloordiv__(self, other: Syntax[B, S]) -> Syntax[Then[B, A], S]:
|
|
194
199
|
other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
|
|
195
200
|
return other.__floordiv__(self)
|
|
201
|
+
|
|
202
|
+
def __rlshift__(self, other: Syntax[B, S]) -> Syntax[Then[B, A], S]:
|
|
203
|
+
return self.__rfloordiv__(other)
|
|
196
204
|
|
|
197
|
-
def
|
|
198
|
-
|
|
205
|
+
def __add__(self, other: Syntax[B, S]) -> Syntax[Then[A, B], S]:
|
|
206
|
+
other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
|
|
207
|
+
ret: Syntax[Then[A, B], S] = self.__class__(lambda cls: self.alg(cls).then_both(other.alg(cls))) # type: ignore
|
|
208
|
+
return ret.describe(name=ThenKind.BOTH.value, fixity='infix', parameter=(self, other)).as_(Syntax[Then[A, B], S])
|
|
199
209
|
|
|
200
210
|
def __radd__(self, other: Syntax[B, S]) -> Syntax[Then[B, A], S]:
|
|
201
211
|
other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
|
|
202
212
|
return other.__add__(self)
|
|
203
213
|
|
|
204
|
-
def
|
|
205
|
-
other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
|
|
206
|
-
return self.__class__(
|
|
207
|
-
lambda cls: self.alg(cls).then_both(other.alg(cls)) # type: ignore
|
|
208
|
-
).describe(name=ThenKind.BOTH.value, fixity='infix', parameter=(self, other))
|
|
209
|
-
|
|
210
|
-
def __rshift__(self, other: Syntax[B, S]) -> Syntax[Then[None, B], S]:
|
|
214
|
+
def __rshift__(self, other: Syntax[B, S]) -> Syntax[Then[A, B], S]:
|
|
211
215
|
other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
).describe(name=ThenKind.RIGHT.value, fixity='infix', parameter=(self, other)).as_(Syntax[Then[None, B], S])
|
|
215
|
-
|
|
216
|
+
ret: Syntax[Then[A, B], S] = self.__class__(lambda cls: self.alg(cls).then_right(other.alg(cls))) # type: ignore
|
|
217
|
+
return ret.describe(name=ThenKind.RIGHT.value, fixity='infix', parameter=(self, other)).as_(Syntax[Then[A, B], S])
|
|
216
218
|
|
|
217
|
-
def __rrshift__(self, other: Syntax[B, S]) -> Syntax[Then[
|
|
219
|
+
def __rrshift__(self, other: Syntax[B, S]) -> Syntax[Then[B, A], S]:
|
|
218
220
|
other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
|
|
219
221
|
return other.__rshift__(self)
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
def __or__(self, other: Syntax[B, S]) -> Syntax[A | B, S]:
|
|
222
|
+
|
|
223
|
+
def __or__(self, other: Syntax[B, S]) -> Syntax[Choice[A, B], S]:
|
|
223
224
|
other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
|
|
224
|
-
|
|
225
|
-
|
|
225
|
+
ret: Syntax[Choice[A, B], S] = self.__class__(lambda cls: self.alg(cls).or_else(other.alg(cls))) # type: ignore
|
|
226
|
+
return ret.describe(name='|', fixity='infix', parameter=(self, other))
|
|
226
227
|
|
|
227
|
-
def __ror__(self, other: Syntax[B, S]) -> Syntax[
|
|
228
|
+
def __ror__(self, other: Syntax[B, S]) -> Syntax[Choice[B, A], S]:
|
|
228
229
|
other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
|
|
229
|
-
return other.__or__(self)
|
|
230
|
+
return other.__or__(self)
|
|
231
|
+
|
|
232
|
+
def __invert__(self) -> Syntax[Choice[A, Optional[Nothing]], S]:
|
|
233
|
+
return self.optional()
|
|
230
234
|
|
|
231
235
|
|
|
232
236
|
######################################################################## data processing combinators #########################################################
|
|
233
|
-
def bind(self,
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
237
|
+
def bind(self,
|
|
238
|
+
var: Variable,
|
|
239
|
+
collector: Optional[Type[E]]=None) -> Syntax[A |
|
|
240
|
+
Marked[A] |
|
|
241
|
+
Marked[Collect[A, E]] |
|
|
242
|
+
Collect[A, E], S]:
|
|
243
|
+
def bind_v(v: A | Marked[A] | Marked[Collect[A, E]] | Collect[A, E],
|
|
244
|
+
s: S)->Tuple[A | Marked[A] | Marked[Collect[A, E]] | Collect[A, E], S]:
|
|
245
|
+
return v, s.bind(var, v)
|
|
246
|
+
if callable(collector):
|
|
247
|
+
ret = self.to(collector).mark(var.name).map_all(bind_v) if var.name else self.to(collector).map_all(bind_v)
|
|
248
|
+
else:
|
|
249
|
+
ret = self.mark(var.name).map_all(bind_v) if var.name else self.map_all(bind_v)
|
|
241
250
|
return ret.describe(name=f'bind({var.name})', fixity='postfix', parameter=(self,))
|
|
242
251
|
|
|
243
|
-
def
|
|
252
|
+
def to(self, f: Collector[E])-> Syntax[Collect[A, E], S]:
|
|
253
|
+
def to_f(v: A) -> Collect[A, E]:
|
|
254
|
+
if isinstance(v, Collect):
|
|
255
|
+
return replace(v, collector=f)
|
|
256
|
+
else:
|
|
257
|
+
return Collect(collector=f, value=v)
|
|
258
|
+
def ito_f(c: Collect[A, E]) -> A:
|
|
259
|
+
return c.value if isinstance(c, Collect) else c
|
|
260
|
+
return self.bimap(to_f, ito_f).describe(name=f'to({f})', fixity='postfix', parameter=(self,))
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def mark(self, var: str) -> Syntax[Marked[A], S]:
|
|
244
264
|
def bind_s(value: A) -> Marked[A]:
|
|
245
265
|
if isinstance(value, Marked):
|
|
246
266
|
return replace(value, name=var)
|
|
@@ -278,17 +298,17 @@ def success(value: Any) -> Syntax[Any, Any]:
|
|
|
278
298
|
return Syntax(lambda alg: alg.success(value)).describe(name=f'success({value})', fixity='prefix')
|
|
279
299
|
|
|
280
300
|
def choice(*parsers: Syntax[Any, S]) -> Syntax[Any, S]:
|
|
281
|
-
return reduce(lambda a, b: a | b, parsers) if len(parsers) > 0 else success(
|
|
301
|
+
return reduce(lambda a, b: a | b, parsers) if len(parsers) > 0 else success(Nothing())
|
|
282
302
|
|
|
283
303
|
|
|
284
304
|
def all(*parsers: Syntax[Any, S]) -> Syntax[Then[Any, Any], S]:
|
|
285
|
-
return reduce(lambda a, b: a + b, parsers) if len(parsers) > 0 else success(
|
|
305
|
+
return reduce(lambda a, b: a + b, parsers) if len(parsers) > 0 else success(Nothing())
|
|
286
306
|
|
|
287
307
|
def first(*parsers: Syntax[Any, S]) -> Syntax[Any, S]:
|
|
288
|
-
return reduce(lambda a, b: a // b, parsers) if len(parsers) > 0 else success(
|
|
308
|
+
return reduce(lambda a, b: a // b, parsers) if len(parsers) > 0 else success(Nothing())
|
|
289
309
|
|
|
290
310
|
def last(*parsers: Syntax[Any, S]) -> Syntax[Any, S]:
|
|
291
|
-
return reduce(lambda a, b: a >> b, parsers) if len(parsers) > 0 else success(
|
|
311
|
+
return reduce(lambda a, b: a >> b, parsers) if len(parsers) > 0 else success(Nothing())
|
|
292
312
|
|
|
293
313
|
def bound(* parsers: Syntax[Any, S] | Tuple[str|Variable, Syntax[Any, S]]) -> Syntax[Any, S]:
|
|
294
314
|
def is_named_parser(x: Any) -> bool:
|
|
@@ -325,5 +345,5 @@ def bound(* parsers: Syntax[Any, S] | Tuple[str|Variable, Syntax[Any, S]]) -> Sy
|
|
|
325
345
|
else:
|
|
326
346
|
ret = ret >> just_parser
|
|
327
347
|
|
|
328
|
-
return ret if ret is not None else success(
|
|
348
|
+
return ret if ret is not None else success(Nothing())
|
|
329
349
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: syncraft
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.29
|
|
4
4
|
Summary: Parser combinator library
|
|
5
5
|
Author-email: Michael Afmokt <michael@esacca.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -19,8 +19,6 @@ Syncraft is a parser/generator combinator library with full round-trip support:
|
|
|
19
19
|
|
|
20
20
|
- Parse source code into AST or dataclasses
|
|
21
21
|
- Generate source code from dataclasses
|
|
22
|
-
- Bidirectional transformations via lenses
|
|
23
|
-
- Convenience combinators: `all`, `first`, `last`, `named`
|
|
24
22
|
- SQLite syntax support included
|
|
25
23
|
|
|
26
24
|
## Installation
|
|
@@ -33,5 +31,7 @@ pip install syncraft
|
|
|
33
31
|
## TODO
|
|
34
32
|
- [ ] simplify the result of then_left and then_right by bimap the result in syntax.
|
|
35
33
|
- [ ] simplify the result of sep_by and between by bimap the result in syntax
|
|
34
|
+
- [ ] convert to dict/dataclass via bimap in syntax
|
|
35
|
+
- [ ] define DSL over Variable to construct predicates
|
|
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=gXs4rD6Inp6kGf5Vd5kHGgmb2K_qFlWFQ09i7tmcCmo,15662
|
|
3
|
+
syncraft/ast.py,sha256=MSzEvpXoEPxbLX_tYwKbvNEpPzyGc22-poGtHsc1S30,13747
|
|
4
|
+
syncraft/constraint.py,sha256=uT-ELzvv8J-s-Y1VYkXePUezseqCLhqzYUzFBYs0HsE,6418
|
|
5
|
+
syncraft/diagnostic.py,sha256=cgwcQnCcgrCRX3h-oGTDb5rcJAtitPV3LfH9eLvO93E,2837
|
|
6
|
+
syncraft/finder.py,sha256=L75A0bAjdlntWk-Y1rCeINBOzGeGdE_zHrTmcpQ7m9g,1875
|
|
7
|
+
syncraft/generator.py,sha256=C3N0Nxk9grN6iPxLHt1Ur2xzS5S-9LsNvMMc-ehd36E,12981
|
|
8
|
+
syncraft/parser.py,sha256=RkQwFv00rOI-n4kvG4baGVTdK46T-_Hw7OJ0FB7g72g,11379
|
|
9
|
+
syncraft/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
syncraft/sqlite3.py,sha256=Pq09IHZOwuWg5W82l9D1flzd36QV0TOHQpTJ5U02V8g,34701
|
|
11
|
+
syncraft/syntax.py,sha256=a3g-qcTHpIicMO0KseHuKe5liNfYawLRO-M1eCBS6d4,16574
|
|
12
|
+
syncraft-0.1.29.dist-info/licenses/LICENSE,sha256=wHSV424U5csa3339dy1AZbsz2xsd0hrkMx2QK48CcUk,1062
|
|
13
|
+
syncraft-0.1.29.dist-info/METADATA,sha256=OcqydNw8LWB6R94v-AV_SS4-YSA6QxxTioAqnViK-2w,1204
|
|
14
|
+
syncraft-0.1.29.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
15
|
+
syncraft-0.1.29.dist-info/top_level.txt,sha256=Kq3t8ESXB2xW1Xt3uPmkENFc-c4f2pamNmaURBk7zc8,9
|
|
16
|
+
syncraft-0.1.29.dist-info/RECORD,,
|
syncraft-0.1.27.dist-info/RECORD
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
syncraft/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
syncraft/algebra.py,sha256=FBy0cCl4RGnsi1uxoIIZPRcHgfHxWwUal9iLV_Mtc6I,14791
|
|
3
|
-
syncraft/ast.py,sha256=5WW3NsnCWWPLHr1tP2dDrZQgkl03h4xsz9zJsaABaq0,5049
|
|
4
|
-
syncraft/constraint.py,sha256=uT-ELzvv8J-s-Y1VYkXePUezseqCLhqzYUzFBYs0HsE,6418
|
|
5
|
-
syncraft/diagnostic.py,sha256=cgwcQnCcgrCRX3h-oGTDb5rcJAtitPV3LfH9eLvO93E,2837
|
|
6
|
-
syncraft/finder.py,sha256=GJYnUyBlteMb9gblVKkwy_vVxOMxTwP5P80tWA3kM2Q,1813
|
|
7
|
-
syncraft/generator.py,sha256=CGasCEu79ce8pC56Lb6_gcGCS7pHN6MERv5OEh_Cl-c,11751
|
|
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=JqydSdFkD4Bj0A-S6tqM47ZSbDQEoioNBhOy0WqCEY8,15005
|
|
12
|
-
syncraft-0.1.27.dist-info/licenses/LICENSE,sha256=wHSV424U5csa3339dy1AZbsz2xsd0hrkMx2QK48CcUk,1062
|
|
13
|
-
syncraft-0.1.27.dist-info/METADATA,sha256=vF7-9O08fDH43chYaXyXqKNvwPwuWbIfrROdSCag2wA,1199
|
|
14
|
-
syncraft-0.1.27.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
15
|
-
syncraft-0.1.27.dist-info/top_level.txt,sha256=Kq3t8ESXB2xW1Xt3uPmkENFc-c4f2pamNmaURBk7zc8,9
|
|
16
|
-
syncraft-0.1.27.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|