syncraft 0.1.30__tar.gz → 0.1.32__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.

Files changed (25) hide show
  1. {syncraft-0.1.30 → syncraft-0.1.32}/PKG-INFO +1 -1
  2. {syncraft-0.1.30 → syncraft-0.1.32}/pyproject.toml +1 -1
  3. {syncraft-0.1.30 → syncraft-0.1.32}/syncraft/algebra.py +6 -3
  4. {syncraft-0.1.30 → syncraft-0.1.32}/syncraft/ast.py +50 -34
  5. {syncraft-0.1.30 → syncraft-0.1.32}/syncraft/syntax.py +28 -13
  6. {syncraft-0.1.30 → syncraft-0.1.32}/syncraft.egg-info/PKG-INFO +1 -1
  7. {syncraft-0.1.30 → syncraft-0.1.32}/syncraft.egg-info/SOURCES.txt +1 -0
  8. syncraft-0.1.32/tests/test_to.py +51 -0
  9. {syncraft-0.1.30 → syncraft-0.1.32}/LICENSE +0 -0
  10. {syncraft-0.1.30 → syncraft-0.1.32}/README.md +0 -0
  11. {syncraft-0.1.30 → syncraft-0.1.32}/setup.cfg +0 -0
  12. {syncraft-0.1.30 → syncraft-0.1.32}/syncraft/__init__.py +0 -0
  13. {syncraft-0.1.30 → syncraft-0.1.32}/syncraft/constraint.py +0 -0
  14. {syncraft-0.1.30 → syncraft-0.1.32}/syncraft/diagnostic.py +0 -0
  15. {syncraft-0.1.30 → syncraft-0.1.32}/syncraft/finder.py +0 -0
  16. {syncraft-0.1.30 → syncraft-0.1.32}/syncraft/generator.py +0 -0
  17. {syncraft-0.1.30 → syncraft-0.1.32}/syncraft/parser.py +0 -0
  18. {syncraft-0.1.30 → syncraft-0.1.32}/syncraft/py.typed +0 -0
  19. {syncraft-0.1.30 → syncraft-0.1.32}/syncraft/sqlite3.py +0 -0
  20. {syncraft-0.1.30 → syncraft-0.1.32}/syncraft.egg-info/dependency_links.txt +0 -0
  21. {syncraft-0.1.30 → syncraft-0.1.32}/syncraft.egg-info/requires.txt +0 -0
  22. {syncraft-0.1.30 → syncraft-0.1.32}/syncraft.egg-info/top_level.txt +0 -0
  23. {syncraft-0.1.30 → syncraft-0.1.32}/tests/test_bimap.py +0 -0
  24. {syncraft-0.1.30 → syncraft-0.1.32}/tests/test_parse.py +0 -0
  25. {syncraft-0.1.30 → syncraft-0.1.32}/tests/test_until.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: syncraft
3
- Version: 0.1.30
3
+ Version: 0.1.32
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.30"
3
+ version = "0.1.32"
4
4
  description = "Parser combinator library"
5
5
  license = "MIT"
6
6
  license-files = ["LICENSE"]
@@ -5,12 +5,15 @@ from typing import (
5
5
  )
6
6
 
7
7
  import traceback
8
- from dataclasses import dataclass, replace, asdict
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 = asdict(current)
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, asdict, fields
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')
@@ -182,6 +188,11 @@ class AST:
182
188
 
183
189
  @dataclass(frozen=True)
184
190
  class Nothing(AST):
191
+ _instance = None
192
+ def __new__(cls):
193
+ if cls._instance is None:
194
+ cls._instance = super(Nothing, cls).__new__(cls)
195
+ return cls._instance
185
196
  def __str__(self)->str:
186
197
  return self.__class__.__name__
187
198
  def __repr__(self)->str:
@@ -220,8 +231,8 @@ class Many(Generic[A], AST):
220
231
  if len(bs) <= len(ret):
221
232
  return Many(value = tuple(ret[i][1](bs[i]) for i in range(len(bs))))
222
233
  else:
223
- half = [ret[i][1](bs[i]) for i in range(len(bs))]
224
- tmp = [ret[-1][1](bs[i]) for i in range(len(ret)-1, len(bs))]
234
+ half = [ret[i][1](bs[i]) for i in range(len(ret))]
235
+ tmp = [ret[-1][1](bs[i]) for i in range(len(ret), len(bs))]
225
236
  return Many(value = tuple(half + tmp))
226
237
  return [v[0] for v in ret], inv
227
238
 
@@ -287,37 +298,42 @@ class Collect(Generic[A, E], AST):
287
298
  collector: Collector
288
299
  value: A
289
300
  def bimap(self, r: Bimap[A, B]=Bimap.identity()) -> Tuple[B | E, Callable[[B | E], Collect[A, E]]]:
