syncraft 0.1.29__tar.gz → 0.1.31__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of syncraft might be problematic. Click here for more details.
- {syncraft-0.1.29 → syncraft-0.1.31}/PKG-INFO +1 -3
- {syncraft-0.1.29 → syncraft-0.1.31}/README.md +0 -2
- {syncraft-0.1.29 → syncraft-0.1.31}/pyproject.toml +1 -1
- {syncraft-0.1.29 → syncraft-0.1.31}/syncraft/algebra.py +6 -3
- {syncraft-0.1.29 → syncraft-0.1.31}/syncraft/ast.py +43 -32
- {syncraft-0.1.29 → syncraft-0.1.31}/syncraft/syntax.py +22 -13
- {syncraft-0.1.29 → syncraft-0.1.31}/syncraft.egg-info/PKG-INFO +1 -3
- {syncraft-0.1.29 → syncraft-0.1.31}/tests/test_bimap.py +34 -48
- {syncraft-0.1.29 → syncraft-0.1.31}/tests/test_parse.py +6 -0
- {syncraft-0.1.29 → syncraft-0.1.31}/LICENSE +0 -0
- {syncraft-0.1.29 → syncraft-0.1.31}/setup.cfg +0 -0
- {syncraft-0.1.29 → syncraft-0.1.31}/syncraft/__init__.py +0 -0
- {syncraft-0.1.29 → syncraft-0.1.31}/syncraft/constraint.py +0 -0
- {syncraft-0.1.29 → syncraft-0.1.31}/syncraft/diagnostic.py +0 -0
- {syncraft-0.1.29 → syncraft-0.1.31}/syncraft/finder.py +0 -0
- {syncraft-0.1.29 → syncraft-0.1.31}/syncraft/generator.py +0 -0
- {syncraft-0.1.29 → syncraft-0.1.31}/syncraft/parser.py +0 -0
- {syncraft-0.1.29 → syncraft-0.1.31}/syncraft/py.typed +0 -0
- {syncraft-0.1.29 → syncraft-0.1.31}/syncraft/sqlite3.py +0 -0
- {syncraft-0.1.29 → syncraft-0.1.31}/syncraft.egg-info/SOURCES.txt +0 -0
- {syncraft-0.1.29 → syncraft-0.1.31}/syncraft.egg-info/dependency_links.txt +0 -0
- {syncraft-0.1.29 → syncraft-0.1.31}/syncraft.egg-info/requires.txt +0 -0
- {syncraft-0.1.29 → syncraft-0.1.31}/syncraft.egg-info/top_level.txt +0 -0
- {syncraft-0.1.29 → syncraft-0.1.31}/tests/test_until.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: syncraft
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.31
|
|
4
4
|
Summary: Parser combinator library
|
|
5
5
|
Author-email: Michael Afmokt <michael@esacca.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -29,8 +29,6 @@ pip install syncraft
|
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
## TODO
|
|
32
|
-
- [ ] simplify the result of then_left and then_right by bimap the result in syntax.
|
|
33
|
-
- [ ] simplify the result of sep_by and between by bimap the result in syntax
|
|
34
32
|
- [ ] convert to dict/dataclass via bimap in syntax
|
|
35
33
|
- [ ] define DSL over Variable to construct predicates
|
|
36
34
|
- [ ] Try the parsing, generation, and data processing machinery on SQLite3 syntax. So that I can have direct feedback on the usability of this library and a fully functional SQLite3 library.
|
|
@@ -14,8 +14,6 @@ pip install syncraft
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
## TODO
|
|
17
|
-
- [ ] simplify the result of then_left and then_right by bimap the result in syntax.
|
|
18
|
-
- [ ] simplify the result of sep_by and between by bimap the result in syntax
|
|
19
17
|
- [ ] convert to dict/dataclass via bimap in syntax
|
|
20
18
|
- [ ] define DSL over Variable to construct predicates
|
|
21
19
|
- [ ] Try the parsing, generation, and data processing machinery on SQLite3 syntax. So that I can have direct feedback on the usability of this library and a fully functional SQLite3 library.
|
|
@@ -5,12 +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
|
|
9
9
|
from weakref import WeakKeyDictionary
|
|
10
10
|
from abc import ABC
|
|
11
|
-
from syncraft.ast import ThenKind, Then, Choice, Many, ChoiceKind
|
|
11
|
+
from syncraft.ast import ThenKind, Then, Choice, Many, ChoiceKind, shallow_dict
|
|
12
12
|
from syncraft.constraint import Bindable
|
|
13
13
|
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
14
17
|
S = TypeVar('S', bound=Bindable)
|
|
15
18
|
|
|
16
19
|
A = TypeVar('A') # Result type
|
|
@@ -64,7 +67,7 @@ class Error:
|
|
|
64
67
|
lst = []
|
|
65
68
|
current: Optional[Error] = self
|
|
66
69
|
while current is not None:
|
|
67
|
-
d =
|
|
70
|
+
d = shallow_dict(current)
|
|
68
71
|
lst.append({k:v for k,v in d.items() if v is not None and k != 'previous'})
|
|
69
72
|
current = current.previous
|
|
70
73
|
return lst
|
|
@@ -4,14 +4,20 @@ from __future__ import annotations
|
|
|
4
4
|
import re
|
|
5
5
|
from typing import (
|
|
6
6
|
Optional, Any, TypeVar, Tuple, runtime_checkable, cast,
|
|
7
|
-
Generic, Callable, Union, Protocol, Type, List, ClassVar
|
|
7
|
+
Generic, Callable, Union, Protocol, Type, List, ClassVar,
|
|
8
|
+
Dict
|
|
8
9
|
)
|
|
9
10
|
|
|
10
11
|
|
|
11
|
-
from dataclasses import dataclass, replace, is_dataclass,
|
|
12
|
+
from dataclasses import dataclass, replace, is_dataclass, fields
|
|
12
13
|
from enum import Enum
|
|
13
14
|
|
|
14
15
|
|
|
16
|
+
def shallow_dict(a: Any)->Dict[str, Any]:
|
|
17
|
+
assert is_dataclass(a), f"Expected dataclass instance for collector inverse, got {type(a)}"
|
|
18
|
+
return {f.name: getattr(a, f.name) for f in fields(a)}
|
|
19
|
+
|
|
20
|
+
|
|
15
21
|
|
|
16
22
|
|
|
17
23
|
A = TypeVar('A')
|
|
@@ -287,37 +293,42 @@ class Collect(Generic[A, E], AST):
|
|
|
287
293
|
collector: Collector
|
|
288
294
|
value: A
|
|
289
295
|
def bimap(self, r: Bimap[A, B]=Bimap.identity()) -> Tuple[B | E, Callable[[B | E], Collect[A, E]]]:
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
assert
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
assert is_dataclass(e), f"Expected dataclass instance for collector inverse, got {type(e)}"
|
|
306
|
-
named_dict = asdict(e)
|
|
307
|
-
unnamed = []
|
|
308
|
-
for f in fields(e):
|
|
309
|
-
if f.name not in named:
|
|
310
|
-
unnamed.append(named_dict[f.name])
|
|
311
|
-
tmp = []
|
|
312
|
-
for x in index:
|
|
313
|
-
if isinstance(x, str):
|
|
314
|
-
tmp.append(Marked(name=x, value=named_dict[x]))
|
|
296
|
+
|
|
297
|
+
def inv_one_positional(e: E) -> B:
|
|
298
|
+
assert is_dataclass(e), f"Expected dataclass instance for collector inverse, got {type(e)}"
|
|
299
|
+
named_dict = shallow_dict(e)
|
|
300
|
+
return named_dict[fields(e)[0].name]
|
|
301
|
+
|
|
302
|
+
b, inner_f = self.value.bimap(r) if isinstance(self.value, AST) else r(self.value)
|
|
303
|
+
if isinstance(self.value, Then):
|
|
304
|
+
if isinstance(b, tuple):
|
|
305
|
+
index: List[str | int] = []
|
|
306
|
+
named_count = 0
|
|
307
|
+
for i, v in enumerate(b):
|
|
308
|
+
if isinstance(v, Marked):
|
|
309
|
+
index.append(v.name)
|
|
310
|
+
named_count += 1
|
|
315
311
|
else:
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
312
|
+
index.append(i - named_count)
|
|
313
|
+
named = {v.name: v.value for v in b if isinstance(v, Marked)}
|
|
314
|
+
unnamed = [v for v in b if not isinstance(v, Marked)]
|
|
315
|
+
ret: E = self.collector(*unnamed, **named)
|
|
316
|
+
def invf(e: E) -> Tuple[Any, ...]:
|
|
317
|
+
assert is_dataclass(e), f"Expected dataclass instance for collector inverse, got {type(e)}"
|
|
318
|
+
named_dict = shallow_dict(e)
|
|
319
|
+
unnamed = []
|
|
320
|
+
for f in fields(e):
|
|
321
|
+
if f.name not in named:
|
|
322
|
+
unnamed.append(named_dict[f.name])
|
|
323
|
+
tmp = []
|
|
324
|
+
for x in index:
|
|
325
|
+
if isinstance(x, str):
|
|
326
|
+
tmp.append(Marked(name=x, value=named_dict[x]))
|
|
327
|
+
else:
|
|
328
|
+
tmp.append(unnamed[x])
|
|
329
|
+
return tuple(tmp)
|
|
330
|
+
return ret, lambda e: replace(self, value=inner_f(invf(e))) # type: ignore
|
|
331
|
+
return self.collector(b), lambda e: replace(self, value=inner_f(inv_one_positional(e))) # type: ignore
|
|
321
332
|
|
|
322
333
|
#########################################################################################################################
|
|
323
334
|
@dataclass(frozen=True)
|
|
@@ -165,9 +165,24 @@ class Syntax(Generic[A, S]):
|
|
|
165
165
|
def between(self, left: Syntax[B, S], right: Syntax[C, S]) -> Syntax[Then[B, Then[A, C]], S]:
|
|
166
166
|
return left >> self // right
|
|
167
167
|
|
|
168
|
-
def sep_by(self,
|
|
168
|
+
def sep_by(self,
|
|
169
|
+
sep: Syntax[B, S]) -> Syntax[Then[A, Choice[Many[Then[B, A]], Optional[Nothing]]], S]:
|
|
169
170
|
ret: Syntax[Then[A, Choice[Many[Then[B, A]], Optional[Nothing]]], S] = (self + (sep >> self).many().optional())
|
|
170
|
-
|
|
171
|
+
def f(a: Then[A, Choice[Many[A], Optional[Nothing]]]) -> Many[A]:
|
|
172
|
+
if a.right.kind == ChoiceKind.LEFT and isinstance(a.right.value, Many):
|
|
173
|
+
if len(a.right.value.value) == 0:
|
|
174
|
+
return Many(value = (a.left,))
|
|
175
|
+
else:
|
|
176
|
+
return Many(value = (a.left,) + a.right.value.value)
|
|
177
|
+
else:
|
|
178
|
+
return Many(value = (a.left,))
|
|
179
|
+
def i(a: Many[A]) -> Then[A, Choice[Many[A], Optional[Nothing]]]:
|
|
180
|
+
assert len(a.value) >= 1, f"sep_by expect at least one element, got {len(a.value)}. {a}"
|
|
181
|
+
if len(a.value) == 1:
|
|
182
|
+
return Then(kind=ThenKind.BOTH, left=a.value[0], right=Choice(kind=ChoiceKind.RIGHT, value=Nothing()))
|
|
183
|
+
else:
|
|
184
|
+
return Then(kind= ThenKind.BOTH, left=a.value[0], right=Choice(kind=ChoiceKind.LEFT, value=Many(value=a.value[1:])))
|
|
185
|
+
return ret.bimap(f,i).describe( # type: ignore
|
|
171
186
|
name='sep_by',
|
|
172
187
|
fixity='prefix',
|
|
173
188
|
parameter=(self, sep))
|
|
@@ -191,17 +206,11 @@ class Syntax(Generic[A, S]):
|
|
|
191
206
|
other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
|
|
192
207
|
ret: Syntax[Then[A, B], S] = self.__class__(lambda cls: self.alg(cls).then_left(other.alg(cls))) # type: ignore
|
|
193
208
|
return ret.describe(name=ThenKind.LEFT.value, fixity='infix', parameter=(self, other)).as_(Syntax[Then[A, B], S])
|
|
194
|
-
|
|
195
|
-
def __lshift__(self, other: Syntax[B, S]) -> Syntax[Then[A, B], S]:
|
|
196
|
-
return self.__floordiv__(other)
|
|
197
|
-
|
|
209
|
+
|
|
198
210
|
def __rfloordiv__(self, other: Syntax[B, S]) -> Syntax[Then[B, A], S]:
|
|
199
211
|
other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
|
|
200
212
|
return other.__floordiv__(self)
|
|
201
213
|
|
|
202
|
-
def __rlshift__(self, other: Syntax[B, S]) -> Syntax[Then[B, A], S]:
|
|
203
|
-
return self.__rfloordiv__(other)
|
|
204
|
-
|
|
205
214
|
def __add__(self, other: Syntax[B, S]) -> Syntax[Then[A, B], S]:
|
|
206
215
|
other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
|
|
207
216
|
ret: Syntax[Then[A, B], S] = self.__class__(lambda cls: self.alg(cls).then_both(other.alg(cls))) # type: ignore
|
|
@@ -260,16 +269,16 @@ class Syntax(Generic[A, S]):
|
|
|
260
269
|
return self.bimap(to_f, ito_f).describe(name=f'to({f})', fixity='postfix', parameter=(self,))
|
|
261
270
|
|
|
262
271
|
|
|
263
|
-
def mark(self,
|
|
272
|
+
def mark(self, name: str) -> Syntax[Marked[A], S]:
|
|
264
273
|
def bind_s(value: A) -> Marked[A]:
|
|
265
274
|
if isinstance(value, Marked):
|
|
266
|
-
return replace(value, name=
|
|
275
|
+
return replace(value, name=name)
|
|
267
276
|
else:
|
|
268
|
-
return Marked(name=
|
|
277
|
+
return Marked(name=name, value=value)
|
|
269
278
|
def ibind_s(m : Marked[A]) -> A:
|
|
270
279
|
return m.value if isinstance(m, Marked) else m
|
|
271
280
|
|
|
272
|
-
return self.bimap(bind_s, ibind_s).describe(name=f'bind("{
|
|
281
|
+
return self.bimap(bind_s, ibind_s).describe(name=f'bind("{name}")', fixity='postfix', parameter=(self,))
|
|
273
282
|
|
|
274
283
|
|
|
275
284
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: syncraft
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.31
|
|
4
4
|
Summary: Parser combinator library
|
|
5
5
|
Author-email: Michael Afmokt <michael@esacca.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -29,8 +29,6 @@ pip install syncraft
|
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
## TODO
|
|
32
|
-
- [ ] simplify the result of then_left and then_right by bimap the result in syntax.
|
|
33
|
-
- [ ] simplify the result of sep_by and between by bimap the result in syntax
|
|
34
32
|
- [ ] convert to dict/dataclass via bimap in syntax
|
|
35
33
|
- [ ] define DSL over Variable to construct predicates
|
|
36
34
|
- [ ] Try the parsing, generation, and data processing machinery on SQLite3 syntax. So that I can have direct feedback on the usability of this library and a fully functional SQLite3 library.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from syncraft.ast import Then, ThenKind, Many, Choice, ChoiceKind, Token
|
|
3
|
+
from syncraft.ast import Then, ThenKind, Many, Choice, ChoiceKind, Token, Marked, Nothing
|
|
4
4
|
from syncraft.algebra import Error
|
|
5
5
|
from syncraft.parser import literal, parse
|
|
6
6
|
import syncraft.generator as gen
|
|
@@ -19,9 +19,9 @@ def test1_simple_then() -> None:
|
|
|
19
19
|
print("---" * 40)
|
|
20
20
|
print(generated)
|
|
21
21
|
assert ast == generated
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
value, bmap = generated.bimap()
|
|
23
|
+
print(value)
|
|
24
|
+
assert gen.generate(syntax, bmap(value)) == generated
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
def test2_named_results() -> None:
|
|
@@ -35,10 +35,9 @@ def test2_named_results() -> None:
|
|
|
35
35
|
print("---" * 40)
|
|
36
36
|
print(generated)
|
|
37
37
|
assert ast == generated
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
# assert bmap(value) == generated
|
|
38
|
+
value, bmap = generated.bimap()
|
|
39
|
+
assert gen.generate(syntax, bmap(value)) == generated
|
|
40
|
+
|
|
42
41
|
|
|
43
42
|
|
|
44
43
|
def test3_many_literals() -> None:
|
|
@@ -52,9 +51,8 @@ def test3_many_literals() -> None:
|
|
|
52
51
|
print("---" * 40)
|
|
53
52
|
print(generated)
|
|
54
53
|
assert ast == generated
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
# assert bmap(value) == generated
|
|
54
|
+
value, bmap = generated.bimap()
|
|
55
|
+
assert gen.generate(syntax, bmap(value)) == generated
|
|
58
56
|
|
|
59
57
|
|
|
60
58
|
def test4_mixed_many_named() -> None:
|
|
@@ -69,9 +67,8 @@ def test4_mixed_many_named() -> None:
|
|
|
69
67
|
print("---" * 40)
|
|
70
68
|
print(generated)
|
|
71
69
|
assert ast == generated
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
# assert bmap(value) == generated
|
|
70
|
+
value, bmap = generated.bimap()
|
|
71
|
+
assert gen.generate(syntax, bmap(value)) == generated
|
|
75
72
|
|
|
76
73
|
|
|
77
74
|
def test5_nested_then_many() -> None:
|
|
@@ -85,9 +82,8 @@ def test5_nested_then_many() -> None:
|
|
|
85
82
|
print("---" * 40)
|
|
86
83
|
print(generated)
|
|
87
84
|
assert ast == generated
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
# assert bmap(value) == generated
|
|
85
|
+
value, bmap = generated.bimap()
|
|
86
|
+
assert gen.generate(syntax, bmap(value), restore_pruned=True) == generated
|
|
91
87
|
|
|
92
88
|
|
|
93
89
|
|
|
@@ -99,8 +95,8 @@ def test_then_flatten():
|
|
|
99
95
|
print(ast)
|
|
100
96
|
generated = gen.generate(syntax, ast)
|
|
101
97
|
assert ast == generated
|
|
102
|
-
|
|
103
|
-
|
|
98
|
+
value, bmap = generated.bimap()
|
|
99
|
+
assert gen.generate(syntax, bmap(value)) == generated
|
|
104
100
|
|
|
105
101
|
|
|
106
102
|
|
|
@@ -114,11 +110,8 @@ def test_named_in_then():
|
|
|
114
110
|
print(ast)
|
|
115
111
|
generated = gen.generate(syntax, ast)
|
|
116
112
|
assert ast == generated
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
# print(value)
|
|
120
|
-
# assert set(x.name for x in value if isinstance(x, Marked)) == {"first", "second", "third"}
|
|
121
|
-
# assert bmap(value) == ast
|
|
113
|
+
value, bmap = generated.bimap()
|
|
114
|
+
assert gen.generate(syntax, bmap(value)) == generated
|
|
122
115
|
|
|
123
116
|
|
|
124
117
|
def test_named_in_many():
|
|
@@ -129,10 +122,8 @@ def test_named_in_many():
|
|
|
129
122
|
print(ast)
|
|
130
123
|
generated = gen.generate(syntax, ast)
|
|
131
124
|
assert ast == generated
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
# assert all(isinstance(v, Marked) for v in value if isinstance(v, Marked))
|
|
135
|
-
# assert bmap(value) == ast
|
|
125
|
+
value, bmap = generated.bimap()
|
|
126
|
+
assert gen.generate(syntax, bmap(value)) == generated
|
|
136
127
|
|
|
137
128
|
|
|
138
129
|
def test_named_in_or():
|
|
@@ -144,10 +135,8 @@ def test_named_in_or():
|
|
|
144
135
|
print(ast)
|
|
145
136
|
generated = gen.generate(syntax, ast)
|
|
146
137
|
assert ast == generated
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
# assert value.name == "b"
|
|
150
|
-
# assert bmap(value) == ast
|
|
138
|
+
value, bmap = generated.bimap()
|
|
139
|
+
assert gen.generate(syntax, bmap(value)) == generated
|
|
151
140
|
|
|
152
141
|
|
|
153
142
|
|
|
@@ -165,8 +154,8 @@ def test_deep_mix():
|
|
|
165
154
|
print('---' * 40)
|
|
166
155
|
print(generated)
|
|
167
156
|
assert ast == generated
|
|
168
|
-
|
|
169
|
-
|
|
157
|
+
value, bmap = generated.bimap()
|
|
158
|
+
assert gen.generate(syntax, bmap(value)) == generated
|
|
170
159
|
|
|
171
160
|
|
|
172
161
|
def test_empty_many() -> None:
|
|
@@ -183,8 +172,8 @@ def test_backtracking_many() -> None:
|
|
|
183
172
|
syntax = (A.many() + B) # must not eat the final "a" needed for B
|
|
184
173
|
sql = "a a a a b"
|
|
185
174
|
ast = parse(syntax, sql, dialect="sqlite")
|
|
186
|
-
|
|
187
|
-
|
|
175
|
+
value, bmap = ast.bimap()
|
|
176
|
+
assert gen.generate(syntax, bmap(value)) == ast
|
|
188
177
|
|
|
189
178
|
def test_deep_nesting() -> None:
|
|
190
179
|
A = literal("a")
|
|
@@ -209,9 +198,8 @@ def test_named_many() -> None:
|
|
|
209
198
|
syntax = A.many()
|
|
210
199
|
sql = "a a"
|
|
211
200
|
ast = parse(syntax, sql, dialect="sqlite")
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
# assert all(isinstance(x, Marked) for x in flattened)
|
|
201
|
+
value, bmap = ast.bimap()
|
|
202
|
+
assert gen.generate(syntax, bmap(value)) == ast
|
|
215
203
|
|
|
216
204
|
|
|
217
205
|
def test_or_named() -> None:
|
|
@@ -220,10 +208,8 @@ def test_or_named() -> None:
|
|
|
220
208
|
syntax = A | B
|
|
221
209
|
sql = "b"
|
|
222
210
|
ast = parse(syntax, sql, dialect="sqlite")
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
# value, _ = ast.bimap()
|
|
226
|
-
# assert value == Marked(name="y", value=TokenGen.from_string("b"))
|
|
211
|
+
value, bmap = ast.bimap()
|
|
212
|
+
assert gen.generate(syntax, bmap(value)) == ast
|
|
227
213
|
|
|
228
214
|
|
|
229
215
|
def test_then_associativity() -> None:
|
|
@@ -269,11 +255,11 @@ def test_optional():
|
|
|
269
255
|
A = literal("a").mark("a")
|
|
270
256
|
syntax = A.optional()
|
|
271
257
|
ast1 = parse(syntax, "", dialect="sqlite")
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
258
|
+
v1, _ = ast1.bimap()
|
|
259
|
+
assert isinstance(v1, Nothing)
|
|
260
|
+
ast2 = parse(syntax, "a", dialect="sqlite")
|
|
261
|
+
v2, _ = ast2.bimap()
|
|
262
|
+
assert v2 == Marked(name='a', value=TokenGen.from_string('a'))
|
|
277
263
|
|
|
278
264
|
|
|
279
265
|
|
|
@@ -16,6 +16,8 @@ def test_between()->None:
|
|
|
16
16
|
ast:AST = parse(syntax, sql, dialect='sqlite')
|
|
17
17
|
generated = gen.generate(syntax, ast)
|
|
18
18
|
assert ast == generated, "Parsed and generated results do not match."
|
|
19
|
+
x, f = generated.bimap()
|
|
20
|
+
assert gen.generate(syntax, f(x)) == ast
|
|
19
21
|
|
|
20
22
|
|
|
21
23
|
def test_sep_by()->None:
|
|
@@ -24,6 +26,8 @@ def test_sep_by()->None:
|
|
|
24
26
|
ast:AST = parse(syntax, sql, dialect='sqlite')
|
|
25
27
|
generated = gen.generate(syntax, ast)
|
|
26
28
|
assert ast == generated, "Parsed and generated results do not match."
|
|
29
|
+
x, f = generated.bimap()
|
|
30
|
+
assert gen.generate(syntax, f(x)) == ast
|
|
27
31
|
|
|
28
32
|
def test_many_or()->None:
|
|
29
33
|
IF = literal("if")
|
|
@@ -34,3 +38,5 @@ def test_many_or()->None:
|
|
|
34
38
|
ast:AST = parse(syntax, sql, dialect='sqlite')
|
|
35
39
|
generated = gen.generate(syntax, ast)
|
|
36
40
|
assert ast == generated, "Parsed and generated results do not match."
|
|
41
|
+
x, f = generated.bimap()
|
|
42
|
+
assert gen.generate(syntax, f(x)) == ast
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|