syncraft 0.1.21__py3-none-any.whl → 0.1.24__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 +19 -191
- syncraft/ast.py +225 -17
- syncraft/constraint.py +189 -0
- syncraft/diagnostic.py +5 -5
- syncraft/finder.py +18 -88
- syncraft/generator.py +12 -4
- syncraft/parser.py +6 -3
- syncraft/syntax.py +35 -41
- {syncraft-0.1.21.dist-info → syncraft-0.1.24.dist-info}/METADATA +1 -1
- syncraft-0.1.24.dist-info/RECORD +16 -0
- syncraft/cmd.py +0 -61
- syncraft-0.1.21.dist-info/RECORD +0 -16
- {syncraft-0.1.21.dist-info → syncraft-0.1.24.dist-info}/WHEEL +0 -0
- {syncraft-0.1.21.dist-info → syncraft-0.1.24.dist-info}/licenses/LICENSE +0 -0
- {syncraft-0.1.21.dist-info → syncraft-0.1.24.dist-info}/top_level.txt +0 -0
syncraft/algebra.py
CHANGED
|
@@ -5,200 +5,15 @@ from typing import (
|
|
|
5
5
|
)
|
|
6
6
|
|
|
7
7
|
import traceback
|
|
8
|
-
from dataclasses import dataclass, replace
|
|
8
|
+
from dataclasses import dataclass, replace, asdict
|
|
9
9
|
from weakref import WeakKeyDictionary
|
|
10
10
|
from abc import ABC
|
|
11
|
-
from
|
|
12
|
-
from functools import reduce
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
A = TypeVar('A')
|
|
16
|
-
B = TypeVar('B')
|
|
17
|
-
C = TypeVar('C')
|
|
18
|
-
S = TypeVar('S')
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
@dataclass(frozen=True)
|
|
22
|
-
class Bimap(Generic[S, A, B]):
|
|
23
|
-
forward: Callable[[S, A], Tuple[S, B]]
|
|
24
|
-
inverse: Callable[[S, B], Tuple[S, A]]
|
|
25
|
-
def __rshift__(self, other: Bimap[S, B, C]) -> Bimap[S, A, C]:
|
|
26
|
-
def fwd(s: S, a: A) -> Tuple[S, C]:
|
|
27
|
-
s1, b = self.forward(s, a)
|
|
28
|
-
return other.forward(s1, b)
|
|
29
|
-
def inv(s: S, c: C) -> Tuple[S, A]:
|
|
30
|
-
s1, b = other.inverse(s, c)
|
|
31
|
-
return self.inverse(s1, b)
|
|
32
|
-
return Bimap(
|
|
33
|
-
forward=fwd,
|
|
34
|
-
inverse=inv
|
|
35
|
-
)
|
|
36
|
-
@staticmethod
|
|
37
|
-
def identity()->Bimap[S, A, A]:
|
|
38
|
-
return Bimap(
|
|
39
|
-
forward=lambda s, x: (s, x),
|
|
40
|
-
inverse=lambda s, y: (s, y)
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
@staticmethod
|
|
44
|
-
def when(condition: Callable[..., bool],
|
|
45
|
-
then: Bimap[S, A, B],
|
|
46
|
-
otherwise: Optional[Bimap[S, A, B]] = None) -> Callable[..., Bimap[S, A, B]]:
|
|
47
|
-
def _when(*args:Any, **kwargs:Any) -> Bimap[S, A, B]:
|
|
48
|
-
return then if condition(*args, **kwargs) else (otherwise or Bimap.identity())
|
|
49
|
-
return _when
|
|
11
|
+
from syncraft.ast import ThenKind, ThenResult, ManyResult, OrResult, S
|
|
50
12
|
|
|
13
|
+
A = TypeVar('A') # Result type
|
|
14
|
+
B = TypeVar('B') # Mapped result type
|
|
15
|
+
|
|
51
16
|
|
|
52
|
-
|
|
53
|
-
class StructuralResult:
|
|
54
|
-
def bimap(self,
|
|
55
|
-
before: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity(),
|
|
56
|
-
after: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity()
|
|
57
|
-
) -> Bimap[Any, Any, Any]:
|
|
58
|
-
return Bimap.identity()
|
|
59
|
-
|
|
60
|
-
@dataclass(frozen=True)
|
|
61
|
-
class NamedResult(Generic[A], StructuralResult):
|
|
62
|
-
name: str
|
|
63
|
-
value: A
|
|
64
|
-
def bimap(self,
|
|
65
|
-
before: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity(),
|
|
66
|
-
after: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity()
|
|
67
|
-
) -> Bimap[Any, NamedResult[A], NamedResult[Any]]:
|
|
68
|
-
inner_b = self.value.bimap(before, after) if isinstance(self.value, StructuralResult) else before(self.value) >> after(self.value)
|
|
69
|
-
def fwd(s: S, a: NamedResult[A])-> Tuple[S, NamedResult[Any]]:
|
|
70
|
-
assert a == self, f"Expected {self}, got {a}"
|
|
71
|
-
inner_s, inner_v = inner_b.forward(s, a.value)
|
|
72
|
-
return (inner_s, replace(a, value=inner_v)) if not isinstance(inner_v, NamedResult) else (inner_s, inner_v)
|
|
73
|
-
|
|
74
|
-
def inv(s: S, a: NamedResult[Any]) -> Tuple[S, NamedResult[A]]:
|
|
75
|
-
assert isinstance(a, NamedResult), f"Expected NamedResult, got {type(a)}"
|
|
76
|
-
inner_s, inner_v = inner_b.inverse(s, a.value)
|
|
77
|
-
return (inner_s, replace(self, value=inner_v)) if not isinstance(inner_v, NamedResult) else (inner_s, replace(self, value=inner_v.value))
|
|
78
|
-
ret: Bimap[Any, Any, Any] = Bimap(
|
|
79
|
-
forward=fwd,
|
|
80
|
-
inverse=inv
|
|
81
|
-
)
|
|
82
|
-
return before(self) >> ret >> after(self)
|
|
83
|
-
@dataclass(eq=True, frozen=True)
|
|
84
|
-
class ManyResult(Generic[A], StructuralResult):
|
|
85
|
-
value: Tuple[A, ...]
|
|
86
|
-
def bimap(self,
|
|
87
|
-
before: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity(),
|
|
88
|
-
after: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity()
|
|
89
|
-
) -> Bimap[Any, ManyResult[A], List[A]]:
|
|
90
|
-
# We don't allow zero length ManyResult e.g. many(at_least >= 1) at the syntax level
|
|
91
|
-
# so inner_b has at least 1 element
|
|
92
|
-
assert len(self.value) > 0, "ManyResult must have at least one element"
|
|
93
|
-
inner_b = [v.bimap(before, after) if isinstance(v, StructuralResult) else before(v) >> after(v) for v in self.value]
|
|
94
|
-
def fwd(s: Any, a: ManyResult[A]) -> Tuple[Any, List[A]]:
|
|
95
|
-
assert a == self, f"Expected {self}, got {a}"
|
|
96
|
-
return s, [inner_b[i].forward(s, v)[1] for i, v in enumerate(a.value)]
|
|
97
|
-
|
|
98
|
-
def inv(s: Any, a: List[A]) -> Tuple[Any, ManyResult[A]]:
|
|
99
|
-
assert isinstance(a, list), f"Expected list, got {type(a)}"
|
|
100
|
-
ret = [inner_b[i].inverse(s, v)[1] for i, v in enumerate(a)]
|
|
101
|
-
if len(ret) <= len(inner_b):
|
|
102
|
-
return s, ManyResult(value=tuple(ret))
|
|
103
|
-
else:
|
|
104
|
-
extra = [inner_b[-1].inverse(s, v)[1] for v in a[len(inner_b):]]
|
|
105
|
-
return s, ManyResult(value=tuple(ret + extra))
|
|
106
|
-
|
|
107
|
-
ret = Bimap(
|
|
108
|
-
forward=fwd,
|
|
109
|
-
inverse=inv
|
|
110
|
-
)
|
|
111
|
-
return before(self) >> ret >> after(self)
|
|
112
|
-
|
|
113
|
-
@dataclass(eq=True, frozen=True)
|
|
114
|
-
class OrResult(Generic[A], StructuralResult):
|
|
115
|
-
value: A
|
|
116
|
-
def bimap(self,
|
|
117
|
-
before: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity(),
|
|
118
|
-
after: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity()
|
|
119
|
-
) -> Bimap[Any, OrResult[A], Any]:
|
|
120
|
-
inner_b = self.value.bimap(before, after) if isinstance(self.value, StructuralResult) else before(self.value) >> after(self.value)
|
|
121
|
-
def fwd(s: Any, a: OrResult[A]) -> Tuple[Any, Any]:
|
|
122
|
-
assert a == self, f"Expected {self}, got {a}"
|
|
123
|
-
return inner_b.forward(s, a.value)
|
|
124
|
-
|
|
125
|
-
def inv(s: Any, a: Any) -> Tuple[Any, OrResult[A]]:
|
|
126
|
-
inner_s, inner_v = inner_b.inverse(s, a)
|
|
127
|
-
return inner_s, OrResult(value=inner_v)
|
|
128
|
-
|
|
129
|
-
ret = Bimap(
|
|
130
|
-
forward=fwd,
|
|
131
|
-
inverse=inv
|
|
132
|
-
)
|
|
133
|
-
return before(self) >> ret >> after(self)
|
|
134
|
-
class ThenKind(Enum):
|
|
135
|
-
BOTH = '+'
|
|
136
|
-
LEFT = '//'
|
|
137
|
-
RIGHT = '>>'
|
|
138
|
-
|
|
139
|
-
@dataclass(eq=True, frozen=True)
|
|
140
|
-
class ThenResult(Generic[A, B], StructuralResult):
|
|
141
|
-
kind: ThenKind
|
|
142
|
-
left: A
|
|
143
|
-
right: B
|
|
144
|
-
def arity(self)->int:
|
|
145
|
-
if self.kind == ThenKind.LEFT:
|
|
146
|
-
return self.left.arity() if isinstance(self.left, ThenResult) else 1
|
|
147
|
-
elif self.kind == ThenKind.RIGHT:
|
|
148
|
-
return self.right.arity() if isinstance(self.right, ThenResult) else 1
|
|
149
|
-
elif self.kind == ThenKind.BOTH:
|
|
150
|
-
left_arity = self.left.arity() if isinstance(self.left, ThenResult) else 1
|
|
151
|
-
right_arity = self.right.arity() if isinstance(self.right, ThenResult) else 1
|
|
152
|
-
return left_arity + right_arity
|
|
153
|
-
else:
|
|
154
|
-
return 1
|
|
155
|
-
|
|
156
|
-
def bimap(self,
|
|
157
|
-
before: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity(),
|
|
158
|
-
after: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity()
|
|
159
|
-
) -> Bimap[Any, ThenResult[A, B], Tuple[Any, ...] | Any]:
|
|
160
|
-
kind = self.kind
|
|
161
|
-
lb = self.left.bimap(before, after) if isinstance(self.left, StructuralResult) else before(self.left) >> after(self.left)
|
|
162
|
-
rb = self.right.bimap(before, after) if isinstance(self.right, StructuralResult) else before(self.right) >> after(self.right)
|
|
163
|
-
left_size = self.left.arity() if isinstance(self.left, ThenResult) else 1
|
|
164
|
-
right_size = self.right.arity() if isinstance(self.right, ThenResult) else 1
|
|
165
|
-
def fwd(s : S, a : ThenResult[A, B]) -> Tuple[S, Tuple[Any, ...] | Any]:
|
|
166
|
-
assert a == self, f"Expected {self}, got {a}"
|
|
167
|
-
match kind:
|
|
168
|
-
case ThenKind.LEFT:
|
|
169
|
-
return lb.forward(s, a.left)
|
|
170
|
-
case ThenKind.RIGHT:
|
|
171
|
-
return rb.forward(s, a.right)
|
|
172
|
-
case ThenKind.BOTH:
|
|
173
|
-
s1, left_v = lb.forward(s, a.left)
|
|
174
|
-
s2, right_v = rb.forward(s1, a.right)
|
|
175
|
-
left_v = (left_v,) if not isinstance(a.left, ThenResult) else left_v
|
|
176
|
-
right_v = (right_v,) if not isinstance(a.right, ThenResult) else right_v
|
|
177
|
-
return s2, left_v + right_v
|
|
178
|
-
|
|
179
|
-
def inv(s: S, b: Tuple[Any, ...] | Any) -> Tuple[S, ThenResult[A, B]]:
|
|
180
|
-
match kind:
|
|
181
|
-
case ThenKind.LEFT:
|
|
182
|
-
s1, lv = lb.inverse(s, b)
|
|
183
|
-
return s1, replace(self, left=lv)
|
|
184
|
-
case ThenKind.RIGHT:
|
|
185
|
-
s1, rv = rb.inverse(s, b)
|
|
186
|
-
return s1, replace(self, right=rv)
|
|
187
|
-
case ThenKind.BOTH:
|
|
188
|
-
lraw = b[:left_size]
|
|
189
|
-
rraw = b[left_size:left_size + right_size]
|
|
190
|
-
lraw = lraw[0] if left_size == 1 else lraw
|
|
191
|
-
rraw = rraw[0] if right_size == 1 else rraw
|
|
192
|
-
s1, lv = lb.inverse(s, lraw)
|
|
193
|
-
s2, rv = rb.inverse(s1, rraw)
|
|
194
|
-
return s2, replace(self, left=lv, right=rv)
|
|
195
|
-
|
|
196
|
-
ret: Bimap[Any, Any, Any] = Bimap(
|
|
197
|
-
forward=fwd,
|
|
198
|
-
inverse=inv
|
|
199
|
-
)
|
|
200
|
-
return before(self) >> ret >> after(self)
|
|
201
|
-
|
|
202
17
|
InProgress = object() # Marker for in-progress state, used to prevent re-entrance in recursive calls
|
|
203
18
|
L = TypeVar('L') # Left type for combined results
|
|
204
19
|
R = TypeVar('R') # Right type for combined results
|
|
@@ -242,7 +57,14 @@ class Error:
|
|
|
242
57
|
state=state,
|
|
243
58
|
previous=self
|
|
244
59
|
)
|
|
245
|
-
|
|
60
|
+
def to_list(self)->List[Dict[str, Any]]:
|
|
61
|
+
lst = []
|
|
62
|
+
current: Optional[Error] = self
|
|
63
|
+
while current is not None:
|
|
64
|
+
d = asdict(current)
|
|
65
|
+
lst.append({k:v for k,v in d.items() if v is not None and k != 'previous'})
|
|
66
|
+
current = current.previous
|
|
67
|
+
return lst
|
|
246
68
|
|
|
247
69
|
|
|
248
70
|
@dataclass(frozen=True)
|
|
@@ -423,6 +245,12 @@ class Algebra(ABC, Generic[A, S]):
|
|
|
423
245
|
return cast(Either[Any, Tuple[B, S]], parsed)
|
|
424
246
|
return self.__class__(map_run, name=self.name) # type: ignore
|
|
425
247
|
|
|
248
|
+
def map_all(self, f: Callable[[Either[Any, Tuple[A, S]]], Either[Any, Tuple[B, S]]])->Algebra[B, S]:
|
|
249
|
+
def map_all_run(input: S, use_cache:bool) -> Either[Any, Tuple[B, S]]:
|
|
250
|
+
parsed = self.run(input, use_cache)
|
|
251
|
+
return f(parsed)
|
|
252
|
+
return self.__class__(map_all_run, name=self.name) # type: ignore
|
|
253
|
+
|
|
426
254
|
def map_error(self, f: Callable[[Optional[Any]], Any]) -> Algebra[A, S]:
|
|
427
255
|
def map_error_run(input: S, use_cache:bool) -> Either[Any, Tuple[A, S]]:
|
|
428
256
|
parsed = self.run(input, use_cache)
|
syncraft/ast.py
CHANGED
|
@@ -4,15 +4,224 @@ from __future__ import annotations
|
|
|
4
4
|
import re
|
|
5
5
|
from typing import (
|
|
6
6
|
Optional, Any, TypeVar, Tuple, runtime_checkable,
|
|
7
|
-
|
|
8
|
-
)
|
|
9
|
-
from syncraft.algebra import (
|
|
10
|
-
OrResult,ThenResult, ManyResult, ThenKind,NamedResult, StructuralResult
|
|
7
|
+
Dict, Generic, Callable, Union, cast, List, Protocol, Type
|
|
11
8
|
)
|
|
9
|
+
|
|
10
|
+
|
|
12
11
|
from dataclasses import dataclass, replace, is_dataclass, asdict
|
|
13
12
|
from enum import Enum
|
|
14
13
|
from functools import cached_property
|
|
14
|
+
from syncraft.constraint import Binding, Variable, Bindable
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
A = TypeVar('A')
|
|
19
|
+
B = TypeVar('B')
|
|
20
|
+
C = TypeVar('C')
|
|
21
|
+
S = TypeVar('S', bound=Bindable)
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True)
|
|
24
|
+
class Reducer(Generic[A, S]):
|
|
25
|
+
run_f: Callable[[A, S], S]
|
|
26
|
+
def __call__(self, a: A, s: S) -> S:
|
|
27
|
+
return self.run_f(a, s)
|
|
28
|
+
|
|
29
|
+
def map(self, f: Callable[[B], A]) -> Reducer[B, S]:
|
|
30
|
+
def map_run(b: B, s: S) -> S:
|
|
31
|
+
return self(f(b), s)
|
|
32
|
+
return Reducer(map_run)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass(frozen=True)
|
|
37
|
+
class Bimap(Generic[A, B]):
|
|
38
|
+
run_f: Callable[[A], Tuple[B, Callable[[B], A]]]
|
|
39
|
+
def __call__(self, a: A) -> Tuple[B, Callable[[B], A]]:
|
|
40
|
+
return self.run_f(a)
|
|
41
|
+
def __rshift__(self, other: Bimap[B, C]) -> Bimap[A, C]:
|
|
42
|
+
def then_run(a: A) -> Tuple[C, Callable[[C], A]]:
|
|
43
|
+
b, inv1 = self(a)
|
|
44
|
+
c, inv2 = other(b)
|
|
45
|
+
def inv(c2: C) -> A:
|
|
46
|
+
return inv1(inv2(c2))
|
|
47
|
+
return c, inv
|
|
48
|
+
return Bimap(then_run)
|
|
49
|
+
@staticmethod
|
|
50
|
+
def const(a: B)->Bimap[B, B]:
|
|
51
|
+
return Bimap(lambda _: (a, lambda b: b))
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def identity()->Bimap[Any, Any]:
|
|
55
|
+
return Bimap(lambda a: (a, lambda b: b))
|
|
56
|
+
|
|
57
|
+
@staticmethod
|
|
58
|
+
def when(cond: Callable[[A], bool],
|
|
59
|
+
then: Bimap[A, B],
|
|
60
|
+
otherwise: Optional[Bimap[A, C]] = None) -> Bimap[A, A | B | C]:
|
|
61
|
+
def when_run(a:A) -> Tuple[A | B | C, Callable[[A | B | C], A]]:
|
|
62
|
+
bimap = then if cond(a) else (otherwise if otherwise is not None else Bimap.identity())
|
|
63
|
+
abc, inv = bimap(a)
|
|
64
|
+
def inv_f(b: Any) -> A:
|
|
65
|
+
return inv(b)
|
|
66
|
+
return abc, inv_f
|
|
67
|
+
return Bimap(when_run)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass(frozen=True)
|
|
72
|
+
class Biarrow(Generic[S, A, B]):
|
|
73
|
+
forward: Callable[[S, A], Tuple[S, B]]
|
|
74
|
+
inverse: Callable[[S, B], Tuple[S, A]]
|
|
75
|
+
def __rshift__(self, other: Biarrow[S, B, C]) -> Biarrow[S, A, C]:
|
|
76
|
+
def fwd(s: S, a: A) -> Tuple[S, C]:
|
|
77
|
+
s1, b = self.forward(s, a)
|
|
78
|
+
return other.forward(s1, b)
|
|
79
|
+
def inv(s: S, c: C) -> Tuple[S, A]:
|
|
80
|
+
s1, b = other.inverse(s, c)
|
|
81
|
+
return self.inverse(s1, b)
|
|
82
|
+
return Biarrow(
|
|
83
|
+
forward=fwd,
|
|
84
|
+
inverse=inv
|
|
85
|
+
)
|
|
86
|
+
@staticmethod
|
|
87
|
+
def identity()->Biarrow[S, A, A]:
|
|
88
|
+
return Biarrow(
|
|
89
|
+
forward=lambda s, x: (s, x),
|
|
90
|
+
inverse=lambda s, y: (s, y)
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
@staticmethod
|
|
94
|
+
def when(condition: Callable[..., bool],
|
|
95
|
+
then: Biarrow[S, A, B],
|
|
96
|
+
otherwise: Optional[Biarrow[S, A, B]] = None) -> Callable[..., Biarrow[S, A, B]]:
|
|
97
|
+
def _when(*args:Any, **kwargs:Any) -> Biarrow[S, A, B]:
|
|
98
|
+
return then if condition(*args, **kwargs) else (otherwise or Biarrow.identity())
|
|
99
|
+
return _when
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class StructuralResult:
|
|
104
|
+
|
|
105
|
+
def bimap(self, f: Bimap[Any, Any]=Bimap.identity())->Tuple[Any, Callable[[Any], Any]]:
|
|
106
|
+
return f(self)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@dataclass(frozen=True)
|
|
110
|
+
class MarkedResult(Generic[A], StructuralResult):
|
|
111
|
+
name: str
|
|
112
|
+
value: A
|
|
113
|
+
def bimap(self, f: Bimap[A, B]=Bimap.identity())->Tuple[MarkedResult[B], Callable[[MarkedResult[B]], MarkedResult[A]]]:
|
|
114
|
+
b, inv = self.value.bimap(f) if isinstance(self.value, StructuralResult) else f(self.value)
|
|
115
|
+
return MarkedResult(name=self.name, value=b), lambda b: replace(self, value=inv(b.value))
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@dataclass(eq=True, frozen=True)
|
|
119
|
+
class ManyResult(Generic[A], StructuralResult):
|
|
120
|
+
value: Tuple[A, ...]
|
|
121
|
+
def bimap(self, f: Bimap[A, B]=Bimap.identity())->Tuple[List[B], Callable[[List[B]], ManyResult[A]]]:
|
|
122
|
+
assert self.value
|
|
123
|
+
forward = [v.bimap(f) if isinstance(v, StructuralResult) else f(v) for v in self.value]
|
|
124
|
+
def invf(b: List[B]) -> ManyResult[A]:
|
|
125
|
+
assert len(b) <= len(forward)
|
|
126
|
+
return replace(self, value=tuple([forward[i][1](bb) for i, bb in enumerate(b)]))
|
|
127
|
+
return [b for b, _ in forward], invf
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@dataclass(eq=True, frozen=True)
|
|
132
|
+
class OrResult(Generic[A], StructuralResult):
|
|
133
|
+
value: A
|
|
134
|
+
def bimap(self, f: Bimap[A, B]=Bimap.identity())->Tuple[B, Callable[[B], OrResult[A]]]:
|
|
135
|
+
b, inv = self.value.bimap(f) if isinstance(self.value, StructuralResult) else f(self.value)
|
|
136
|
+
return b, lambda b: replace(self, value=inv(b))
|
|
137
|
+
|
|
138
|
+
class ThenKind(Enum):
|
|
139
|
+
BOTH = '+'
|
|
140
|
+
LEFT = '//'
|
|
141
|
+
RIGHT = '>>'
|
|
142
|
+
|
|
143
|
+
FlatThen = Tuple[Any, ...]
|
|
144
|
+
MarkedThen = Tuple[Dict[str, Any] | Any, FlatThen]
|
|
145
|
+
|
|
146
|
+
@dataclass(eq=True, frozen=True)
|
|
147
|
+
class ThenResult(Generic[A, B], StructuralResult):
|
|
148
|
+
kind: ThenKind
|
|
149
|
+
left: A
|
|
150
|
+
right: B
|
|
151
|
+
@staticmethod
|
|
152
|
+
def collect_marked(a: FlatThen, f: Optional[Callable[..., Any]] = None)->Tuple[MarkedThen, Callable[[MarkedThen], FlatThen]]:
|
|
153
|
+
index: List[str | int] = []
|
|
154
|
+
named_count = 0
|
|
155
|
+
for i, v in enumerate(a):
|
|
156
|
+
if isinstance(v, MarkedResult):
|
|
157
|
+
index.append(v.name)
|
|
158
|
+
named_count += 1
|
|
159
|
+
else:
|
|
160
|
+
index.append(i - named_count)
|
|
161
|
+
named = {v.name: v.value for v in a if isinstance(v, MarkedResult)}
|
|
162
|
+
unnamed = [v for v in a if not isinstance(v, MarkedResult)]
|
|
163
|
+
if f is None:
|
|
164
|
+
ret = (named, tuple(unnamed))
|
|
165
|
+
else:
|
|
166
|
+
ret = (f(**named), tuple(unnamed))
|
|
167
|
+
def invf(b: MarkedThen) -> Tuple[Any, ...]:
|
|
168
|
+
named_value, unnamed_value = b
|
|
169
|
+
assert isinstance(named_value, dict) or is_dataclass(named_value), f"Expected dict or dataclass for named values, got {type(named_value)}"
|
|
170
|
+
if is_dataclass(named_value):
|
|
171
|
+
named_dict = named | asdict(cast(Any, named_value))
|
|
172
|
+
else:
|
|
173
|
+
named_dict = named | named_value
|
|
174
|
+
ret = []
|
|
175
|
+
for x in index:
|
|
176
|
+
if isinstance(x, str):
|
|
177
|
+
assert x in named_dict, f"Missing named value: {x}"
|
|
178
|
+
ret.append(named_dict[x])
|
|
179
|
+
else:
|
|
180
|
+
assert 0 <= x < len(unnamed_value), f"Missing unnamed value at index: {x}"
|
|
181
|
+
ret.append(unnamed_value[x])
|
|
182
|
+
return tuple(ret)
|
|
183
|
+
return ret, invf
|
|
184
|
+
|
|
185
|
+
def bimap(self, f: Bimap[Any, Any]=Bimap.identity()) -> Tuple[MarkedThen, Callable[[MarkedThen], ThenResult[A, B]]]:
|
|
186
|
+
match self.kind:
|
|
187
|
+
case ThenKind.LEFT:
|
|
188
|
+
lb, linv = self.left.bimap(f) if isinstance(self.left, StructuralResult) else f(self.left)
|
|
189
|
+
return lb, lambda b: replace(self, left=linv(b))
|
|
190
|
+
case ThenKind.RIGHT:
|
|
191
|
+
rb, rinv = self.right.bimap(f) if isinstance(self.right, StructuralResult) else f(self.right)
|
|
192
|
+
return rb, lambda b: replace(self, right=rinv(b))
|
|
193
|
+
case ThenKind.BOTH:
|
|
194
|
+
lb, linv = self.left.bimap(f) if isinstance(self.left, StructuralResult) else f(self.left)
|
|
195
|
+
rb, rinv = self.right.bimap(f) if isinstance(self.right, StructuralResult) else f(self.right)
|
|
196
|
+
left_v = (lb,) if not isinstance(self.left, ThenResult) else lb
|
|
197
|
+
right_v = (rb,) if not isinstance(self.right, ThenResult) else rb
|
|
198
|
+
def invf(b: Tuple[Any, ...]) -> ThenResult[A, B]:
|
|
199
|
+
left_size = self.left.arity() if isinstance(self.left, ThenResult) else 1
|
|
200
|
+
right_size = self.right.arity() if isinstance(self.right, ThenResult) else 1
|
|
201
|
+
lraw = b[:left_size]
|
|
202
|
+
rraw = b[left_size:left_size + right_size]
|
|
203
|
+
lraw = lraw[0] if left_size == 1 else lraw
|
|
204
|
+
rraw = rraw[0] if right_size == 1 else rraw
|
|
205
|
+
la = linv(lraw)
|
|
206
|
+
ra = rinv(rraw)
|
|
207
|
+
return replace(self, left=la, right=ra)
|
|
208
|
+
return left_v + right_v, invf
|
|
209
|
+
# data, func = ThenResult.collect_marked(left_v + right_v)
|
|
210
|
+
# return data, lambda d: invf(func(d))
|
|
211
|
+
|
|
15
212
|
|
|
213
|
+
def arity(self)->int:
|
|
214
|
+
if self.kind == ThenKind.LEFT:
|
|
215
|
+
return self.left.arity() if isinstance(self.left, ThenResult) else 1
|
|
216
|
+
elif self.kind == ThenKind.RIGHT:
|
|
217
|
+
return self.right.arity() if isinstance(self.right, ThenResult) else 1
|
|
218
|
+
elif self.kind == ThenKind.BOTH:
|
|
219
|
+
left_arity = self.left.arity() if isinstance(self.left, ThenResult) else 1
|
|
220
|
+
right_arity = self.right.arity() if isinstance(self.right, ThenResult) else 1
|
|
221
|
+
return left_arity + right_arity
|
|
222
|
+
else:
|
|
223
|
+
return 1
|
|
224
|
+
|
|
16
225
|
@runtime_checkable
|
|
17
226
|
class TokenProtocol(Protocol):
|
|
18
227
|
@property
|
|
@@ -55,11 +264,14 @@ T = TypeVar('T', bound=TokenProtocol)
|
|
|
55
264
|
|
|
56
265
|
ParseResult = Union[
|
|
57
266
|
ThenResult['ParseResult[T]', 'ParseResult[T]'],
|
|
58
|
-
|
|
267
|
+
MarkedResult['ParseResult[T]'],
|
|
59
268
|
ManyResult['ParseResult[T]'],
|
|
60
269
|
OrResult['ParseResult[T]'],
|
|
61
270
|
T,
|
|
62
271
|
]
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
|
|
63
275
|
@dataclass(frozen=True)
|
|
64
276
|
class AST(Generic[T]):
|
|
65
277
|
focus: ParseResult[T]
|
|
@@ -68,24 +280,20 @@ class AST(Generic[T]):
|
|
|
68
280
|
|
|
69
281
|
def bimap(self)->Tuple[Any, Callable[[Any], AST[T]]]:
|
|
70
282
|
if isinstance(self.focus, StructuralResult):
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
def inverse(data: Any) -> AST[T]:
|
|
74
|
-
s1, v1 = b.inverse(None, data)
|
|
75
|
-
return replace(self, focus=v1)
|
|
76
|
-
return v, inverse
|
|
283
|
+
data, invf = self.focus.bimap()
|
|
284
|
+
return data, lambda x: replace(self, focus=invf(x))
|
|
77
285
|
else:
|
|
78
286
|
return self.focus, lambda x: replace(self, focus=x)
|
|
79
287
|
|
|
80
288
|
def wrapper(self)-> Callable[[Any], Any]:
|
|
81
|
-
if isinstance(self.focus,
|
|
82
|
-
focus = cast(
|
|
83
|
-
return lambda x:
|
|
289
|
+
if isinstance(self.focus, MarkedResult):
|
|
290
|
+
focus = cast(MarkedResult[Any], self.focus)
|
|
291
|
+
return lambda x: MarkedResult(name = focus.name, value = x)
|
|
84
292
|
else:
|
|
85
293
|
return lambda x: x
|
|
86
294
|
|
|
87
295
|
def is_named(self) -> bool:
|
|
88
|
-
return isinstance(self.focus,
|
|
296
|
+
return isinstance(self.focus, MarkedResult)
|
|
89
297
|
|
|
90
298
|
def left(self) -> Optional[AST[T]]:
|
|
91
299
|
match self.focus:
|
|
@@ -114,13 +322,13 @@ class AST(Generic[T]):
|
|
|
114
322
|
return replace(self, focus=value, parent=self, pruned=self.pruned)
|
|
115
323
|
else:
|
|
116
324
|
raise IndexError(f"Index {index} out of bounds for OrResult")
|
|
117
|
-
case
|
|
325
|
+
case MarkedResult(value=value):
|
|
118
326
|
return replace(self, focus=value, parent=self, pruned=self.pruned)
|
|
119
327
|
case _:
|
|
120
328
|
raise TypeError(f"Invalid focus type({self.focus}) for down traversal")
|
|
121
329
|
|
|
122
330
|
def how_many(self)->int:
|
|
123
|
-
focus = self.focus.value if isinstance(self.focus,
|
|
331
|
+
focus = self.focus.value if isinstance(self.focus, MarkedResult) else self.focus
|
|
124
332
|
match focus:
|
|
125
333
|
case ManyResult(value=children):
|
|
126
334
|
return len(children)
|
syncraft/constraint.py
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Callable, Generic, Tuple, TypeVar, Optional, Any, Protocol
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from dataclasses import dataclass, field, replace
|
|
5
|
+
import collections.abc
|
|
6
|
+
from collections import defaultdict
|
|
7
|
+
from itertools import product
|
|
8
|
+
|
|
9
|
+
K = TypeVar('K')
|
|
10
|
+
V = TypeVar('V')
|
|
11
|
+
class FrozenDict(collections.abc.Mapping, Generic[K, V]):
|
|
12
|
+
def __init__(self, *args, **kwargs):
|
|
13
|
+
self._data = dict(*args, **kwargs)
|
|
14
|
+
self._hash = None
|
|
15
|
+
def __getitem__(self, key):
|
|
16
|
+
return self._data[key]
|
|
17
|
+
|
|
18
|
+
def __iter__(self):
|
|
19
|
+
return iter(self._data)
|
|
20
|
+
|
|
21
|
+
def __len__(self):
|
|
22
|
+
return len(self._data)
|
|
23
|
+
|
|
24
|
+
def __hash__(self):
|
|
25
|
+
if self._hash is None:
|
|
26
|
+
self._hash = hash(frozenset(self._data.items()))
|
|
27
|
+
return self._hash
|
|
28
|
+
|
|
29
|
+
def __eq__(self, other):
|
|
30
|
+
if isinstance(other, collections.abc.Mapping):
|
|
31
|
+
return self._data == other
|
|
32
|
+
return NotImplemented
|
|
33
|
+
|
|
34
|
+
def __repr__(self):
|
|
35
|
+
return f"{self.__class__.__name__}({self._data})"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass(frozen=True)
|
|
41
|
+
class Expr:
|
|
42
|
+
left: Any
|
|
43
|
+
op: str
|
|
44
|
+
right: Any
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass(frozen=True)
|
|
48
|
+
class Variable:
|
|
49
|
+
name: Optional[str] = None
|
|
50
|
+
_root: Optional[Variable] = field(default=None, compare=False, repr=False)
|
|
51
|
+
_mapf: Optional[Callable[[Any], Any]] = field(default=None, compare=False, repr=False)
|
|
52
|
+
|
|
53
|
+
def __post_init__(self):
|
|
54
|
+
if self._root is None:
|
|
55
|
+
object.__setattr__(self, '_root', self)
|
|
56
|
+
|
|
57
|
+
def raw(self, b:'BoundVar') -> Tuple[Any, ...]:
|
|
58
|
+
assert self._root is not None, "_rawf can not be None"
|
|
59
|
+
return b.get(self._root, ())
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def map(self, f: Callable[[Any], Any]) -> "Variable":
|
|
63
|
+
if self._mapf is None:
|
|
64
|
+
return replace(self, _mapf=f)
|
|
65
|
+
else:
|
|
66
|
+
oldf = self._mapf
|
|
67
|
+
return replace(self, _mapf=lambda a: f(oldf(a)))
|
|
68
|
+
|
|
69
|
+
def get(self, b: 'BoundVar') -> Tuple[Any, ...]:
|
|
70
|
+
vals = self.raw(b)
|
|
71
|
+
if self._mapf is not None:
|
|
72
|
+
return tuple(self._mapf(v) for v in vals)
|
|
73
|
+
else:
|
|
74
|
+
return vals
|
|
75
|
+
|
|
76
|
+
def __call__(self, b:'BoundVar', raw:bool=False) -> Any:
|
|
77
|
+
if raw:
|
|
78
|
+
return self.raw(b)
|
|
79
|
+
else:
|
|
80
|
+
return self.get(b)
|
|
81
|
+
|
|
82
|
+
def __eq__(self, other):
|
|
83
|
+
return Expr(self, '==', other)
|
|
84
|
+
def __ne__(self, other):
|
|
85
|
+
return Expr(self, '!=', other)
|
|
86
|
+
def __lt__(self, other):
|
|
87
|
+
return Expr(self, '<', other)
|
|
88
|
+
def __le__(self, other):
|
|
89
|
+
return Expr(self, '<=', other)
|
|
90
|
+
def __gt__(self, other):
|
|
91
|
+
return Expr(self, '>', other)
|
|
92
|
+
def __ge__(self, other):
|
|
93
|
+
return Expr(self, '>=', other)
|
|
94
|
+
|
|
95
|
+
BoundVar = FrozenDict[Variable, Tuple[Any, ...]]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@dataclass(frozen=True)
|
|
99
|
+
class Binding:
|
|
100
|
+
bindings : frozenset[Tuple[Variable, Any]] = frozenset()
|
|
101
|
+
def bind(self, var: Variable, node: Any) -> Binding:
|
|
102
|
+
new_binding = set(self.bindings)
|
|
103
|
+
new_binding.add((var, node))
|
|
104
|
+
return Binding(bindings=frozenset(new_binding))
|
|
105
|
+
|
|
106
|
+
def to_dict(self)->BoundVar:
|
|
107
|
+
ret = defaultdict(list)
|
|
108
|
+
for var, node in self.bindings:
|
|
109
|
+
ret[var].append(node)
|
|
110
|
+
return FrozenDict({k: tuple(vs) for k, vs in ret.items()})
|
|
111
|
+
|
|
112
|
+
class Bindable(Protocol):
|
|
113
|
+
binding: Binding
|
|
114
|
+
def bind(self, var: Variable, node: Any) -> Any: ...
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class Quantifier(Enum):
|
|
118
|
+
FORALL = "forall"
|
|
119
|
+
EXISTS = "exists"
|
|
120
|
+
|
|
121
|
+
@dataclass(frozen=True)
|
|
122
|
+
class Constraint:
|
|
123
|
+
run_f: Callable[[BoundVar], bool]
|
|
124
|
+
name: str = ""
|
|
125
|
+
def __call__(self, bound: BoundVar)->bool:
|
|
126
|
+
return self.run_f(bound)
|
|
127
|
+
def __and__(self, other: Constraint) -> Constraint:
|
|
128
|
+
return Constraint(
|
|
129
|
+
run_f=lambda bound: self(bound) and other(bound),
|
|
130
|
+
name=f"({self.name} && {other.name})"
|
|
131
|
+
)
|
|
132
|
+
def __or__(self, other: Constraint) -> Constraint:
|
|
133
|
+
return Constraint(
|
|
134
|
+
run_f=lambda bound: self(bound) or other(bound),
|
|
135
|
+
name=f"({self.name} || {other.name})"
|
|
136
|
+
)
|
|
137
|
+
def __xor__(self, other: Constraint) -> Constraint:
|
|
138
|
+
return Constraint(
|
|
139
|
+
run_f=lambda bound: self(bound) ^ other(bound),
|
|
140
|
+
name=f"({self.name} ^ {other.name})"
|
|
141
|
+
)
|
|
142
|
+
def __invert__(self) -> Constraint:
|
|
143
|
+
return Constraint(
|
|
144
|
+
run_f=lambda bound: not self(bound),
|
|
145
|
+
name=f"!({self.name})"
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
@classmethod
|
|
149
|
+
def predicate(cls, f: Callable[..., bool],*, name: Optional[str] = None, quant: Quantifier = Quantifier.FORALL)->Callable[..., Constraint]:
|
|
150
|
+
def wrapper(*args: Any, **kwargs:Any) -> Constraint:
|
|
151
|
+
arg_list = list(args)
|
|
152
|
+
kw_list = [(k, v) for k, v in kwargs.items()]
|
|
153
|
+
def run_f(bound: BoundVar) -> bool:
|
|
154
|
+
# positional argument values
|
|
155
|
+
pos_values = [
|
|
156
|
+
arg.get(bound) if isinstance(arg, Variable) else (arg,)
|
|
157
|
+
for arg in arg_list
|
|
158
|
+
]
|
|
159
|
+
# keyword argument values
|
|
160
|
+
kw_keys, kw_values = zip(*[
|
|
161
|
+
(k, v.get(bound) if isinstance(v, Variable) else (v,))
|
|
162
|
+
for k, v in kw_list
|
|
163
|
+
]) if kw_list else ([], [])
|
|
164
|
+
|
|
165
|
+
# Cartesian product over all argument values
|
|
166
|
+
all_combos = product(*pos_values, *kw_values)
|
|
167
|
+
|
|
168
|
+
# evaluate predicate on each combination
|
|
169
|
+
def eval_combo(combo):
|
|
170
|
+
pos_args = combo[:len(pos_values)]
|
|
171
|
+
kw_args = dict(zip(kw_keys, combo[len(pos_values):]))
|
|
172
|
+
return f(*pos_args, **kw_args)
|
|
173
|
+
|
|
174
|
+
if quant is Quantifier.EXISTS:
|
|
175
|
+
return any(eval_combo(c) for c in all_combos)
|
|
176
|
+
else:
|
|
177
|
+
return all(eval_combo(c) for c in all_combos)
|
|
178
|
+
return cls(run_f=run_f, name = name or f.__name__)
|
|
179
|
+
return wrapper
|
|
180
|
+
|
|
181
|
+
@classmethod
|
|
182
|
+
def forall(cls, f: Callable[..., bool], name: Optional[str] = None) -> Callable[..., Constraint]:
|
|
183
|
+
return cls.predicate(f, name=name, quant=Quantifier.FORALL)
|
|
184
|
+
|
|
185
|
+
@classmethod
|
|
186
|
+
def exists(cls, f: Callable[..., bool], name: Optional[str] = None):
|
|
187
|
+
return cls.predicate(f, name=name, quant=Quantifier.EXISTS)
|
|
188
|
+
|
|
189
|
+
|
syncraft/diagnostic.py
CHANGED
|
@@ -10,7 +10,7 @@ from sqlglot.expressions import Expression
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def rich_error(err: Error)->None:
|
|
13
|
-
lst = err.to_list(
|
|
13
|
+
lst = err.to_list()
|
|
14
14
|
root, leaf = lst[0], lst[-1]
|
|
15
15
|
tbl = RichTable(title="Parser Error", show_lines=True)
|
|
16
16
|
tbl.add_column("Root Parser Field", style="blue")
|
|
@@ -26,9 +26,9 @@ def rich_error(err: Error)->None:
|
|
|
26
26
|
print(tbl)
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
def rich_parser(p:
|
|
29
|
+
def rich_parser(p: Syntax)-> None:
|
|
30
30
|
print("Parser Debug Information:")
|
|
31
|
-
print(p.
|
|
31
|
+
print(p.meta.to_string(lambda _ : True) or repr(p))
|
|
32
32
|
|
|
33
33
|
def rich_debug(this: Algebra[Any, ParserState[Any]],
|
|
34
34
|
state: ParserState[Any],
|
|
@@ -44,8 +44,8 @@ def rich_debug(this: Algebra[Any, ParserState[Any]],
|
|
|
44
44
|
return prefix + value.sql()
|
|
45
45
|
elif isinstance(value, Token):
|
|
46
46
|
return prefix + f"{value.token_type.name}({value.text})"
|
|
47
|
-
elif isinstance(value,
|
|
48
|
-
return prefix + (value.
|
|
47
|
+
elif isinstance(value, Syntax):
|
|
48
|
+
return prefix + (value.meta.to_string(lambda _ : True) or 'N/A')
|
|
49
49
|
else:
|
|
50
50
|
return prefix + str(value)
|
|
51
51
|
|
syncraft/finder.py
CHANGED
|
@@ -1,96 +1,34 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from typing import (
|
|
4
|
-
Any, Tuple, Optional,
|
|
5
|
-
List, Generator as YieldGen
|
|
4
|
+
Any, Tuple, Optional, Generator as YieldGen
|
|
6
5
|
)
|
|
7
|
-
from dataclasses import dataclass
|
|
6
|
+
from dataclasses import dataclass, replace
|
|
8
7
|
from syncraft.algebra import (
|
|
9
|
-
Algebra, Either,
|
|
10
|
-
OrResult, ManyResult, ThenResult,
|
|
8
|
+
Algebra, Either, Right,
|
|
9
|
+
OrResult, ManyResult, ThenResult, MarkedResult
|
|
11
10
|
)
|
|
12
11
|
|
|
13
|
-
from syncraft.ast import T, ParseResult,
|
|
14
|
-
from syncraft.generator import GenState,
|
|
12
|
+
from syncraft.ast import T, ParseResult, AST
|
|
13
|
+
from syncraft.generator import GenState, Generator
|
|
15
14
|
from sqlglot import TokenType
|
|
16
15
|
from syncraft.syntax import Syntax
|
|
17
16
|
import re
|
|
18
17
|
|
|
19
|
-
@dataclass(frozen=True)
|
|
20
|
-
class Finder(Algebra[ParseResult[T], GenState[T]]):
|
|
21
|
-
def flat_map(self, f: Callable[[ParseResult[T]], Algebra[B, GenState[T]]]) -> Algebra[B, GenState[T]]:
|
|
22
|
-
def flat_map_run(original: GenState[T], use_cache:bool) -> Either[Any, Tuple[B, GenState[T]]]:
|
|
23
|
-
wrapper = original.wrapper()
|
|
24
|
-
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
|
|
25
|
-
try:
|
|
26
|
-
lft = input.left()
|
|
27
|
-
match self.run(lft, use_cache=use_cache):
|
|
28
|
-
case Left(error):
|
|
29
|
-
return Left(error)
|
|
30
|
-
case Right((value, next_input)):
|
|
31
|
-
r = input.right()
|
|
32
|
-
match f(value).run(r, use_cache):
|
|
33
|
-
case Left(e):
|
|
34
|
-
return Left(e)
|
|
35
|
-
case Right((result, next_input)):
|
|
36
|
-
return Right((wrapper(result), next_input))
|
|
37
|
-
raise ValueError("flat_map should always return a value or an error.")
|
|
38
|
-
except Exception as e:
|
|
39
|
-
return Left(Error(
|
|
40
|
-
message=str(e),
|
|
41
|
-
this=self,
|
|
42
|
-
state=original,
|
|
43
|
-
error=e
|
|
44
|
-
))
|
|
45
|
-
return Finder(run_f = flat_map_run, name=self.name) # type: ignore
|
|
46
|
-
|
|
47
18
|
|
|
19
|
+
@dataclass(frozen=True)
|
|
20
|
+
class Finder(Generator[T]):
|
|
48
21
|
def many(self, *, at_least: int, at_most: Optional[int]) -> Algebra[ManyResult[ParseResult[T]], GenState[T]]:
|
|
49
22
|
assert at_least > 0, "at_least must be greater than 0"
|
|
50
23
|
assert at_most is None or at_least <= at_most, "at_least must be less than or equal to at_most"
|
|
51
|
-
|
|
52
|
-
wrapper = input.wrapper()
|
|
53
|
-
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
|
|
54
|
-
ret = []
|
|
55
|
-
for index in range(input.how_many):
|
|
56
|
-
match self.run(input.down(index), use_cache):
|
|
57
|
-
case Right((value, next_input)):
|
|
58
|
-
ret.append(value)
|
|
59
|
-
if at_most is not None and len(ret) > at_most:
|
|
60
|
-
return Left(Error(
|
|
61
|
-
message=f"Expected at most {at_most} matches, got {len(ret)}",
|
|
62
|
-
this=self,
|
|
63
|
-
state=input.down(index)
|
|
64
|
-
))
|
|
65
|
-
case Left(_):
|
|
66
|
-
pass
|
|
67
|
-
if len(ret) < at_least:
|
|
68
|
-
return Left(Error(
|
|
69
|
-
message=f"Expected at least {at_least} matches, got {len(ret)}",
|
|
70
|
-
this=self,
|
|
71
|
-
state=input.down(index)
|
|
72
|
-
))
|
|
73
|
-
return Right((wrapper(ManyResult(tuple(ret))), input))
|
|
74
|
-
return self.__class__(many_run, name=f"many({self.name})") # type: ignore
|
|
24
|
+
return self.map_state(lambda s: replace(s, is_pruned = False)).many(at_least=at_least, at_most=at_most)
|
|
75
25
|
|
|
76
26
|
|
|
77
27
|
def or_else(self, # type: ignore
|
|
78
28
|
other: Algebra[ParseResult[T], GenState[T]]
|
|
79
29
|
) -> Algebra[OrResult[ParseResult[T]], GenState[T]]:
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
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
|
|
83
|
-
match self.run(input.down(0), use_cache):
|
|
84
|
-
case Right((value, next_input)):
|
|
85
|
-
return Right((wrapper(OrResult(value)), next_input))
|
|
86
|
-
case Left(error):
|
|
87
|
-
match other.run(input.down(0), use_cache):
|
|
88
|
-
case Right((value, next_input)):
|
|
89
|
-
return Right((wrapper(OrResult(value)), next_input))
|
|
90
|
-
case Left(error):
|
|
91
|
-
return Left(error)
|
|
92
|
-
raise ValueError("or_else should always return a value or an error.")
|
|
93
|
-
return self.__class__(or_else_run, name=f"or_else({self.name} | {other.name})") # type: ignore
|
|
30
|
+
return self.map_state(lambda s: replace(s, is_pruned = False)).or_else(other)
|
|
31
|
+
|
|
94
32
|
|
|
95
33
|
@classmethod
|
|
96
34
|
def token(cls,
|
|
@@ -98,20 +36,12 @@ class Finder(Algebra[ParseResult[T], GenState[T]]):
|
|
|
98
36
|
text: Optional[str] = None,
|
|
99
37
|
case_sensitive: bool = False,
|
|
100
38
|
regex: Optional[re.Pattern[str]] = None
|
|
101
|
-
)-> Algebra[ParseResult[T], GenState[T]]:
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
current = input.focus
|
|
108
|
-
if not isinstance(current, Token) or not gen.is_valid(current):
|
|
109
|
-
return Left(Error(None,
|
|
110
|
-
message=f"Expected a Token, but got {type(current)}.",
|
|
111
|
-
state=input))
|
|
112
|
-
return Right((wrapper(current), input))
|
|
113
|
-
lazy_self = cls(token_run, name=cls.__name__ + f'.token({token_type or text or regex})') # type: ignore
|
|
114
|
-
return lazy_self
|
|
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
|
+
|
|
115
45
|
|
|
116
46
|
@classmethod
|
|
117
47
|
def anything(cls)->Algebra[ParseResult[T], GenState[T]]:
|
|
@@ -141,7 +71,7 @@ def find(syntax: Syntax[Any, Any], data: AST[Any]) -> YieldGen[AST[Any], None, N
|
|
|
141
71
|
case ManyResult(value = value):
|
|
142
72
|
for e in value:
|
|
143
73
|
yield from find(syntax, AST(e))
|
|
144
|
-
case
|
|
74
|
+
case MarkedResult(value=value):
|
|
145
75
|
yield from find(syntax, AST(value))
|
|
146
76
|
case OrResult(value=value):
|
|
147
77
|
yield from find(syntax, AST(value))
|
syncraft/generator.py
CHANGED
|
@@ -11,7 +11,8 @@ from syncraft.algebra import (
|
|
|
11
11
|
OrResult, ManyResult
|
|
12
12
|
)
|
|
13
13
|
|
|
14
|
-
from syncraft.ast import T, ParseResult, AST, Token, TokenSpec
|
|
14
|
+
from syncraft.ast import T, ParseResult, AST, Token, TokenSpec, Binding, Variable, Bindable
|
|
15
|
+
|
|
15
16
|
from syncraft.syntax import Syntax
|
|
16
17
|
from sqlglot import TokenType
|
|
17
18
|
import re
|
|
@@ -23,9 +24,13 @@ B = TypeVar('B')
|
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
@dataclass(frozen=True)
|
|
26
|
-
class GenState(Generic[T]):
|
|
27
|
+
class GenState(Bindable, Generic[T]):
|
|
27
28
|
ast: Optional[AST[T]]
|
|
28
29
|
seed: int
|
|
30
|
+
is_pruned: Optional[bool] = None
|
|
31
|
+
binding: Binding = Binding()
|
|
32
|
+
def bind(self, var: Variable, node:ParseResult[T])->GenState[T]:
|
|
33
|
+
return replace(self, binding=self.binding.bind(var, node))
|
|
29
34
|
|
|
30
35
|
def fork(self, tag: Any) -> GenState[T]:
|
|
31
36
|
return replace(self, seed=hash((self.seed, tag)))
|
|
@@ -38,7 +43,10 @@ class GenState(Generic[T]):
|
|
|
38
43
|
|
|
39
44
|
@cached_property
|
|
40
45
|
def pruned(self)->bool:
|
|
41
|
-
|
|
46
|
+
if self.is_pruned is None:
|
|
47
|
+
return self.ast is None or self.ast.pruned
|
|
48
|
+
else:
|
|
49
|
+
return self.is_pruned
|
|
42
50
|
|
|
43
51
|
|
|
44
52
|
@property
|
|
@@ -167,7 +175,7 @@ class Generator(Algebra[ParseResult[T], GenState[T]]):
|
|
|
167
175
|
state=original,
|
|
168
176
|
error=e
|
|
169
177
|
))
|
|
170
|
-
return
|
|
178
|
+
return self.__class__(run_f = flat_map_run, name=self.name) # type: ignore
|
|
171
179
|
|
|
172
180
|
|
|
173
181
|
def many(self, *, at_least: int, at_most: Optional[int]) -> Algebra[ManyResult[ParseResult[T]], GenState[T]]:
|
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, Callable
|
|
6
|
+
Generic, Callable, Hashable
|
|
7
7
|
)
|
|
8
8
|
from syncraft.algebra import (
|
|
9
9
|
Either, Left, Right, Error, Algebra
|
|
@@ -13,13 +13,16 @@ 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, ParseResult, Binding, Variable, Bindable
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
@dataclass(frozen=True)
|
|
20
|
-
class ParserState(Generic[T]):
|
|
20
|
+
class ParserState(Bindable, Generic[T]):
|
|
21
21
|
input: Tuple[T, ...] = field(default_factory=tuple)
|
|
22
22
|
index: int = 0
|
|
23
|
+
binding: Binding[ParseResult[T]] = Binding()
|
|
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
|
def token_sample_string(self)-> str:
|
|
25
28
|
def encode_tokens(*tokens:T) -> str:
|
syncraft/syntax.py
CHANGED
|
@@ -6,7 +6,9 @@ from typing import (
|
|
|
6
6
|
)
|
|
7
7
|
from dataclasses import dataclass, field, replace
|
|
8
8
|
from functools import reduce
|
|
9
|
-
from syncraft.algebra import Algebra, Error, Either,
|
|
9
|
+
from syncraft.algebra import Algebra, Error, Either, Right
|
|
10
|
+
from syncraft.constraint import Variable, Bindable
|
|
11
|
+
from syncraft.ast import ThenResult, ManyResult, ThenKind, MarkedResult
|
|
10
12
|
from types import MethodType, FunctionType
|
|
11
13
|
|
|
12
14
|
|
|
@@ -15,33 +17,9 @@ from types import MethodType, FunctionType
|
|
|
15
17
|
A = TypeVar('A') # Result type
|
|
16
18
|
B = TypeVar('B') # Result type for mapping
|
|
17
19
|
C = TypeVar('C') # Result type for else branch
|
|
18
|
-
S = TypeVar('S') # State type
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class Variable(Generic[A]):
|
|
22
|
-
def __init__(self, name: Optional[str] = None):
|
|
23
|
-
self.name = name
|
|
24
|
-
self.initialized = False
|
|
25
|
-
self._value = None
|
|
26
|
-
def __call__(self, a: Any) -> Any:
|
|
27
|
-
if self.initialized:
|
|
28
|
-
if self._value != a:
|
|
29
|
-
raise ValueError(f"Variable {self.name or ''} is already initialized")
|
|
30
|
-
else:
|
|
31
|
-
return a
|
|
32
|
-
elif self.name is not None and isinstance(a, NamedResult):
|
|
33
|
-
if a.name == self.name:
|
|
34
|
-
self._value = a.value
|
|
35
|
-
self.initialized = True
|
|
36
|
-
else:
|
|
37
|
-
self._value = a
|
|
38
|
-
self.initialized = True
|
|
39
|
-
return a
|
|
40
|
-
@property
|
|
41
|
-
def value(self) -> A:
|
|
42
|
-
if self.initialized is False or self._value is None:
|
|
43
|
-
raise ValueError(f"Variable {self.name or ''} is not initialized")
|
|
44
|
-
return self._value
|
|
20
|
+
S = TypeVar('S', bound=Bindable) # State type
|
|
21
|
+
|
|
22
|
+
|
|
45
23
|
|
|
46
24
|
|
|
47
25
|
@dataclass(frozen=True)
|
|
@@ -157,11 +135,15 @@ class Syntax(Generic[A, S]):
|
|
|
157
135
|
def map(self, f: Callable[[A], B]) -> Syntax[B, S]:
|
|
158
136
|
return self.__class__(lambda cls: self.alg(cls).map(f), meta = self.meta) # type: ignore
|
|
159
137
|
|
|
138
|
+
def map_all(self, f: Callable[[Either[Any, Tuple[A, S]]], Either[Any, Tuple[B, S]]]) -> Syntax[B, S]:
|
|
139
|
+
return self.__class__(lambda cls: self.alg(cls).map_all(f), meta=self.meta) # type: ignore
|
|
140
|
+
|
|
160
141
|
def map_error(self, f: Callable[[Optional[Any]], Any]) -> Syntax[A, S]:
|
|
161
142
|
return self.__class__(lambda cls: self.alg(cls).map_error(f), meta=self.meta)
|
|
162
143
|
|
|
163
144
|
def map_state(self, f: Callable[[S], S]) -> Syntax[A, S]:
|
|
164
145
|
return self.__class__(lambda cls: self.alg(cls).map_state(f), meta=self.meta)
|
|
146
|
+
|
|
165
147
|
|
|
166
148
|
def flat_map(self, f: Callable[[A], Algebra[B, S]]) -> Syntax[B, S]:
|
|
167
149
|
return self.__class__(lambda cls: self.alg(cls).flat_map(f)) # type: ignore
|
|
@@ -251,16 +233,23 @@ class Syntax(Generic[A, S]):
|
|
|
251
233
|
|
|
252
234
|
|
|
253
235
|
######################################################################## data processing combinators #########################################################
|
|
254
|
-
def bind(self,
|
|
255
|
-
def
|
|
256
|
-
if isinstance(
|
|
257
|
-
|
|
236
|
+
def bind(self, var: Variable) -> Syntax[A, S]:
|
|
237
|
+
def bind_v(result: Either[Any, Tuple[A, S]])->Either[Any, Tuple[A, S]]:
|
|
238
|
+
if isinstance(result, Right):
|
|
239
|
+
value, state = result.value
|
|
240
|
+
return Right((value, state.bind(var, value)))
|
|
241
|
+
return result
|
|
242
|
+
return self.map_all(bind_v).describe(name=f'bind({var.name})', fixity='postfix', parameter=[self])
|
|
243
|
+
|
|
244
|
+
def mark(self, var: str) -> Syntax[MarkedResult[A], S]:
|
|
245
|
+
def bind_s(value: A) -> MarkedResult[A]:
|
|
246
|
+
if isinstance(value, MarkedResult):
|
|
247
|
+
return replace(value, name=var)
|
|
258
248
|
else:
|
|
259
|
-
return
|
|
260
|
-
return self.map(
|
|
249
|
+
return MarkedResult(name=var, value=value)
|
|
250
|
+
return self.map(bind_s).describe(name=f'bind("{var}")', fixity='postfix', parameter=[self])
|
|
251
|
+
|
|
261
252
|
|
|
262
|
-
def assign(self, var: Variable[A]) -> Syntax[A, S]:
|
|
263
|
-
return self.map(var).describe(name=f'assign({var.name or ""})', fixity='postfix', parameter=[self])
|
|
264
253
|
|
|
265
254
|
def dump_error(self, formatter: Optional[Callable[[Error], None]] = None) -> Syntax[A, S]:
|
|
266
255
|
def dump_error_run(err: Any)->Any:
|
|
@@ -299,13 +288,18 @@ def first(*parsers: Syntax[Any, S]) -> Syntax[Any, S]:
|
|
|
299
288
|
def last(*parsers: Syntax[Any, S]) -> Syntax[Any, S]:
|
|
300
289
|
return reduce(lambda a, b: a >> b, parsers) if len(parsers) > 0 else success(None)
|
|
301
290
|
|
|
302
|
-
def
|
|
291
|
+
def bound(* parsers: Syntax[Any, S] | Tuple[str|Variable, Syntax[Any, S]]) -> Syntax[Any, S]:
|
|
303
292
|
def is_named_parser(x: Any) -> bool:
|
|
304
|
-
return isinstance(x, tuple) and len(x) == 2 and isinstance(x[0], str) and isinstance(x[1], Syntax)
|
|
293
|
+
return isinstance(x, tuple) and len(x) == 2 and isinstance(x[0], (str, Variable)) and isinstance(x[1], Syntax)
|
|
305
294
|
|
|
306
|
-
def to_parser(x: Syntax[Any, S] | Tuple[str, Syntax[Any, S]])->Syntax[Any, S]:
|
|
307
|
-
if isinstance(x, tuple) and len(x) == 2 and isinstance(x[0], str) and isinstance(x[1], Syntax):
|
|
308
|
-
|
|
295
|
+
def to_parser(x: Syntax[Any, S] | Tuple[str|Variable, Syntax[Any, S]])->Syntax[Any, S]:
|
|
296
|
+
if isinstance(x, tuple) and len(x) == 2 and isinstance(x[0], (str, Variable)) and isinstance(x[1], Syntax):
|
|
297
|
+
if isinstance(x[0], str):
|
|
298
|
+
return x[1].mark(x[0])
|
|
299
|
+
elif isinstance(x[0], Variable):
|
|
300
|
+
return x[1].bind(x[0])
|
|
301
|
+
else:
|
|
302
|
+
raise ValueError(f"Invalid variable type(must be str | Variable): {x[0]}", x)
|
|
309
303
|
elif isinstance(x, Syntax):
|
|
310
304
|
return x
|
|
311
305
|
else:
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
syncraft/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
syncraft/algebra.py,sha256=Um8iMgVwlWcseQDkB_GqY-xmo0hR7zSw5j7oRRxrZhE,14122
|
|
3
|
+
syncraft/ast.py,sha256=TM18FkdFIBbxaaKgEvTvellrcXdkzMLLjLm7sRuC7FY,12985
|
|
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.24.dist-info/licenses/LICENSE,sha256=wHSV424U5csa3339dy1AZbsz2xsd0hrkMx2QK48CcUk,1062
|
|
13
|
+
syncraft-0.1.24.dist-info/METADATA,sha256=jleKB866QNoRRkpcmtNflWRHUS4HgNYqQsg9zfJHd4Q,1381
|
|
14
|
+
syncraft-0.1.24.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
15
|
+
syncraft-0.1.24.dist-info/top_level.txt,sha256=Kq3t8ESXB2xW1Xt3uPmkENFc-c4f2pamNmaURBk7zc8,9
|
|
16
|
+
syncraft-0.1.24.dist-info/RECORD,,
|
syncraft/cmd.py
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import subprocess
|
|
2
|
-
import tempfile
|
|
3
|
-
from typing import List, Tuple
|
|
4
|
-
from dataclasses import dataclass, field
|
|
5
|
-
|
|
6
|
-
@dataclass(frozen=True)
|
|
7
|
-
class Sqlite3Result:
|
|
8
|
-
stdout: Tuple[str, ...] = field(default_factory=tuple)
|
|
9
|
-
stderr: Tuple[str, ...] = field(default_factory=tuple)
|
|
10
|
-
schema: Tuple[str, ...] = field(default_factory=tuple)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class SQLite3:
|
|
14
|
-
def __init__(self, cmd: str = 'sqlite3'):
|
|
15
|
-
self.cmd = cmd
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def exec(self, sql: str)->Sqlite3Result:
|
|
19
|
-
with tempfile.NamedTemporaryFile(suffix=".db") as temp_db:
|
|
20
|
-
process = subprocess.run(
|
|
21
|
-
[self.cmd, temp_db.name],
|
|
22
|
-
input=sql.encode(),
|
|
23
|
-
stdout=subprocess.PIPE,
|
|
24
|
-
stderr=subprocess.PIPE
|
|
25
|
-
)
|
|
26
|
-
stdout = process.stdout.decode('utf-8').strip().splitlines()
|
|
27
|
-
stderr = process.stderr.decode('utf-8').strip().splitlines()
|
|
28
|
-
schema_process = subprocess.run(
|
|
29
|
-
[self.cmd, temp_db.name, '.schema'],
|
|
30
|
-
stdout=subprocess.PIPE,
|
|
31
|
-
stderr=subprocess.PIPE
|
|
32
|
-
)
|
|
33
|
-
schema = schema_process.stdout.decode('utf-8').strip().splitlines()
|
|
34
|
-
return Sqlite3Result(
|
|
35
|
-
stdout=tuple(stdout),
|
|
36
|
-
stderr=tuple(stderr),
|
|
37
|
-
schema=tuple(schema)
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if __name__ == "__main__":
|
|
42
|
-
sql = """
|
|
43
|
-
DROP TABLE IF EXISTS test;
|
|
44
|
-
DROP TABLE IF EXISTS error;
|
|
45
|
-
DROP INDEX IF EXISTS idx_name;
|
|
46
|
-
CREATE TABLE d.x (id INTEGER PRIMARY KEY, name TEXT);
|
|
47
|
-
|
|
48
|
-
CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT);
|
|
49
|
-
CREATE TABLE IF NOT EXISTS error (id INTEGER PRIMARY KEY, name TEXT);
|
|
50
|
-
CREATE INDEX IF NOT EXISTS idx_name ON test(name);
|
|
51
|
-
CREATE TRIGGER IF NOT EXISTS trg_test
|
|
52
|
-
AFTER INSERT ON test
|
|
53
|
-
BEGIN
|
|
54
|
-
INSERT INTO error (name) VALUES ('Trigger Error');
|
|
55
|
-
END;
|
|
56
|
-
"""
|
|
57
|
-
result = SQLite3().exec(sql)
|
|
58
|
-
print("STDOUT:", result.stdout)
|
|
59
|
-
print("STDERR:", result.stderr)
|
|
60
|
-
print("SCHEMA:", result.schema)
|
|
61
|
-
|
syncraft-0.1.21.dist-info/RECORD
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
syncraft/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
syncraft/algebra.py,sha256=bya-2b5WpCcOMxgJjGqrFaNG8dCjs5NUqnxbJTc4uOM,21908
|
|
3
|
-
syncraft/ast.py,sha256=Hn88_xOqSzrHYtPjNFKREJuE6yISmNI-VVTcU6PJKI0,4737
|
|
4
|
-
syncraft/cmd.py,sha256=DzXgU8QLVOg0YTFKpmOyzbf02LKPphQ4EeKSLzqpJ_s,2012
|
|
5
|
-
syncraft/diagnostic.py,sha256=mnzzceE9HpN2jVa7ztecZCz3sbCD79JxgkdLhJRBsvY,2822
|
|
6
|
-
syncraft/finder.py,sha256=zADOQQdcHu60413ZrRxTSq-E5EAJ9xv0OQodCpoYpo0,7202
|
|
7
|
-
syncraft/generator.py,sha256=Mh9Sx1hNR0EAWcox6MMvVw7aq2s33TU4stGZFZ1N1cw,11287
|
|
8
|
-
syncraft/parser.py,sha256=PSy9s3q_KXQh-Qlg0NxvJixxcgVYi3yCq0ebqlA5WwM,11288
|
|
9
|
-
syncraft/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
syncraft/sqlite3.py,sha256=Pq09IHZOwuWg5W82l9D1flzd36QV0TOHQpTJ5U02V8g,34701
|
|
11
|
-
syncraft/syntax.py,sha256=yUp3DmDnkwNEqDAFZH4vesjdnbIUsrwFjWN-Ljy1qN4,14880
|
|
12
|
-
syncraft-0.1.21.dist-info/licenses/LICENSE,sha256=wHSV424U5csa3339dy1AZbsz2xsd0hrkMx2QK48CcUk,1062
|
|
13
|
-
syncraft-0.1.21.dist-info/METADATA,sha256=cJyW-BifZOf3RdmFKp1-auJv13aMNjRwk1VDqPG5gR4,1381
|
|
14
|
-
syncraft-0.1.21.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
15
|
-
syncraft-0.1.21.dist-info/top_level.txt,sha256=Kq3t8ESXB2xW1Xt3uPmkENFc-c4f2pamNmaURBk7zc8,9
|
|
16
|
-
syncraft-0.1.21.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|