290
- b, inner_f = r(self.value)
291
- if isinstance(self.value, Then) and self.value.kind == ThenKind.BOTH:
292
- assert isinstance(b, tuple), f"Expected tuple from Then.BOTH combinator, got {type(b)}"
293
- index: List[str | int] = []
294
- named_count = 0
295
- for i, v in enumerate(b):
296
- if isinstance(v, Marked):
297
- index.append(v.name)
298
- named_count += 1
299
- else:
300
- index.append(i - named_count)
301
- named = {v.name: v.value for v in b if isinstance(v, Marked)}
302
- unnamed = [v for v in b if not isinstance(v, Marked)]
303
- ret: E = self.collector(*unnamed, **named)
304
- def invf(e: E) -> Tuple[Any, ...]:
305
- assert is_dataclass(e), f"Expected dataclass instance for collector inverse, got {type(e)}"
306
- named_dict = asdict(e)
307
- unnamed = []
308
- for f in fields(e):
309
- if f.name not in named:
310
- unnamed.append(named_dict[f.name])
311
- tmp = []
312
- for x in index:
313
- if isinstance(x, str):
314
- tmp.append(Marked(name=x, value=named_dict[x]))
301
+
302
+ def inv_one_positional(e: E) -> B:
303
+ assert is_dataclass(e), f"Expected dataclass instance for collector inverse, got {type(e)}"
304
+ named_dict = shallow_dict(e)
305
+ return named_dict[fields(e)[0].name]
306
+
307
+ b, inner_f = self.value.bimap(r) if isinstance(self.value, AST) else r(self.value)
308
+ if isinstance(self.value, Then):
309
+ if isinstance(b, tuple):
310
+ index: List[str | int] = []
311
+ named_count = 0
312
+ for i, v in enumerate(b):
313
+ if isinstance(v, Marked):
314
+ index.append(v.name)
315
+ named_count += 1
315
316
  else:
316
- tmp.append(unnamed[x])
317
- return tuple(tmp)
318
- return ret, lambda e: replace(self, value=inner_f(invf(e))) # type: ignore
319
- else:
320
- return b, lambda e: replace(self, value=inner_f(e)) # type: ignore
317
+ index.append(i - named_count)
318
+ named = {v.name: v.value for v in b if isinstance(v, Marked)}
319
+ unnamed = [v for v in b if not isinstance(v, Marked)]
320
+ ret: E = self.collector(*unnamed, **named)
321
+ def invf(e: E) -> Tuple[Any, ...]:
322
+ assert is_dataclass(e), f"Expected dataclass instance for collector inverse, got {type(e)}"
323
+ named_dict = shallow_dict(e)
324
+ unnamed = []
325
+ for f in fields(e):
326
+ if f.name not in named:
327
+ unnamed.append(named_dict[f.name])
328
+ tmp = []
329
+ for x in index:
330
+ if isinstance(x, str):
331
+ tmp.append(Marked(name=x, value=named_dict[x]))
332
+ else:
333
+ tmp.append(unnamed[x])
334
+ return tuple(tmp)
335
+ return ret, lambda e: replace(self, value=inner_f(invf(e))) # type: ignore
336
+ return self.collector(b), lambda e: replace(self, value=inner_f(inv_one_positional(e))) # type: ignore
321
337
 
322
338
  #########################################################################################################################
323
339
  @dataclass(frozen=True)
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from typing import (
4
4
  Optional, Any, TypeVar, Generic, Callable, Tuple, cast,
5
- Type, Literal
5
+ Type, Literal, List
6
6
  )
7
7
  from dataclasses import dataclass, field, replace
8
8
  from functools import reduce
