syncraft 0.1.20__py3-none-any.whl → 0.1.22__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.
- syncraft/algebra.py +19 -171
- syncraft/ast.py +225 -17
- syncraft/constraint.py +189 -0
- syncraft/diagnostic.py +5 -5
- syncraft/finder.py +79 -0
- syncraft/generator.py +26 -49
- syncraft/parser.py +6 -10
- syncraft/syntax.py +36 -21
- {syncraft-0.1.20.dist-info → syncraft-0.1.22.dist-info}/METADATA +1 -1
- syncraft-0.1.22.dist-info/RECORD +16 -0
- syncraft/cmd.py +0 -61
- syncraft-0.1.20.dist-info/RECORD +0 -15
- {syncraft-0.1.20.dist-info → syncraft-0.1.22.dist-info}/WHEEL +0 -0
- {syncraft-0.1.20.dist-info → syncraft-0.1.22.dist-info}/licenses/LICENSE +0 -0
- {syncraft-0.1.20.dist-info → syncraft-0.1.22.dist-info}/top_level.txt +0 -0
syncraft/algebra.py
CHANGED
|
@@ -5,180 +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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
A = TypeVar('A')
|
|
16
|
-
B = TypeVar('B')
|
|
17
|
-
C = TypeVar('C')
|
|
18
|
-
S = TypeVar('S')
|
|
11
|
+
from syncraft.ast import ThenKind, ThenResult, ManyResult, OrResult, S
|
|
12
|
+
|
|
13
|
+
A = TypeVar('A') # Result type
|
|
14
|
+
B = TypeVar('B') # Mapped result type
|
|
19
15
|
|
|
20
16
|
|
|
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
|
-
@staticmethod
|
|
43
|
-
def variable(value: A)->Bimap[S, A, A]:
|
|
44
|
-
return Bimap(
|
|
45
|
-
forward=lambda s, _: (s, value),
|
|
46
|
-
inverse=lambda s, y: (s, y)
|
|
47
|
-
)
|
|
48
|
-
@staticmethod
|
|
49
|
-
def const(state: S, value: A)->Bimap[Any, A, A]:
|
|
50
|
-
return Bimap(
|
|
51
|
-
forward=lambda s, _: (state, value),
|
|
52
|
-
inverse=lambda s, y: (state, value)
|
|
53
|
-
)
|
|
54
|
-
@staticmethod
|
|
55
|
-
def combine(*biarrows: Bimap[Any, Any, Any]) -> Bimap[Any, Any, Any]:
|
|
56
|
-
return reduce(lambda a, b: a >> b, biarrows, Bimap.identity())
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
class StructuralResult:
|
|
61
|
-
def bimap(self, arr: Bimap[Any, Any, Any] = Bimap.identity()) -> Bimap[Any, Any, Any]:
|
|
62
|
-
return Bimap.identity()
|
|
63
|
-
|
|
64
|
-
@dataclass(frozen=True)
|
|
65
|
-
class NamedResult(Generic[A], StructuralResult):
|
|
66
|
-
name: str
|
|
67
|
-
value: A
|
|
68
|
-
def bimap(self, arr: Bimap[Any, Any, Any] = Bimap.identity()) -> Bimap[Any, NamedResult[A], NamedResult[Any]]:
|
|
69
|
-
inner_b = self.value.bimap(arr) if isinstance(self.value, StructuralResult) else arr
|
|
70
|
-
def fwd(s: S, a: NamedResult[A])-> Tuple[S, NamedResult[Any]]:
|
|
71
|
-
assert a == self, f"Expected {self}, got {a}"
|
|
72
|
-
inner_s, inner_v = inner_b.forward(s, a.value)
|
|
73
|
-
return (inner_s, replace(a, value=inner_v)) if not isinstance(inner_v, NamedResult) else (inner_s, inner_v)
|
|
74
|
-
|
|
75
|
-
def inv(s: S, a: NamedResult[Any]) -> Tuple[S, NamedResult[A]]:
|
|
76
|
-
assert isinstance(a, NamedResult), f"Expected NamedResult, got {type(a)}"
|
|
77
|
-
inner_s, inner_v = inner_b.inverse(s, a.value)
|
|
78
|
-
return (inner_s, replace(self, value=inner_v)) if not isinstance(inner_v, NamedResult) else (inner_s, replace(self, value=inner_v.value))
|
|
79
|
-
|
|
80
|
-
return Bimap(
|
|
81
|
-
forward=fwd,
|
|
82
|
-
inverse=inv
|
|
83
|
-
)
|
|
84
|
-
@dataclass(eq=True, frozen=True)
|
|
85
|
-
class ManyResult(Generic[A], StructuralResult):
|
|
86
|
-
value: Tuple[A, ...]
|
|
87
|
-
def bimap(self, arr: Bimap[Any, Any, Any] = Bimap.identity()) -> Bimap[Any, ManyResult[A], List[A]]:
|
|
88
|
-
inner_b = [v.bimap(arr) if isinstance(v, StructuralResult) else arr for v in self.value]
|
|
89
|
-
def fwd(s: Any, a: ManyResult[A]) -> Tuple[Any, List[A]]:
|
|
90
|
-
assert a == self, f"Expected {self}, got {a}"
|
|
91
|
-
return s, [inner_b[i].forward(s, v)[1] for i, v in enumerate(a.value)]
|
|
92
|
-
|
|
93
|
-
def inv(s: Any, a: List[A]) -> Tuple[Any, ManyResult[A]]:
|
|
94
|
-
assert isinstance(a, list), f"Expected list, got {type(a)}"
|
|
95
|
-
assert len(a) == len(inner_b), f"Expected {len(inner_b)} elements, got {len(a)}"
|
|
96
|
-
return s, ManyResult(value=tuple(inner_b[i].inverse(s, v)[1] for i, v in enumerate(a)))
|
|
97
|
-
return Bimap(
|
|
98
|
-
forward=fwd,
|
|
99
|
-
inverse=inv
|
|
100
|
-
)
|
|
101
|
-
@dataclass(eq=True, frozen=True)
|
|
102
|
-
class OrResult(Generic[A], StructuralResult):
|
|
103
|
-
value: A
|
|
104
|
-
def bimap(self, arr: Bimap[Any, Any, Any] = Bimap.identity()) -> Bimap[Any, OrResult[A], Any]:
|
|
105
|
-
inner_b = self.value.bimap(arr) if isinstance(self.value, StructuralResult) else arr
|
|
106
|
-
def fwd(s: Any, a: OrResult[A]) -> Tuple[Any, Any]:
|
|
107
|
-
assert a == self, f"Expected {self}, got {a}"
|
|
108
|
-
return inner_b.forward(s, a.value)
|
|
109
|
-
|
|
110
|
-
def inv(s: Any, a: Any) -> Tuple[Any, OrResult[A]]:
|
|
111
|
-
inner_s, inner_v = inner_b.inverse(s, a)
|
|
112
|
-
return inner_s, OrResult(value=inner_v)
|
|
113
|
-
|
|
114
|
-
return Bimap(
|
|
115
|
-
forward=fwd,
|
|
116
|
-
inverse=inv
|
|
117
|
-
)
|
|
118
|
-
class ThenKind(Enum):
|
|
119
|
-
BOTH = '+'
|
|
120
|
-
LEFT = '//'
|
|
121
|
-
RIGHT = '>>'
|
|
122
|
-
|
|
123
|
-
@dataclass(eq=True, frozen=True)
|
|
124
|
-
class ThenResult(Generic[A, B], StructuralResult):
|
|
125
|
-
kind: ThenKind
|
|
126
|
-
left: A
|
|
127
|
-
right: B
|
|
128
|
-
def arity(self)->int:
|
|
129
|
-
if self.kind == ThenKind.LEFT:
|
|
130
|
-
return self.left.arity() if isinstance(self.left, ThenResult) else 1
|
|
131
|
-
elif self.kind == ThenKind.RIGHT:
|
|
132
|
-
return self.right.arity() if isinstance(self.right, ThenResult) else 1
|
|
133
|
-
elif self.kind == ThenKind.BOTH:
|
|
134
|
-
left_arity = self.left.arity() if isinstance(self.left, ThenResult) else 1
|
|
135
|
-
right_arity = self.right.arity() if isinstance(self.right, ThenResult) else 1
|
|
136
|
-
return left_arity + right_arity
|
|
137
|
-
else:
|
|
138
|
-
return 1
|
|
139
|
-
|
|
140
|
-
def bimap(self, arr: Bimap[Any, Any, Any] = Bimap.identity()) -> Bimap[Any, ThenResult[A, B], Tuple[Any, ...] | Any]:
|
|
141
|
-
kind = self.kind
|
|
142
|
-
lb = self.left.bimap(arr) if isinstance(self.left, StructuralResult) else arr
|
|
143
|
-
rb = self.right.bimap(arr) if isinstance(self.right, StructuralResult) else arr
|
|
144
|
-
left_size = self.left.arity() if isinstance(self.left, ThenResult) else 1
|
|
145
|
-
right_size = self.right.arity() if isinstance(self.right, ThenResult) else 1
|
|
146
|
-
def fwd(s : S, a : ThenResult[A, B]) -> Tuple[S, Tuple[Any, ...] | Any]:
|
|
147
|
-
assert a == self, f"Expected {self}, got {a}"
|
|
148
|
-
match kind:
|
|
149
|
-
case ThenKind.LEFT:
|
|
150
|
-
return lb.forward(s, a.left)
|
|
151
|
-
case ThenKind.RIGHT:
|
|
152
|
-
return rb.forward(s, a.right)
|
|
153
|
-
case ThenKind.BOTH:
|
|
154
|
-
s1, left_v = lb.forward(s, a.left)
|
|
155
|
-
s2, right_v = rb.forward(s1, a.right)
|
|
156
|
-
left_v = (left_v,) if not isinstance(a.left, ThenResult) else left_v
|
|
157
|
-
right_v = (right_v,) if not isinstance(a.right, ThenResult) else right_v
|
|
158
|
-
return s2, left_v + right_v
|
|
159
|
-
|
|
160
|
-
def inv(s: S, b: Tuple[Any, ...] | Any) -> Tuple[S, ThenResult[A, B]]:
|
|
161
|
-
match kind:
|
|
162
|
-
case ThenKind.LEFT:
|
|
163
|
-
s1, lv = lb.inverse(s, b)
|
|
164
|
-
return s1, replace(self, left=lv)
|
|
165
|
-
case ThenKind.RIGHT:
|
|
166
|
-
s1, rv = rb.inverse(s, b)
|
|
167
|
-
return s1, replace(self, right=rv)
|
|
168
|
-
case ThenKind.BOTH:
|
|
169
|
-
lraw = b[:left_size]
|
|
170
|
-
rraw = b[left_size:left_size + right_size]
|
|
171
|
-
lraw = lraw[0] if left_size == 1 else lraw
|
|
172
|
-
rraw = rraw[0] if right_size == 1 else rraw
|
|
173
|
-
s1, lv = lb.inverse(s, lraw)
|
|
174
|
-
s2, rv = rb.inverse(s1, rraw)
|
|
175
|
-
return s2, replace(self, left=lv, right=rv)
|
|
176
|
-
|
|
177
|
-
return Bimap(
|
|
178
|
-
forward=fwd,
|
|
179
|
-
inverse=inv
|
|
180
|
-
)
|
|
181
|
-
|
|
182
17
|
InProgress = object() # Marker for in-progress state, used to prevent re-entrance in recursive calls
|
|
183
18
|
L = TypeVar('L') # Left type for combined results
|
|
184
19
|
R = TypeVar('R') # Right type for combined results
|
|
@@ -222,7 +57,14 @@ class Error:
|
|
|
222
57
|
state=state,
|
|
223
58
|
previous=self
|
|
224
59
|
)
|
|
225
|
-
|
|
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
|
|
226
68
|
|
|
227
69
|
|
|
228
70
|
@dataclass(frozen=True)
|
|
@@ -403,6 +245,12 @@ class Algebra(ABC, Generic[A, S]):
|
|
|
403
245
|
return cast(Either[Any, Tuple[B, S]], parsed)
|
|
404
246
|
return self.__class__(map_run, name=self.name) # type: ignore
|
|
405
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
|
+
|
|
406
254
|
def map_error(self, f: Callable[[Optional[Any]], Any]) -> Algebra[A, S]:
|
|
407
255
|
def map_error_run(input: S, use_cache:bool) -> Either[Any, Tuple[A, S]]:
|
|
408
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
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import (
|
|
4
|
+
Any, Tuple, Optional, Generator as YieldGen
|
|
5
|
+
)
|
|
6
|
+
from dataclasses import dataclass, replace
|
|
7
|
+
from syncraft.algebra import (
|
|
8
|
+
Algebra, Either, Right,
|
|
9
|
+
OrResult, ManyResult, ThenResult, MarkedResult
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
from syncraft.ast import T, ParseResult, AST
|
|
13
|
+
from syncraft.generator import GenState, Generator
|
|
14
|
+
from sqlglot import TokenType
|
|
15
|
+
from syncraft.syntax import Syntax
|
|
16
|
+
import re
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True)
|
|
20
|
+
class Finder(Generator[T]):
|
|
21
|
+
def many(self, *, at_least: int, at_most: Optional[int]) -> Algebra[ManyResult[ParseResult[T]], GenState[T]]:
|
|
22
|
+
assert at_least > 0, "at_least must be greater than 0"
|
|
23
|
+
assert at_most is None or at_least <= at_most, "at_least must be less than or equal to at_most"
|
|
24
|
+
return self.map_state(lambda s: replace(s, is_pruned = False)).many(at_least=at_least, at_most=at_most)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def or_else(self, # type: ignore
|
|
28
|
+
other: Algebra[ParseResult[T], GenState[T]]
|
|
29
|
+
) -> Algebra[OrResult[ParseResult[T]], GenState[T]]:
|
|
30
|
+
return self.map_state(lambda s: replace(s, is_pruned = False)).or_else(other)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@classmethod
|
|
34
|
+
def token(cls,
|
|
35
|
+
token_type: Optional[TokenType] = None,
|
|
36
|
+
text: Optional[str] = None,
|
|
37
|
+
case_sensitive: bool = False,
|
|
38
|
+
regex: Optional[re.Pattern[str]] = None
|
|
39
|
+
)-> Algebra[ParseResult[T], GenState[T]]:
|
|
40
|
+
return super().token(token_type=token_type,
|
|
41
|
+
text=text,
|
|
42
|
+
case_sensitive=case_sensitive,
|
|
43
|
+
regex=regex).map_state(lambda s: replace(s, is_pruned = False)) # type: ignore
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def anything(cls)->Algebra[ParseResult[T], GenState[T]]:
|
|
48
|
+
def anything_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[ParseResult[T], GenState[T]]]:
|
|
49
|
+
wrapper = input.wrapper()
|
|
50
|
+
return Right((wrapper(input.focus), input))
|
|
51
|
+
return cls(anything_run, name=cls.__name__ + '.anything()')
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
anything = Syntax(lambda cls: cls.factory('anything')).describe(name="anything", fixity='infix')
|
|
56
|
+
|
|
57
|
+
def matches(syntax: Syntax[Any, Any], data: AST[Any])-> bool:
|
|
58
|
+
gen = syntax(Finder)
|
|
59
|
+
state = GenState.from_ast(data)
|
|
60
|
+
result = gen.run(state, use_cache=True)
|
|
61
|
+
return isinstance(result, Right)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def find(syntax: Syntax[Any, Any], data: AST[Any]) -> YieldGen[AST[Any], None, None]:
|
|
65
|
+
if matches(syntax, data):
|
|
66
|
+
yield data
|
|
67
|
+
match data.focus:
|
|
68
|
+
case ThenResult(left = left, right=right):
|
|
69
|
+
yield from find(syntax, AST(left))
|
|
70
|
+
yield from find(syntax, AST(right))
|
|
71
|
+
case ManyResult(value = value):
|
|
72
|
+
for e in value:
|
|
73
|
+
yield from find(syntax, AST(e))
|
|
74
|
+
case MarkedResult(value=value):
|
|
75
|
+
yield from find(syntax, AST(value))
|
|
76
|
+
case OrResult(value=value):
|
|
77
|
+
yield from find(syntax, AST(value))
|
|
78
|
+
case _:
|
|
79
|
+
pass
|
syncraft/generator.py
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from typing import (
|
|
4
|
-
Any, TypeVar, Tuple, Optional, Callable, Generic,
|
|
5
|
-
List,
|
|
4
|
+
Any, TypeVar, Tuple, Optional, Callable, Generic,
|
|
5
|
+
List,
|
|
6
6
|
)
|
|
7
7
|
from functools import cached_property
|
|
8
8
|
from dataclasses import dataclass, replace
|
|
9
9
|
from syncraft.algebra import (
|
|
10
|
-
Algebra,
|
|
11
|
-
OrResult, ManyResult
|
|
10
|
+
Algebra, Either, Left, Right, Error,
|
|
11
|
+
OrResult, ManyResult
|
|
12
12
|
)
|
|
13
13
|
|
|
14
|
-
from syncraft.ast import
|
|
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
|
|
@@ -20,20 +21,16 @@ from functools import lru_cache
|
|
|
20
21
|
import random
|
|
21
22
|
|
|
22
23
|
B = TypeVar('B')
|
|
23
|
-
T = TypeVar('T', bound=TokenProtocol)
|
|
24
|
-
|
|
25
|
-
GenResult = Union[
|
|
26
|
-
ThenResult['GenResult[T]', 'GenResult[T]'],
|
|
27
|
-
ManyResult['GenResult[T]'],
|
|
28
|
-
OrResult['GenResult[T]'],
|
|
29
24
|
|
|
30
|
-
T
|
|
31
|
-
]
|
|
32
25
|
|
|
33
26
|
@dataclass(frozen=True)
|
|
34
|
-
class GenState(Generic[T]):
|
|
27
|
+
class GenState(Bindable, Generic[T]):
|
|
35
28
|
ast: Optional[AST[T]]
|
|
36
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))
|
|
37
34
|
|
|
38
35
|
def fork(self, tag: Any) -> GenState[T]:
|
|
39
36
|
return replace(self, seed=hash((self.seed, tag)))
|
|
@@ -46,7 +43,10 @@ class GenState(Generic[T]):
|
|
|
46
43
|
|
|
47
44
|
@cached_property
|
|
48
45
|
def pruned(self)->bool:
|
|
49
|
-
|
|
46
|
+
if self.is_pruned is None:
|
|
47
|
+
return self.ast is None or self.ast.pruned
|
|
48
|
+
else:
|
|
49
|
+
return self.is_pruned
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
@property
|
|
@@ -150,8 +150,8 @@ class TokenGen(TokenSpec):
|
|
|
150
150
|
|
|
151
151
|
|
|
152
152
|
@dataclass(frozen=True)
|
|
153
|
-
class Generator(Algebra[
|
|
154
|
-
def flat_map(self, f: Callable[[
|
|
153
|
+
class Generator(Algebra[ParseResult[T], GenState[T]]):
|
|
154
|
+
def flat_map(self, f: Callable[[ParseResult[T]], Algebra[B, GenState[T]]]) -> Algebra[B, GenState[T]]:
|
|
155
155
|
def flat_map_run(original: GenState[T], use_cache:bool) -> Either[Any, Tuple[B, GenState[T]]]:
|
|
156
156
|
wrapper = original.wrapper()
|
|
157
157
|
input = original if not original.is_named else original.down(0) # If the input is named, we need to go down to the first child
|
|
@@ -175,13 +175,13 @@ class Generator(Algebra[GenResult[T], GenState[T]]):
|
|
|
175
175
|
state=original,
|
|
176
176
|
error=e
|
|
177
177
|
))
|
|
178
|
-
return
|
|
178
|
+
return self.__class__(run_f = flat_map_run, name=self.name) # type: ignore
|
|
179
179
|
|
|
180
180
|
|
|
181
|
-
def many(self, *, at_least: int, at_most: Optional[int]) -> Algebra[ManyResult[
|
|
181
|
+
def many(self, *, at_least: int, at_most: Optional[int]) -> Algebra[ManyResult[ParseResult[T]], GenState[T]]:
|
|
182
182
|
assert at_least > 0, "at_least must be greater than 0"
|
|
183
183
|
assert at_most is None or at_least <= at_most, "at_least must be less than or equal to at_most"
|
|
184
|
-
def many_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[ManyResult[
|
|
184
|
+
def many_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[ManyResult[ParseResult[T]], GenState[T]]]:
|
|
185
185
|
wrapper = input.wrapper()
|
|
186
186
|
input = input if not input.is_named else input.down(0) # If the input is named, we need to go down to the first child
|
|
187
187
|
if input.pruned:
|
|
@@ -221,9 +221,9 @@ class Generator(Algebra[GenResult[T], GenState[T]]):
|
|
|
221
221
|
|
|
222
222
|
|
|
223
223
|
def or_else(self, # type: ignore
|
|
224
|
-
other: Algebra[
|
|
225
|
-
) -> Algebra[OrResult[
|
|
226
|
-
def or_else_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[OrResult[
|
|
224
|
+
other: Algebra[ParseResult[T], GenState[T]]
|
|
225
|
+
) -> Algebra[OrResult[ParseResult[T]], GenState[T]]:
|
|
226
|
+
def or_else_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[OrResult[ParseResult[T]], GenState[T]]]:
|
|
227
227
|
wrapper = input.wrapper()
|
|
228
228
|
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
|
|
229
229
|
if input.pruned:
|
|
@@ -252,10 +252,10 @@ class Generator(Algebra[GenResult[T], GenState[T]]):
|
|
|
252
252
|
text: Optional[str] = None,
|
|
253
253
|
case_sensitive: bool = False,
|
|
254
254
|
regex: Optional[re.Pattern[str]] = None
|
|
255
|
-
)-> Algebra[
|
|
255
|
+
)-> Algebra[ParseResult[T], GenState[T]]:
|
|
256
256
|
gen = TokenGen(token_type=token_type, text=text, case_sensitive=case_sensitive, regex=regex)
|
|
257
|
-
lazy_self: Algebra[
|
|
258
|
-
def token_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[
|
|
257
|
+
lazy_self: Algebra[ParseResult[T], GenState[T]]
|
|
258
|
+
def token_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[ParseResult[Token], GenState[T]]]:
|
|
259
259
|
wrapper = input.wrapper()
|
|
260
260
|
input = input if not input.is_named else input.down(0) # If the input is named, we need to go down to the first child
|
|
261
261
|
if input.pruned:
|
|
@@ -282,27 +282,4 @@ def generate(syntax: Syntax[Any, Any], data: Optional[AST[Any]] = None, seed: in
|
|
|
282
282
|
return result.value
|
|
283
283
|
|
|
284
284
|
|
|
285
|
-
def matches(syntax: Syntax[Any, Any], data: AST[Any])-> bool:
|
|
286
|
-
gen = syntax(Generator)
|
|
287
|
-
state = GenState.from_ast(data)
|
|
288
|
-
result = gen.run(state, use_cache=True)
|
|
289
|
-
return isinstance(result, Right)
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
def search(syntax: Syntax[Any, Any], data: AST[Any]) -> YieldGen[AST[Any], None, None]:
|
|
293
|
-
if matches(syntax, data):
|
|
294
|
-
yield data
|
|
295
|
-
match data.focus:
|
|
296
|
-
case ThenResult(left = left, right=right):
|
|
297
|
-
yield from search(syntax, AST(left))
|
|
298
|
-
yield from search(syntax, AST(right))
|
|
299
|
-
case ManyResult(value = value):
|
|
300
|
-
for e in value:
|
|
301
|
-
yield from search(syntax, AST(e))
|
|
302
|
-
case NamedResult(value=value):
|
|
303
|
-
yield from search(syntax, AST(value))
|
|
304
|
-
case OrResult(value=value):
|
|
305
|
-
yield from search(syntax, AST(value))
|
|
306
|
-
case _:
|
|
307
|
-
pass
|
|
308
285
|
|
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,20 +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
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
from syncraft.ast import Token, TokenSpec, AST, T, ParseResult, Binding, Variable, Bindable
|
|
24
17
|
|
|
25
18
|
|
|
26
19
|
@dataclass(frozen=True)
|
|
27
|
-
class ParserState(Generic[T]):
|
|
20
|
+
class ParserState(Bindable, Generic[T]):
|
|
28
21
|
input: Tuple[T, ...] = field(default_factory=tuple)
|
|
29
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))
|
|
30
26
|
|
|
31
27
|
def token_sample_string(self)-> str:
|
|
32
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,7 +17,10 @@ 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
|
|
20
|
+
S = TypeVar('S', bound=Bindable) # State type
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
19
24
|
|
|
20
25
|
@dataclass(frozen=True)
|
|
21
26
|
class Description:
|
|
@@ -98,8 +103,6 @@ class Syntax(Generic[A, S]):
|
|
|
98
103
|
return self.__class__(alg=algebra_run, meta=self.meta)
|
|
99
104
|
|
|
100
105
|
|
|
101
|
-
|
|
102
|
-
|
|
103
106
|
def as_(self, typ: Type[B])->B:
|
|
104
107
|
return cast(typ, self) # type: ignore
|
|
105
108
|
|
|
@@ -132,11 +135,15 @@ class Syntax(Generic[A, S]):
|
|
|
132
135
|
def map(self, f: Callable[[A], B]) -> Syntax[B, S]:
|
|
133
136
|
return self.__class__(lambda cls: self.alg(cls).map(f), meta = self.meta) # type: ignore
|
|
134
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
|
+
|
|
135
141
|
def map_error(self, f: Callable[[Optional[Any]], Any]) -> Syntax[A, S]:
|
|
136
142
|
return self.__class__(lambda cls: self.alg(cls).map_error(f), meta=self.meta)
|
|
137
143
|
|
|
138
144
|
def map_state(self, f: Callable[[S], S]) -> Syntax[A, S]:
|
|
139
145
|
return self.__class__(lambda cls: self.alg(cls).map_state(f), meta=self.meta)
|
|
146
|
+
|
|
140
147
|
|
|
141
148
|
def flat_map(self, f: Callable[[A], Algebra[B, S]]) -> Syntax[B, S]:
|
|
142
149
|
return self.__class__(lambda cls: self.alg(cls).flat_map(f)) # type: ignore
|
|
@@ -226,13 +233,22 @@ class Syntax(Generic[A, S]):
|
|
|
226
233
|
|
|
227
234
|
|
|
228
235
|
######################################################################## data processing combinators #########################################################
|
|
229
|
-
def bind(self,
|
|
230
|
-
def
|
|
231
|
-
if isinstance(
|
|
232
|
-
|
|
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)
|
|
233
248
|
else:
|
|
234
|
-
return
|
|
235
|
-
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
|
+
|
|
236
252
|
|
|
237
253
|
|
|
238
254
|
def dump_error(self, formatter: Optional[Callable[[Error], None]] = None) -> Syntax[A, S]:
|
|
@@ -263,12 +279,6 @@ def choice(*parsers: Syntax[Any, S]) -> Syntax[Any, S]:
|
|
|
263
279
|
return reduce(lambda a, b: a | b, parsers) if len(parsers) > 0 else success(None)
|
|
264
280
|
|
|
265
281
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
282
|
def all(*parsers: Syntax[Any, S]) -> Syntax[ThenResult[Any, Any], S]:
|
|
273
283
|
return reduce(lambda a, b: a + b, parsers) if len(parsers) > 0 else success(None)
|
|
274
284
|
|
|
@@ -278,13 +288,18 @@ def first(*parsers: Syntax[Any, S]) -> Syntax[Any, S]:
|
|
|
278
288
|
def last(*parsers: Syntax[Any, S]) -> Syntax[Any, S]:
|
|
279
289
|
return reduce(lambda a, b: a >> b, parsers) if len(parsers) > 0 else success(None)
|
|
280
290
|
|
|
281
|
-
def
|
|
291
|
+
def bound(* parsers: Syntax[Any, S] | Tuple[str|Variable, Syntax[Any, S]]) -> Syntax[Any, S]:
|
|
282
292
|
def is_named_parser(x: Any) -> bool:
|
|
283
|
-
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)
|
|
284
294
|
|
|
285
|
-
def to_parser(x: Syntax[Any, S] | Tuple[str, Syntax[Any, S]])->Syntax[Any, S]:
|
|
286
|
-
if isinstance(x, tuple) and len(x) == 2 and isinstance(x[0], str) and isinstance(x[1], Syntax):
|
|
287
|
-
|
|
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)
|
|
288
303
|
elif isinstance(x, Syntax):
|
|
289
304
|
return x
|
|
290
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.22.dist-info/licenses/LICENSE,sha256=wHSV424U5csa3339dy1AZbsz2xsd0hrkMx2QK48CcUk,1062
|
|
13
|
+
syncraft-0.1.22.dist-info/METADATA,sha256=C_pkVYRVDWfpj30cGXa3N4Izxw2kL-bYKf7F_s6MCAE,1381
|
|
14
|
+
syncraft-0.1.22.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
15
|
+
syncraft-0.1.22.dist-info/top_level.txt,sha256=Kq3t8ESXB2xW1Xt3uPmkENFc-c4f2pamNmaURBk7zc8,9
|
|
16
|
+
syncraft-0.1.22.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.20.dist-info/RECORD
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
syncraft/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
syncraft/algebra.py,sha256=utnhCbrF5GFG6P2Oef_Ru6YVgVCgsGccMh6wx80ok5E,20519
|
|
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/generator.py,sha256=ygt5HXfKG5z1LcTxhU8adqEePegqmokLjbP6pi34hGA,12355
|
|
7
|
-
syncraft/parser.py,sha256=hk67kvOTBToOB2GhAh4weJQHR9xv73YPNQnqrW_qYv8,11295
|
|
8
|
-
syncraft/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
syncraft/sqlite3.py,sha256=Pq09IHZOwuWg5W82l9D1flzd36QV0TOHQpTJ5U02V8g,34701
|
|
10
|
-
syncraft/syntax.py,sha256=NtxkfPvSNsmpbRcESxEgRAW9d04ZHukWQoSJqIHsHNk,13842
|
|
11
|
-
syncraft-0.1.20.dist-info/licenses/LICENSE,sha256=wHSV424U5csa3339dy1AZbsz2xsd0hrkMx2QK48CcUk,1062
|
|
12
|
-
syncraft-0.1.20.dist-info/METADATA,sha256=j3Ylao4YeT_HxO4Gv95chaRvpoZLBallacoeoaoLQjk,1381
|
|
13
|
-
syncraft-0.1.20.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
14
|
-
syncraft-0.1.20.dist-info/top_level.txt,sha256=Kq3t8ESXB2xW1Xt3uPmkENFc-c4f2pamNmaURBk7zc8,9
|
|
15
|
-
syncraft-0.1.20.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|