syncraft 0.1.14__tar.gz → 0.1.16__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: syncraft
3
- Version: 0.1.14
3
+ Version: 0.1.16
4
4
  Summary: Parser combinator library
5
5
  Author-email: Michael Afmokt <michael@esacca.com>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "syncraft"
3
- version = "0.1.14"
3
+ version = "0.1.16"
4
4
  description = "Parser combinator library"
5
5
  license = "MIT"
6
6
  license-files = ["LICENSE"]
@@ -170,17 +170,7 @@ class ThenResult(Generic[A, B], StructuralResult):
170
170
  right: B
171
171
  def bimap(self, ctx: Any) -> Tuple[Any, Callable[[Any], StructuralResult]]:
172
172
  def branch(b: Any) -> Tuple[Any, Callable[[Any], StructuralResult]]:
173
- if isinstance(b, ThenResult):
174
- value, backward = b.bimap(ctx)
175
- if isinstance(value, tuple):
176
- x, y = ThenResult.flat(value)
177
- return x, lambda data: ThenResult(self.kind, y(data), self.right)
178
- else:
179
- return value, backward
180
- elif isinstance(b, StructuralResult):
181
- return b.bimap(ctx)
182
- else:
183
- return b, lambda x: x
173
+ return b.bimap(ctx) if isinstance(b, StructuralResult) else (b, lambda x: x)
184
174
  match self.kind:
185
175
  case ThenKind.BOTH:
186
176
  left_value, left_bmap = branch(self.left)
@@ -147,24 +147,31 @@ class TokenGen(TokenSpec):
147
147
  @dataclass(frozen=True)
148
148
  class Generator(Algebra[GenResult[T], GenState[T]]):
149
149
  def flat_map(self, f: Callable[[GenResult[T]], Algebra[B, GenState[T]]]) -> Algebra[B, GenState[T]]:
150
- def flat_map_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[B, GenState[T]]]:
151
- wrapper = input.wrapper()
152
- 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
153
- lft = input.left()
154
- match self.run(lft, use_cache=use_cache):
155
- case Left(error):
156
- return Left(error)
157
- case Right((value, next_input)):
158
- r = input.right()
159
- match f(value).run(r, use_cache):
160
- case Left(e):
161
- return Left(e)
162
- case Right((result, next_input)):
163
- return Right((wrapper(result), next_input))
164
- raise ValueError("flat_map should always return a value or an error.")
165
- return Generator(run_f = flat_map_run, name=self.name) # type: ignore
166
-
167
-
150
+ def flat_map_run(original: GenState[T], use_cache:bool) -> Either[Any, Tuple[B, GenState[T]]]:
151
+ wrapper = original.wrapper()
152
+ 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
153
+ try:
154
+ lft = input.left()
155
+ match self.run(lft, use_cache=use_cache):
156
+ case Left(error):
157
+ return Left(error)
158
+ case Right((value, next_input)):
159
+ r = input.right()
160
+ match f(value).run(r, use_cache):
161
+ case Left(e):
162
+ return Left(e)
163
+ case Right((result, next_input)):
164
+ return Right((wrapper(result), next_input))
165
+ raise ValueError("flat_map should always return a value or an error.")
166
+ except Exception as e:
167
+ return Left(Error(
168
+ message=str(e),
169
+ this=self,
170
+ state=original,
171
+ error=e
172
+ ))
173
+ return Generator(run_f = flat_map_run, name=self.name) # type: ignore
174
+
168
175
 
169
176
  def many(self, *, at_least: int, at_most: Optional[int]) -> Algebra[ManyResult[GenResult[T]], GenState[T]]:
170
177
  assert at_least > 0, "at_least must be greater than 0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: syncraft
3
- Version: 0.1.14
3
+ Version: 0.1.16
4
4
  Summary: Parser combinator library
5
5
  Author-email: Michael Afmokt <michael@esacca.com>
6
6
  License-Expression: MIT
@@ -16,5 +16,6 @@ syncraft.egg-info/SOURCES.txt
16
16
  syncraft.egg-info/dependency_links.txt
17
17
  syncraft.egg-info/requires.txt
18
18
  syncraft.egg-info/top_level.txt
19
+ tests/test_bimap.py
19
20
  tests/test_parse.py
20
21
  tests/test_until.py