@@ -165,8 +165,29 @@ 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, sep: Syntax[B, S]) -> Syntax[Then[A, Choice[Many[Then[B, A]], Optional[Nothing]]], S]:
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())
171
+ def f(a: Then[A, Choice[Many[Then[B, A]], Optional[Nothing]]]) -> Many[A]:
172
+ match a:
173
+ case Then(kind=ThenKind.BOTH, left=left, right=Choice(kind=ChoiceKind.RIGHT, value=Nothing())):
174
+ return Many(value = (left,))
175
+ case Then(kind=ThenKind.BOTH, left=left, right=Choice(kind=ChoiceKind.LEFT, value=Many(value=bs))):
176
+ return Many(value = (left,) + tuple([b.right for b in bs]))
177
+ case _:
178
+ raise ValueError(f"Bad data shape {a}")
179
+
180
+ def i(a: Many[A]) -> Then[A, Choice[Many[Then[B|None, A]], Optional[Nothing]]]:
181
+ assert len(a.value) >= 1, f"sep_by expect at least one element, got {len(a.value)}. {a}"
182
+ if len(a.value) == 1:
183
+ return Then(kind=ThenKind.BOTH, left=a.value[0], right=Choice(kind=ChoiceKind.RIGHT, value=Nothing()))
184
+ else:
185
+ v: List[Then[B|None, A]] = [Then(kind=ThenKind.RIGHT, right=x, left=None) for x in a.value[1:]]
186
+ return Then(kind= ThenKind.BOTH,
187
+ left=a.value[0],
188
+ right=Choice(kind=ChoiceKind.LEFT,
189
+ value=Many(value=tuple(v))))
190
+ ret = ret.bimap(f,i) # type: ignore
170
191
  return ret.describe(
171
192
  name='sep_by',
172
193
  fixity='prefix',
@@ -191,17 +212,11 @@ class Syntax(Generic[A, S]):
191
212
  other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
192
213
  ret: Syntax[Then[A, B], S] = self.__class__(lambda cls: self.alg(cls).then_left(other.alg(cls))) # type: ignore
193
214
  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
-
215
+
198
216
  def __rfloordiv__(self, other: Syntax[B, S]) -> Syntax[Then[B, A], S]:
199
217
  other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
200
218
  return other.__floordiv__(self)
201
219
 
202
- def __rlshift__(self, other: Syntax[B, S]) -> Syntax[Then[B, A], S]:
203
- return self.__rfloordiv__(other)
204
-
205
220
  def __add__(self, other: Syntax[B, S]) -> Syntax[Then[A, B], S]:
206
221
  other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
207
222
  ret: Syntax[Then[A, B], S] = self.__class__(lambda cls: self.alg(cls).then_both(other.alg(cls))) # type: ignore
@@ -260,16 +275,16 @@ class Syntax(Generic[A, S]):
260
275
  return self.bimap(to_f, ito_f).describe(name=f'to({f})', fixity='postfix', parameter=(self,))
261
276
 
262
277
 
263
- def mark(self, var: str) -> Syntax[Marked[A], S]:
278
+ def mark(self, name: str) -> Syntax[Marked[A], S]:
264
279
  def bind_s(value: A) -> Marked[A]:
265
280
  if isinstance(value, Marked):
266
- return replace(value, name=var)
281
+ return replace(value, name=name)
267
282
  else:
268
- return Marked(name=var, value=value)
283
+ return Marked(name=name, value=value)
269
284
  def ibind_s(m : Marked[A]) -> A:
270
285
  return m.value if isinstance(m, Marked) else m
271
286
 
272
- return self.bimap(bind_s, ibind_s).describe(name=f'bind("{var}")', fixity='postfix', parameter=(self,))
287
+ return self.bimap(bind_s, ibind_s).describe(name=f'bind("{name}")', fixity='postfix', parameter=(self,))
273
288
 
274
289
 
275
290
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: syncraft
3
- Version: 0.1.30
3
+ Version: 0.1.32
4
4
  Summary: Parser combinator library
5
5
  Author-email: Michael Afmokt <michael@esacca.com>
6
6
  License-Expression: MIT
@@ -19,4 +19,5 @@ syncraft.egg-info/requires.txt
19
19
  syncraft.egg-info/top_level.txt
20
20
  tests/test_bimap.py
21
21
  tests/test_parse.py
22
+ tests/test_to.py
22
23
  tests/test_until.py
@@ -0,0 +1,51 @@
1
+ from __future__ import annotations
2
+ from typing import Any, List, Tuple
3
+ from syncraft.algebra import Either, Left, Right, Error
4
+ from syncraft.ast import Marked, Then, ThenKind, Many, Nothing
5
+ from syncraft.parser import literal, variable, parse, Parser, Token
6
+ from syncraft.generator import TokenGen
7
+ from rich import print
8
+ import syncraft.generator as gen
9
+ from dataclasses import dataclass
10
+
11
+
12
+ def test_to() -> None:
13
+ @dataclass
14
+ class IfThenElse:
15
+ condition: Any
16
+ then: Any
17
+ otherwise: Any
18
+
19
+ @dataclass
20
+ class While:
21
+ condition:Any
22
+ body:Any
23
+
24
+ WHILE = literal("while")
25
+ IF = literal("if")
26
+ ELSE = literal("else")
27
+ THEN = literal("then")
28
+ END = literal("end")
29
+ A = literal('a')
30
+ B = literal('b')
31
+ C = literal('c')
32
+ D = literal('d')
33
+ M = literal(',')
34
+ var = A | B | C | D
35
+ condition = var.sep_by(M).mark('condition')
36
+ ifthenelse = (IF >> condition
37
+ // THEN
38
+ + var.sep_by(M).mark('then')
39
+ // ELSE
40
+ + var.sep_by(M).mark('otherwise')
41
+ // END).to(IfThenElse).many()
42
+ syntax = (WHILE >> condition
43
+ + ifthenelse.mark('body')
44
+ // ~END).to(While)
45
+ sql = 'while b,c,c if a,b then c,d else a,d end if a,b then c,d else a,d end'
46
+ ast = parse(syntax, sql, dialect='sqlite')
47
+ g = gen.generate(syntax, ast, restore_pruned=True)
48
+ assert ast == g
49
+ x, f = g.bimap()
50
+ print(x)
51
+ assert gen.generate(syntax, f(x), restore_pruned=True) == 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