@@ -0,0 +1,166 @@
1
+ from __future__ import annotations
2
+ from syncraft.algebra import NamedResult
3
+ from syncraft.parser import literal, parse, Parser
4
+ import syncraft.generator as gen
5
+ from rich import print
6
+
7
+
8
+ def test1_simple_then() -> None:
9
+ A, B, C = literal("a"), literal("b"), literal("c")
10
+ syntax = A // B // C
11
+ sql = "a b c"
12
+ ast = parse(syntax(Parser), sql, dialect="sqlite")
13
+ print("---" * 40)
14
+ print(ast)
15
+ generated = gen.generate(syntax(gen.Generator), ast)
16
+ print("---" * 40)
17
+ print(generated)
18
+ assert ast == generated
19
+ value, bmap = generated.bimap(None)
20
+ print(value)
21
+ assert bmap(value) == generated
22
+
23
+
24
+ def test2_named_results() -> None:
25
+ A, B = literal("a").bind("x").bind('z'), literal("b").bind("y")
26
+ syntax = A // B
27
+ sql = "a b"
28
+ ast = parse(syntax(Parser), sql, dialect="sqlite")
29
+ print("---" * 40)
30
+ print(ast)
31
+ generated = gen.generate(syntax(gen.Generator), ast)
32
+ print("---" * 40)
33
+ print(generated)
34
+ assert ast == generated
35
+ value, bmap = generated.bimap(None)
36
+ print(value)
37
+ print(bmap(value))
38
+ assert bmap(value) == generated
39
+
40
+
41
+ def test3_many_literals() -> None:
42
+ A = literal("a")
43
+ syntax = A.many()
44
+ sql = "a a a"
45
+ ast = parse(syntax(Parser), sql, dialect="sqlite")
46
+ print("---" * 40)
47
+ print(ast)
48
+ generated = gen.generate(syntax(gen.Generator), ast)
49
+ print("---" * 40)
50
+ print(generated)
51
+ assert ast == generated
52
+ value, bmap = generated.bimap(None)
53
+ print(value)
54
+ assert bmap(value) == generated
55
+
56
+
57
+ def test4_mixed_many_named() -> None:
58
+ A = literal("a").bind("x")
59
+ B = literal("b")
60
+ syntax = (A | B).many()
61
+ sql = "a b a"
62
+ ast = parse(syntax(Parser), sql, dialect="sqlite")
63
+ print("---" * 40)
64
+ print(ast)
65
+ generated = gen.generate(syntax(gen.Generator), ast)
66
+ print("---" * 40)
67
+ print(generated)
68
+ assert ast == generated
69
+ value, bmap = generated.bimap(None)
70
+ print(value)
71
+ assert bmap(value) == generated
72
+
73
+
74
+ def test5_nested_then_many() -> None:
75
+ IF, THEN, END = literal("if"), literal("then"), literal("end")
76
+ syntax = (IF.many() // THEN.many()).many() // END
77
+ sql = "if if then end"
78
+ ast = parse(syntax(Parser), sql, dialect="sqlite")
79
+ print("---" * 40)
80
+ print(ast)
81
+ generated = gen.generate(syntax(gen.Generator), ast)
82
+ print("---" * 40)
83
+ print(generated)
84
+ # assert ast == generated
85
+ value, bmap = generated.bimap(None)
86
+ print(value)
87
+ assert bmap(value) == generated
88
+
89
+
90
+
91
+ def test_then_flatten():
92
+ A, B, C = literal("a"), literal("b"), literal("c")
93
+ syntax = A + (B + C)
94
+ sql = "a b c"
95
+ ast = parse(syntax(Parser), sql, dialect='sqlite')
96
+ print(ast)
97
+ generated = gen.generate(syntax(gen.Generator), ast)
98
+ assert ast == generated
99
+ value, bmap = ast.bimap(None)
100
+ assert bmap(value) == ast
101
+
102
+
103
+
104
+ def test_named_in_then():
105
+ A = literal("a").bind("first")
106
+ B = literal("b").bind("second")
107
+ C = literal("c").bind("third")
108
+ syntax = A + B + C
109
+ sql = "a b c"
110
+ ast = parse(syntax(Parser), sql, dialect='sqlite')
111
+ print(ast)
112
+ generated = gen.generate(syntax(gen.Generator), ast)
113
+ assert ast == generated
114
+ value, bmap = ast.bimap(None)
115
+ assert isinstance(value, tuple)
116
+ print(value)
117
+ assert set(x.name for x in value if isinstance(x, NamedResult)) == {"first", "second", "third"}
118
+ assert bmap(value) == ast
119
+
120
+
121
+ def test_named_in_many():
122
+ A = literal("x").bind("x")
123
+ syntax = A.many()
124
+ sql = "x x x"
125
+ ast = parse(syntax(Parser), sql, dialect='sqlite')
126
+ print(ast)
127
+ generated = gen.generate(syntax(gen.Generator), ast)
128
+ assert ast == generated
129
+ value, bmap = ast.bimap(None)
130
+ assert isinstance(value, list)
131
+ assert all(isinstance(v, NamedResult) for v in value if isinstance(v, NamedResult))
132
+ assert bmap(value) == ast
133
+
134
+
135
+ def test_named_in_or():
136
+ A = literal("a").bind("a")
137
+ B = literal("b").bind("b")
138
+ syntax = A | B
139
+ sql = "b"
140
+ ast = parse(syntax(Parser), sql, dialect='sqlite')
141
+ print(ast)
142
+ generated = gen.generate(syntax(gen.Generator), ast)
143
+ assert ast == generated
144
+ value, bmap = ast.bimap(None)
145
+ assert isinstance(value, NamedResult)
146
+ assert value.name == "b"
147
+ assert bmap(value) == ast
148
+
149
+
150
+
151
+
152
+
153
+ def test_deep_mix():
154
+ A = literal("a").bind("a")
155
+ B = literal("b")
156
+ C = literal("c").bind("c")
157
+ syntax = ((A + B) | C).many() + B
158
+ sql = "a b a b c b"
159
+ ast = parse(syntax(Parser), sql, dialect='sqlite')
160
+ print(ast)
161
+ generated = gen.generate(syntax(gen.Generator), ast)
162
+ print('---' * 40)
163
+ print(generated)
164
+ assert ast == generated
165
+ value, bmap = ast.bimap(None)
166
+ assert bmap(value) == 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