syncraft 0.1.18__py3-none-any.whl → 0.1.20__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 CHANGED
@@ -1,163 +1,120 @@
1
- """
2
- We want to parse a token stream into an AST, and then generate a new token stream from that AST.
3
- The generation should be a dual to the parsing. By 'dual' we mean that the generation algebra should be
4
- as close as possible to the parsing algebra. The closest algebra to the parsing algebra is the parsing
5
- algebra itself.
6
-
7
- Given:
8
- AST = Syntax(Parser)(ParserState([Token, ...]))
9
- AST =?= Syntax(Parser)(GenState(AST))
10
-
11
- where =?= means the LHS and RHS induce the same text output, e.g. the same token stream
12
- inspite of the token metadata, token types, and/or potentially different structure of the AST.
13
-
14
- With the above setting, Generator as a dual to Parser, can reuse most of the parsing combinator, the
15
- change needed is to introduce randomness in the generation process, e.g. to generate a random variable name, etc.
16
-
17
- [Token, ...] == Syntax(Generator)(GenState(AST))
18
-
19
- """
20
-
21
-
22
1
  from __future__ import annotations
23
-
24
2
  from typing import (
25
3
  Optional, List, Any, TypeVar, Generic, Callable, Tuple, cast,
26
- Dict, Type, ClassVar, Hashable,
27
- Mapping, Iterator
4
+ Dict, Type, ClassVar, Hashable
28
5
  )
29
6
 
30
7
  import traceback
31
- from dataclasses import dataclass, fields, replace, field
32
- from functools import cached_property
8
+ from dataclasses import dataclass, replace
33
9
  from weakref import WeakKeyDictionary
34
- from abc import ABC, abstractmethod
10
+ from abc import ABC
35
11
  from enum import Enum
12
+ from functools import reduce
36
13
 
37
14
 
38
-
39
-
40
- class Insptectable(ABC):
41
- @abstractmethod
42
- def to_string(self, interested: Callable[[Any], bool]) -> Optional[str]:
43
- raise NotImplementedError("Subclasses must implement to_string")
44
-
45
- @cached_property
46
- def _string(self)->Optional[str]:
47
- return self.to_string(lambda _: True)
48
-
49
- def __repr__(self) -> str:
50
- return self._string or self.__class__.__name__
51
- def __str__(self) -> str:
52
- return self._string or self.__class__.__name__
53
-
54
-
55
- A = TypeVar('A') # Result type
56
- B = TypeVar('B') # Result type for mapping
57
-
58
- S = TypeVar('S') # State type for the Algebra
59
-
60
-
61
-
62
- class FrozenDict(Generic[A]):
63
- def __init__(self, items: Mapping[str, A]):
64
- for k, v in items.items():
65
- if not isinstance(k, Hashable) or not isinstance(v, Hashable):
66
- raise TypeError(f"Metadata key or value not hashable: {k} = {v}")
67
- self._items = tuple(sorted(items.items()))
68
-
69
- def __getitem__(self, key: str) -> A:
70
- return dict(self._items)[key]
71
-
72
- def __contains__(self, key: str) -> bool:
73
- return key in dict(self._items)
74
-
75
- def items(self) -> Iterator[tuple[str, A]]:
76
- return iter(self._items)
77
-
78
- def to_dict(self) -> dict[str, A]:
79
- return dict(self._items)
80
-
81
- def __hash__(self) -> int:
82
- return hash(self._items)
83
-
84
- def __eq__(self, other: object) -> bool:
85
- return isinstance(other, FrozenDict) and self._items == other._items
86
-
87
- def __repr__(self) -> str:
88
- return f"FrozenDict({dict(self._items)})"
89
-
15
+ A = TypeVar('A')
16
+ B = TypeVar('B')
17
+ C = TypeVar('C')
18
+ S = TypeVar('S')
90
19
 
91
20
 
92
21
  @dataclass(frozen=True)
93
- class Lens(Generic[S, A]):
94
- get: Callable[[S], A]
95
- set: Callable[[S, A], S]
96
-
97
- def modify(self, source: S, f: Callable[[A], A]) -> S:
98
- return self.set(source, f(self.get(source)))
99
-
100
- def bimap(self, ff: Callable[[A], B], bf: Callable[[B], A]) -> Lens[S, B]:
101
- def getf(data: S) -> B:
102
- return ff(self.get(data))
103
-
104
- def setf(data: S, value: B) -> S:
105
- return self.set(data, bf(value))
106
-
107
- return Lens(get=getf, set=setf)
108
-
109
- def __truediv__(self, other: Lens[A, B]) -> Lens[S, B]:
110
- def get_composed(obj: S) -> B:
111
- return other.get(self.get(obj))
112
- def set_composed(obj: S, value: B) -> S:
113
- return self.set(obj, other.set(self.get(obj), value))
114
- return Lens(get=get_composed, set=set_composed)
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
+
115
58
 
116
- def __rtruediv__(self, other: Lens[B, S])->Lens[B, A]:
117
- return other.__truediv__(self)
118
59
 
119
60
  class StructuralResult:
120
- def bimap(self, ctx: Any)->Tuple[Any, Callable[[Any], StructuralResult]]:
121
- return (self, lambda x: self)
122
-
61
+ def bimap(self, arr: Bimap[Any, Any, Any] = Bimap.identity()) -> Bimap[Any, Any, Any]:
62
+ return Bimap.identity()
123
63
 
124
64
  @dataclass(frozen=True)
125
65
  class NamedResult(Generic[A], StructuralResult):
126
66
  name: str
127
67
  value: A
128
- def bimap(self, ctx: Any)->Tuple[NamedResult[Any], Callable[[NamedResult[Any]], StructuralResult]]:
129
- value, backward = self.value.bimap(ctx) if isinstance(self.value, StructuralResult) else (self.value, lambda x: x)
130
- def named_back(data: Any)->NamedResult[Any]:
131
- v = backward(data)
132
- if isinstance(v, NamedResult):
133
- return replace(v, name=self.name)
134
- else:
135
- return NamedResult(name=self.name, value=v)
136
- return NamedResult(self.name, value), named_back
137
-
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
+ )
138
84
  @dataclass(eq=True, frozen=True)
139
85
  class ManyResult(Generic[A], StructuralResult):
140
86
  value: Tuple[A, ...]
141
- def bimap(self, ctx: Any)->Tuple[List[Any], Callable[[List[Any]], StructuralResult]]:
142
- transformed = [v.bimap(ctx) if isinstance(v, StructuralResult) else (v, lambda x: x) for v in self.value]
143
- backmaps = [b for (_, b) in transformed]
144
- ret = [a for (a, _) in transformed]
145
- def backward(data: List[Any]) -> StructuralResult:
146
- if len(data) != len(transformed):
147
- raise ValueError("Incompatible data length")
148
- return ManyResult(value=tuple([backmaps[i](x) for i, x in enumerate(data)]))
149
- return ret, lambda data: backward(data)
150
-
151
-
152
-
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
+ )
153
101
  @dataclass(eq=True, frozen=True)
154
102
  class OrResult(Generic[A], StructuralResult):
155
103
  value: A
156
- def bimap(self, ctx: Any) -> Tuple[Any, Callable[[Any], StructuralResult]]:
157
- value, backward = self.value.bimap(ctx) if isinstance(self.value, StructuralResult) else (self.value, lambda x: x)
158
- return value, lambda data: OrResult(value=backward(data))
159
-
160
-
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
+ )
161
118
  class ThenKind(Enum):
162
119
  BOTH = '+'
163
120
  LEFT = '//'
@@ -168,49 +125,60 @@ class ThenResult(Generic[A, B], StructuralResult):
168
125
  kind: ThenKind
169
126
  left: A
170
127
  right: B
171
- def bimap(self, ctx: Any) -> Tuple[Any, Callable[[Any], StructuralResult]]:
172
- def branch(b: Any) -> Tuple[Any, Callable[[Any], StructuralResult]]:
173
- return b.bimap(ctx) if isinstance(b, StructuralResult) else (b, lambda x: x)
174
- match self.kind:
175
- case ThenKind.BOTH:
176
- left_value, left_bmap = branch(self.left)
177
- right_value, right_bmap = branch(self.right)
178
- def backward(x: Tuple[Any, Any]) -> StructuralResult:
179
- return ThenResult(self.kind, left_bmap(x[0]), right_bmap(x[1]))
180
- x, y = ThenResult.flat((left_value, right_value))
181
- return x, lambda data: backward(y(data))
182
- case ThenKind.LEFT:
183
- left_value, left_bmap = branch(self.left)
184
- return left_value, lambda data: ThenResult(self.kind, left_bmap(data), self.right)
185
- case ThenKind.RIGHT:
186
- right_value, right_bmap = branch(self.right)
187
- return right_value, lambda data: ThenResult(self.kind, self.left, right_bmap(data))
188
- @staticmethod
189
- def flat(array: Tuple[Any, Any]) -> Tuple[Tuple[Any, ...], Callable[[Tuple[Any, ...]], Tuple[Any, Any]]]:
190
- index: Dict[int, int] = {}
191
- ret: List[Any] = []
192
- for e in array:
193
- if isinstance(e, tuple):
194
- index[len(ret)] = len(e)
195
- ret.extend(e)
196
- else:
197
- ret.append(e)
198
- def backward(data: Tuple[Any, ...]) -> Tuple[Any, Any]:
199
- tmp: List[Any] = []
200
- skip: int = 0
201
- for i, e in enumerate(data):
202
- if skip <= 0:
203
- if i in index:
204
- tmp.append(tuple(data[i:i + index[i]]))
205
- skip = index[i] - 1
206
- else:
207
- tmp.append(e)
208
- else:
209
- skip -= 1
210
- return tuple(tmp)
211
- return tuple(ret), backward
212
-
213
-
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
+
214
182
  InProgress = object() # Marker for in-progress state, used to prevent re-entrance in recursive calls
215
183
  L = TypeVar('L') # Left type for combined results
216
184
  R = TypeVar('R') # Right type for combined results
@@ -233,7 +201,7 @@ class Right(Either[L, R]):
233
201
 
234
202
 
235
203
  @dataclass(frozen=True)
236
- class Error(Insptectable):
204
+ class Error:
237
205
  this: Any
238
206
  message: Optional[str] = None
239
207
  error: Optional[Any] = None
@@ -254,59 +222,9 @@ class Error(Insptectable):
254
222
  state=state,
255
223
  previous=self
256
224
  )
257
-
258
-
259
225
 
260
226
 
261
- def to_list(self, interested: Callable[[Any], bool]) -> List[Dict[str, str]]:
262
-
263
- def to_dict() -> Dict[str, str]:
264
- data: Dict[str, str] = {}
265
- for f in fields(self):
266
- value = getattr(self, f.name)
267
- if isinstance(value, Error):
268
- # self.previous
269
- pass
270
- elif isinstance(value, Insptectable):
271
- # self.this
272
- def inst(x: Any) -> bool:
273
- return x in self.algebras
274
- s = value.to_string(inst)
275
- data[f.name] = s if s is not None else repr(value)
276
- elif value is not None:
277
- # self.committed, self.message, self.expect, self.exception
278
- data[f.name] = repr(value)
279
- return data
280
- ret = []
281
- tmp : None | Error = self
282
- while tmp is not None:
283
- ret.append(to_dict())
284
- tmp = tmp.previous
285
- return ret
286
-
287
- def find(self, predicate: Callable[[Error], bool]) -> Optional[Error]:
288
- if predicate(self):
289
- return self
290
- if self.previous is not None:
291
- return self.previous.find(predicate)
292
- return None
293
-
294
- def to_string(self, interested: Callable[[Any], bool])->str:
295
- lst = self.to_list(interested)
296
- root, leaf = lst[0], lst[-1]
297
- root_fields = ',\n '.join([f"{key}: {value}" for key, value in root.items() if value is not None])
298
- leaf_fields = ',\n '.join([f"{key}: {value}" for key, value in leaf.items() if value is not None])
299
-
300
- return f"{self.__class__.__name__}: ROOT\n"\
301
- f" {root_fields}\n"\
302
- f"\u25cf \u25cf \u25cf LEAF\n"\
303
- f" {leaf_fields}\n"
304
227
 
305
-
306
- @cached_property
307
- def algebras(self) -> List[Any]:
308
- return [self.this] + (self.previous.algebras if self.previous is not None else [])
309
-
310
228
  @dataclass(frozen=True)
311
229
  class Algebra(ABC, Generic[A, S]):
312
230
  ######################################################## shared among all subclasses ########################################################
syncraft/ast.py CHANGED
@@ -3,12 +3,11 @@
3
3
  from __future__ import annotations
4
4
  import re
5
5
  from typing import (
6
- Optional, Any, TypeVar, Tuple, runtime_checkable, Dict,
6
+ Optional, Any, TypeVar, Tuple, runtime_checkable,
7
7
  Protocol, Generic, Callable, Union, cast
8
8
  )
9
9
  from syncraft.algebra import (
10
- OrResult,ThenResult, ManyResult, ThenKind,NamedResult, StructuralResult,
11
- Lens
10
+ OrResult,ThenResult, ManyResult, ThenKind,NamedResult, StructuralResult
12
11
  )
13
12
  from dataclasses import dataclass, replace, is_dataclass, asdict
14
13
  from enum import Enum
@@ -67,13 +66,17 @@ class AST(Generic[T]):
67
66
  pruned: bool = False
68
67
  parent: Optional[AST[T]] = None
69
68
 
70
-
71
- def bimap(self, ctx: Any) -> Tuple[Any, Callable[[Any], AST[T]]]:
72
- value, backward = self.focus.bimap(ctx) if isinstance(self.focus, StructuralResult) else (self.focus, lambda x: x)
73
- def back2ast(data: Any) -> AST[T]:
74
- return replace(self, focus=backward(data)) # type: ignore
75
- return value, back2ast
76
-
69
+ def bimap(self)->Tuple[Any, Callable[[Any], AST[T]]]:
70
+ if isinstance(self.focus, StructuralResult):
71
+ b = self.focus.bimap()
72
+ s, v = b.forward(None, self.focus)
73
+ def inverse(data: Any) -> AST[T]:
74
+ s1, v1 = b.inverse(None, data)
75
+ return replace(self, focus=v1)
76
+ return v, inverse
77
+ else:
78
+ return self.focus, lambda x: replace(self, focus=x)
79
+
77
80
  def wrapper(self)-> Callable[[Any], Any]:
78
81
  if isinstance(self.focus, NamedResult):
79
82
  focus = cast(NamedResult[Any], self.focus)
syncraft/diagnostic.py CHANGED
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
  from rich import print
3
3
  from rich.table import Table as RichTable
4
4
  from typing import Tuple, Any, Set
5
- from syncraft.dsl import DSL
5
+ from syncraft.syntax import Syntax
6
6
  from syncraft.algebra import Left, Right, Error, Either, Algebra
7
7
 
8
8
  from syncraft.parser import ParserState, Token
@@ -44,7 +44,7 @@ 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, (Error, ParserState, DSL)):
47
+ elif isinstance(value, (Error, ParserState, Syntax)):
48
48
  return prefix + (value._string or 'N/A')
49
49
  else:
50
50
  return prefix + str(value)
syncraft/generator.py CHANGED
@@ -2,15 +2,17 @@ from __future__ import annotations
2
2
 
3
3
  from typing import (
4
4
  Any, TypeVar, Tuple, Optional, Callable, Generic, Union,
5
- List
5
+ List, Generator as YieldGen
6
6
  )
7
7
  from functools import cached_property
8
8
  from dataclasses import dataclass, replace
9
9
  from syncraft.algebra import (
10
- Algebra, ThenResult, Either, Left, Right, Error, Insptectable,
11
- OrResult, ManyResult
10
+ Algebra, ThenResult, Either, Left, Right, Error,
11
+ OrResult, ManyResult, NamedResult
12
12
  )
13
+
13
14
  from syncraft.ast import TokenProtocol, ParseResult, AST, Token, TokenSpec
15
+ from syncraft.syntax import Syntax
14
16
  from sqlglot import TokenType
15
17
  import re
16
18
  import rstr
@@ -29,7 +31,7 @@ GenResult = Union[
29
31
  ]
30
32
 
31
33
  @dataclass(frozen=True)
32
- class GenState(Generic[T], Insptectable):
34
+ class GenState(Generic[T]):
33
35
  ast: Optional[AST[T]]
34
36
  seed: int
35
37
 
@@ -270,7 +272,8 @@ class Generator(Algebra[GenResult[T], GenState[T]]):
270
272
 
271
273
 
272
274
 
273
- def generate(gen: Algebra[Any, Any], data: Optional[AST[Any]] = None, seed: int = 0) -> AST[Any] | Any:
275
+ def generate(syntax: Syntax[Any, Any], data: Optional[AST[Any]] = None, seed: int = 0) -> AST[Any] | Any:
276
+ gen = syntax(Generator)
274
277
  state = GenState.from_ast(data, seed)
275
278
  result = gen.run(state, use_cache=False)
276
279
  if isinstance(result, Right):
@@ -278,3 +281,28 @@ def generate(gen: Algebra[Any, Any], data: Optional[AST[Any]] = None, seed: int
278
281
  assert isinstance(result, Left), "Generator must return Either[Any, Tuple[Any, Any]]"
279
282
  return result.value
280
283
 
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
+
syncraft/parser.py CHANGED
@@ -6,12 +6,12 @@ from typing import (
6
6
  Generic, Callable
7
7
  )
8
8
  from syncraft.algebra import (
9
- Either, Left, Right, Error, Insptectable, Algebra
9
+ Either, Left, Right, Error, Algebra
10
10
  )
11
11
  from dataclasses import dataclass, field, replace
12
12
  from enum import Enum
13
13
  from functools import reduce
14
- from syncraft.dsl import DSL
14
+ from syncraft.syntax import Syntax
15
15
 
16
16
  from syncraft.ast import Token, TokenSpec, AST, T
17
17
 
@@ -24,7 +24,7 @@ from syncraft.ast import Token, TokenSpec, AST, T
24
24
 
25
25
 
26
26
  @dataclass(frozen=True)
27
- class ParserState(Generic[T], Insptectable):
27
+ class ParserState(Generic[T]):
28
28
  input: Tuple[T, ...] = field(default_factory=tuple)
29
29
  index: int = 0
30
30
 
@@ -40,13 +40,6 @@ class ParserState(Generic[T], Insptectable):
40
40
  def after(self, length: Optional[int] = 5)->str:
41
41
  length = min(length, len(self.input) - self.index) if length is not None else len(self.input) - self.index
42
42
  return " ".join(token.text for token in self.input[self.index:self.index + length])
43
-
44
- def to_string(self, interested: Callable[[Any], bool])->str:
45
- return f"ParserState(\n"\
46
- f"index={self.index}, \n"\
47
- f"input({len(self.input)})=[{self.token_sample_string()}, ...]), \n"\
48
- f"before=({self.before()}), \n"\
49
- f"after=({self.after()})"
50
43
 
51
44
 
52
45
  def current(self)->T:
@@ -187,15 +180,16 @@ class Parser(Algebra[T, ParserState[T]]):
187
180
  return Right((tuple(tokens), tmp_state))
188
181
  return cls(until_run, name=cls.__name__ + '.until')
189
182
 
190
- def sqlglot(parser: DSL[Any, Any],
191
- dialect: str) -> DSL[List[exp.Expression], ParserState[Any]]:
183
+ def sqlglot(parser: Syntax[Any, Any],
184
+ dialect: str) -> Syntax[List[exp.Expression], ParserState[Any]]:
192
185
  gp = GlotParser(dialect=dialect)
193
186
  return parser.map(lambda tokens: [e for e in gp.parse(raw_tokens=tokens) if e is not None])
194
187
 
195
188
 
196
- def parse(parser: Algebra[Any, ParserState[Token]],
189
+ def parse(syntax: Syntax[Any, Any],
197
190
  sql: str,
198
191
  dialect: str) -> AST[Any] | Any:
192
+ parser = syntax(Parser)
199
193
  input: ParserState[Token] = token_state(sql, dialect=dialect)
200
194
  result = parser.run(input, True)
201
195
  if isinstance(result, Right):
@@ -212,34 +206,34 @@ def token(token_type: Optional[Enum] = None,
212
206
  text: Optional[str] = None,
213
207
  case_sensitive: bool = False,
214
208
  regex: Optional[re.Pattern[str]] = None
215
- ) -> DSL[Any, Any]:
209
+ ) -> Syntax[Any, Any]:
216
210
  token_type_txt = token_type.name if token_type is not None else None
217
211
  token_value_txt = text if text is not None else None
218
212
  msg = 'token(' + ','.join([x for x in [token_type_txt, token_value_txt, str(regex)] if x is not None]) + ')'
219
- return DSL(
213
+ return Syntax(
220
214
  lambda cls: cls.factory('token', token_type=token_type, text=text, case_sensitive=case_sensitive, regex=regex)
221
215
  ).describe(name=msg, fixity='prefix')
222
216
 
223
217
 
224
- def identifier(value: str | None = None) -> DSL[Any, Any]:
218
+ def identifier(value: str | None = None) -> Syntax[Any, Any]:
225
219
  if value is None:
226
220
  return token(TokenType.IDENTIFIER)
227
221
  else:
228
222
  return token(TokenType.IDENTIFIER, text=value)
229
223
 
230
- def variable(value: str | None = None) -> DSL[Any, Any]:
224
+ def variable(value: str | None = None) -> Syntax[Any, Any]:
231
225
  if value is None:
232
226
  return token(TokenType.VAR)
233
227
  else:
234
228
  return token(TokenType.VAR, text=value)
235
229
 
236
- def literal(lit: str) -> DSL[Any, Any]:
230
+ def literal(lit: str) -> Syntax[Any, Any]:
237
231
  return token(token_type=None, text=lit, case_sensitive=True)
238
232
 
239
- def regex(regex: re.Pattern[str]) -> DSL[Any, Any]:
233
+ def regex(regex: re.Pattern[str]) -> Syntax[Any, Any]:
240
234
  return token(token_type=None, regex=regex, case_sensitive=True)
241
235
 
242
- def lift(value: Any)-> DSL[Any, Any]:
236
+ def lift(value: Any)-> Syntax[Any, Any]:
243
237
  if isinstance(value, str):
244
238
  return literal(value)
245
239
  elif isinstance(value, re.Pattern):
@@ -247,22 +241,22 @@ def lift(value: Any)-> DSL[Any, Any]:
247
241
  elif isinstance(value, Enum):
248
242
  return token(value)
249
243
  else:
250
- return DSL(lambda cls: cls.success(value))
244
+ return Syntax(lambda cls: cls.success(value))
251
245
 
252
- def number() -> DSL[Any, Any]:
246
+ def number() -> Syntax[Any, Any]:
253
247
  return token(TokenType.NUMBER)
254
248
 
255
249
 
256
- def string() -> DSL[Any, Any]:
250
+ def string() -> Syntax[Any, Any]:
257
251
  return token(TokenType.STRING)
258
252
 
259
253
 
260
254
 
261
- def until(*open_close: Tuple[DSL[Tuple[T, ...] | T, ParserState[T]], DSL[Tuple[T, ...] | T, ParserState[T]]],
262
- terminator: Optional[DSL[Tuple[T, ...] | T, ParserState[T]]] = None,
255
+ def until(*open_close: Tuple[Syntax[Tuple[T, ...] | T, ParserState[T]], Syntax[Tuple[T, ...] | T, ParserState[T]]],
256
+ terminator: Optional[Syntax[Tuple[T, ...] | T, ParserState[T]]] = None,
263
257
  inclusive: bool = True,
264
- strict: bool = True) -> DSL[Any, Any]:
265
- return DSL(
258
+ strict: bool = True) -> Syntax[Any, Any]:
259
+ return Syntax(
266
260
  lambda cls: cls.factory('until',
267
261
  *[(left.alg(cls), right.alg(cls)) for left, right in open_close],
268
262
  terminator=terminator.alg(cls) if terminator else None,
syncraft/sqlite3.py CHANGED
@@ -4,7 +4,7 @@ https://www.sqlite.org/syntaxdiagrams.html
4
4
  """
5
5
  from __future__ import annotations
6
6
  from typing import Any
7
- from syncraft.dsl import DSL, lazy, choice
7
+ from syncraft.syntax import Syntax, lazy, choice
8
8
  import syncraft.parser as dsl
9
9
  from syncraft.diagnostic import rich_error, rich_debug, rich_parser
10
10
  from sqlglot import TokenType
@@ -607,16 +607,16 @@ update_stmt_limited = (
607
607
  )
608
608
 
609
609
 
610
- def table_or_subquery()->DSL[Any, Any]:
610
+ def table_or_subquery()->Syntax[Any, Any]:
611
611
  t1 = ~schema_name >> table_as_alias >> ~((INDEXED >> BY >> index_name)|(NOT >> INDEXED))
612
612
  t2 = ~schema_name >> table_function_name >> expr.parens(COMMA, L_PAREN, R_PAREN) >> ~(~AS >> var)
613
613
  t3 = select_stmt.between(L_PAREN, R_PAREN) >> ~(~AS >> var)
614
614
  t4 = table_subquery.parens(COMMA, L_PAREN, R_PAREN)
615
615
  t5 = join_clause.between(L_PAREN, R_PAREN)
616
- return (t1 | t2 | t3 | t4 | t5).as_(DSL[Any, Any])
616
+ return (t1 | t2 | t3 | t4 | t5).as_(Syntax[Any, Any])
617
617
 
618
618
 
619
- def expression() -> DSL[Any, Any]:
619
+ def expression() -> Syntax[Any, Any]:
620
620
  return choice(
621
621
  literal_value,
622
622
  bind_parameter,
@@ -639,9 +639,9 @@ def expression() -> DSL[Any, Any]:
639
639
  expr >> ~NOT >> IN >> ~schema_name >> (table_name | (function_name >> expr.parens(COMMA, L_PAREN, R_PAREN))),
640
640
  ~NOT >> ~EXISTS >> select_stmt.between(L_PAREN, R_PAREN),
641
641
  CASE >> ~expr >> (WHEN >> expr >> THEN >> expr).many() >> ~(ELSE >> expr) // END,
642
- ).as_(DSL[Any, Any])
642
+ ).as_(Syntax[Any, Any])
643
643
 
644
- def select_statement() -> DSL[Any, Any]:
644
+ def select_statement() -> Syntax[Any, Any]:
645
645
  select_clause = SELECT >> ~(DISTINCT | ALL) >> result_columns.sep_by(COMMA)
646
646
  from_clause = FROM >> (table_subquery.sep_by(COMMA) | join_clause)
647
647
  where_clause = WHERE >> expr
@@ -657,7 +657,7 @@ def select_statement() -> DSL[Any, Any]:
657
657
  >> select_core.sep_by(compound_operator)
658
658
  >> ~(ordering_clause >> ~limit_clause)
659
659
  >> ~SEMICOLON
660
- ).as_(DSL[Any, Any])
660
+ ).as_(Syntax[Any, Any])
661
661
 
662
662
  column_constraint = ~(CONSTRAINT >> constraint_name) >> (
663
663
  (PRIMARY >> KEY >> ~(ASC | DESC) >> ~conflict_clause >> AUTO_INCREMENT)
@@ -6,7 +6,7 @@ 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, Insptectable, ThenResult, ManyResult, ThenKind, NamedResult
9
+ from syncraft.algebra import Algebra, Error, Either, ThenResult, ManyResult, ThenKind, NamedResult
10
10
  from types import MethodType, FunctionType
11
11
 
12
12
 
@@ -18,7 +18,7 @@ C = TypeVar('C') # Result type for else branch
18
18
  S = TypeVar('S') # State type
19
19
 
20
20
  @dataclass(frozen=True)
21
- class Description(Insptectable):
21
+ class Description:
22
22
  name: Optional[str] = None
23
23
  newline: Optional[str] = None
24
24
  fixity: Literal['infix', 'prefix', 'postfix'] = 'infix'
@@ -67,11 +67,11 @@ class Description(Insptectable):
67
67
 
68
68
 
69
69
  @dataclass(frozen=True)
70
- class DSL(Generic[A, S], Insptectable):
70
+ class Syntax(Generic[A, S]):
71
71
  alg: Callable[[Type[Algebra[Any, Any]]], Algebra[A, S]]
72
72
  meta: Description = field(default_factory=Description, repr=False)
73
73
 
74
- def algebra(self, name: str | MethodType | FunctionType, *args: Any, **kwargs: Any)-> DSL[A, S]:
74
+ def algebra(self, name: str | MethodType | FunctionType, *args: Any, **kwargs: Any)-> Syntax[A, S]:
75
75
  def algebra_run(cls: Type[Algebra[Any, S]]) -> Algebra[Any, S]:
76
76
  a = self.alg(cls)
77
77
  if isinstance(name, str):
@@ -115,33 +115,33 @@ class DSL(Generic[A, S], Insptectable):
115
115
  newline: Optional[str] = None,
116
116
  name: Optional[str] = None,
117
117
  fixity: Optional[Literal['infix', 'prefix', 'postfix']] = None,
118
- parameter: Optional[List[DSL[Any, S]]] = None) -> DSL[A, S]:
118
+ parameter: Optional[List[Syntax[Any, S]]] = None) -> Syntax[A, S]:
119
119
  return self.__class__(alg=self.alg,
120
120
  meta=self.meta.update(name=name,
121
121
  newline=newline,
122
122
  fixity=fixity,
123
123
  parameter=parameter))
124
124
 
125
- def newline(self, info: str='')-> DSL[A, S]:
125
+ def newline(self, info: str='')-> Syntax[A, S]:
126
126
  return self.describe(newline=info)
127
127
 
128
- def terminal(self, name: str)->DSL[A, S]:
128
+ def terminal(self, name: str)->Syntax[A, S]:
129
129
  return self.describe(name=name, fixity='prefix', parameter=[])
130
130
 
131
131
  ######################################################## value transformation ########################################################
132
- def map(self, f: Callable[[A], B]) -> DSL[B, S]:
132
+ def map(self, f: Callable[[A], B]) -> Syntax[B, S]:
133
133
  return self.__class__(lambda cls: self.alg(cls).map(f), meta = self.meta) # type: ignore
134
134
 
135
- def map_error(self, f: Callable[[Optional[Any]], Any]) -> DSL[A, S]:
135
+ def map_error(self, f: Callable[[Optional[Any]], Any]) -> Syntax[A, S]:
136
136
  return self.__class__(lambda cls: self.alg(cls).map_error(f), meta=self.meta)
137
137
 
138
- def map_state(self, f: Callable[[S], S]) -> DSL[A, S]:
138
+ def map_state(self, f: Callable[[S], S]) -> Syntax[A, S]:
139
139
  return self.__class__(lambda cls: self.alg(cls).map_state(f), meta=self.meta)
140
140
 
141
- def flat_map(self, f: Callable[[A], Algebra[B, S]]) -> DSL[B, S]:
141
+ def flat_map(self, f: Callable[[A], Algebra[B, S]]) -> Syntax[B, S]:
142
142
  return self.__class__(lambda cls: self.alg(cls).flat_map(f)) # type: ignore
143
143
 
144
- def many(self, *, at_least: int = 1, at_most: Optional[int] = None) -> DSL[ManyResult[A], S]:
144
+ def many(self, *, at_least: int = 1, at_most: Optional[int] = None) -> Syntax[ManyResult[A], S]:
145
145
  return self.__class__(lambda cls:self.alg(cls).many(at_least=at_least, at_most=at_most)).describe(name='*', # type: ignore
146
146
  fixity='prefix',
147
147
  parameter=[self])
@@ -150,83 +150,83 @@ class DSL(Generic[A, S], Insptectable):
150
150
 
151
151
 
152
152
 
153
- def between(self, left: DSL[Any, S], right: DSL[Any, S]) -> DSL[ThenResult[None, ThenResult[A, None]], S]:
153
+ def between(self, left: Syntax[Any, S], right: Syntax[Any, S]) -> Syntax[ThenResult[None, ThenResult[A, None]], S]:
154
154
  return left >> self // right
155
155
 
156
- def sep_by(self, sep: DSL[Any, S]) -> DSL[ThenResult[A, ManyResult[ThenResult[None, A]]], S]:
156
+ def sep_by(self, sep: Syntax[Any, S]) -> Syntax[ThenResult[A, ManyResult[ThenResult[None, A]]], S]:
157
157
  return (self + (sep >> self).many()).describe(
158
158
  name='sep_by',
159
159
  fixity='prefix',
160
160
  parameter=[self, sep]
161
161
  )
162
162
 
163
- def parens(self, sep: DSL[Any, S], open: DSL[Any, S], close: DSL[Any, S]) -> DSL[Any, S]:
163
+ def parens(self, sep: Syntax[Any, S], open: Syntax[Any, S], close: Syntax[Any, S]) -> Syntax[Any, S]:
164
164
  return self.sep_by(sep=sep).between(left=open, right=close)
165
165
 
166
- def optional(self, default: Optional[B] = None) -> DSL[Optional[A | B], S]:
166
+ def optional(self, default: Optional[B] = None) -> Syntax[Optional[A | B], S]:
167
167
  return (self | success(default)).describe(name='~', fixity='prefix', parameter=[self])
168
168
 
169
169
 
170
- def cut(self) -> DSL[A, S]:
170
+ def cut(self) -> Syntax[A, S]:
171
171
  return self.__class__(lambda cls:self.alg(cls).cut())
172
172
 
173
173
 
174
174
  ####################################################### operator overloading #############################################
175
- def __ge__(self, f: Callable[[A], Algebra[B, S]]) -> DSL[B, S]:
175
+ def __ge__(self, f: Callable[[A], Algebra[B, S]]) -> Syntax[B, S]:
176
176
  return self.flat_map(f).describe(name='>=', fixity='infix', parameter=[self])
177
177
 
178
178
 
179
- def __gt__(self, other: Callable[[A], B])->DSL[B, S]:
179
+ def __gt__(self, other: Callable[[A], B])->Syntax[B, S]:
180
180
  return self.map(other)
181
181
 
182
182
 
183
- def __floordiv__(self, other: DSL[B, S]) -> DSL[ThenResult[A, None], S]:
184
- other = other if isinstance(other, DSL) else self.lift(other).as_(DSL[B, S])
183
+ def __floordiv__(self, other: Syntax[B, S]) -> Syntax[ThenResult[A, None], S]:
184
+ other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
185
185
  return self.__class__(
186
186
  lambda cls: self.alg(cls).then_left(other.alg(cls)) # type: ignore
187
- ).describe(name=ThenKind.LEFT.value, fixity='infix', parameter=[self, other]).as_(DSL[ThenResult[A, None], S])
187
+ ).describe(name=ThenKind.LEFT.value, fixity='infix', parameter=[self, other]).as_(Syntax[ThenResult[A, None], S])
188
188
 
189
- def __rfloordiv__(self, other: DSL[B, S]) -> DSL[ThenResult[B, None], S]:
190
- other = other if isinstance(other, DSL) else self.lift(other).as_(DSL[B, S])
189
+ def __rfloordiv__(self, other: Syntax[B, S]) -> Syntax[ThenResult[B, None], S]:
190
+ other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
191
191
  return other.__floordiv__(self)
192
192
 
193
- def __invert__(self) -> DSL[A | None, S]:
193
+ def __invert__(self) -> Syntax[A | None, S]:
194
194
  return self.optional()
195
195
 
196
- def __radd__(self, other: DSL[B, S]) -> DSL[ThenResult[B, A], S]:
197
- other = other if isinstance(other, DSL) else self.lift(other).as_(DSL[B, S])
196
+ def __radd__(self, other: Syntax[B, S]) -> Syntax[ThenResult[B, A], S]:
197
+ other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
198
198
  return other.__add__(self)
199
199
 
200
- def __add__(self, other: DSL[B, S]) -> DSL[ThenResult[A, B], S]:
201
- other = other if isinstance(other, DSL) else self.lift(other).as_(DSL[B, S])
200
+ def __add__(self, other: Syntax[B, S]) -> Syntax[ThenResult[A, B], S]:
201
+ other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
202
202
  return self.__class__(
203
203
  lambda cls: self.alg(cls).then_both(other.alg(cls)) # type: ignore
204
204
  ).describe(name=ThenKind.BOTH.value, fixity='infix', parameter=[self, other])
205
205
 
206
- def __rshift__(self, other: DSL[B, S]) -> DSL[ThenResult[None, B], S]:
207
- other = other if isinstance(other, DSL) else self.lift(other).as_(DSL[B, S])
206
+ def __rshift__(self, other: Syntax[B, S]) -> Syntax[ThenResult[None, B], S]:
207
+ other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
208
208
  return self.__class__(
209
209
  lambda cls: self.alg(cls).then_right(other.alg(cls)) # type: ignore
210
- ).describe(name=ThenKind.RIGHT.value, fixity='infix', parameter=[self, other]).as_(DSL[ThenResult[None, B], S])
210
+ ).describe(name=ThenKind.RIGHT.value, fixity='infix', parameter=[self, other]).as_(Syntax[ThenResult[None, B], S])
211
211
 
212
212
 
213
- def __rrshift__(self, other: DSL[B, S]) -> DSL[ThenResult[None, A], S]:
214
- other = other if isinstance(other, DSL) else self.lift(other).as_(DSL[B, S])
213
+ def __rrshift__(self, other: Syntax[B, S]) -> Syntax[ThenResult[None, A], S]:
214
+ other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
215
215
  return other.__rshift__(self)
216
216
 
217
217
 
218
- def __or__(self, other: DSL[B, S]) -> DSL[A | B, S]:
219
- other = other if isinstance(other, DSL) else self.lift(other).as_(DSL[B, S])
218
+ def __or__(self, other: Syntax[B, S]) -> Syntax[A | B, S]:
219
+ other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
220
220
  return self.__class__(lambda cls: self.alg(cls).or_else(other.alg(cls))).describe(name='|', fixity='infix', parameter=[self, other]) # type: ignore
221
221
 
222
222
 
223
- def __ror__(self, other: DSL[B, S]) -> DSL[A | B, S]:
224
- other = other if isinstance(other, DSL) else self.lift(other).as_(DSL[B, S])
225
- return other.__or__(self).as_(DSL[A | B, S])
223
+ def __ror__(self, other: Syntax[B, S]) -> Syntax[A | B, S]:
224
+ other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
225
+ return other.__or__(self).as_(Syntax[A | B, S])
226
226
 
227
227
 
228
228
  ######################################################################## data processing combinators #########################################################
229
- def bind(self, name: str) -> DSL[NamedResult[A], S]:
229
+ def bind(self, name: str) -> Syntax[NamedResult[A], S]:
230
230
  def bind_f(value: A) -> NamedResult[A]:
231
231
  if isinstance(value, NamedResult):
232
232
  return replace(value, name=name)
@@ -235,7 +235,7 @@ class DSL(Generic[A, S], Insptectable):
235
235
  return self.map(bind_f).describe(name=f'bind("{name}")', fixity='postfix', parameter=[self])
236
236
 
237
237
 
238
- def dump_error(self, formatter: Optional[Callable[[Error], None]] = None) -> DSL[A, S]:
238
+ def dump_error(self, formatter: Optional[Callable[[Error], None]] = None) -> Syntax[A, S]:
239
239
  def dump_error_run(err: Any)->Any:
240
240
  if isinstance(err, Error) and formatter is not None:
241
241
  formatter(err)
@@ -245,21 +245,21 @@ class DSL(Generic[A, S], Insptectable):
245
245
 
246
246
  def debug(self,
247
247
  label: str,
248
- formatter: Optional[Callable[[Algebra[Any, S], S, Either[Any, Tuple[Any, S]]], None]] = None) -> DSL[A, S]:
248
+ formatter: Optional[Callable[[Algebra[Any, S], S, Either[Any, Tuple[Any, S]]], None]] = None) -> Syntax[A, S]:
249
249
  return self.__class__(lambda cls:self.alg(cls).debug(label, formatter), meta=self.meta)
250
250
 
251
251
 
252
252
 
253
- def lazy(thunk: Callable[[], DSL[A, S]]) -> DSL[A, S]:
254
- return DSL(lambda cls: cls.lazy(lambda: thunk()(cls))).describe(name='lazy(?)', fixity='postfix')
253
+ def lazy(thunk: Callable[[], Syntax[A, S]]) -> Syntax[A, S]:
254
+ return Syntax(lambda cls: cls.lazy(lambda: thunk()(cls))).describe(name='lazy(?)', fixity='postfix')
255
255
 
256
- def fail(error: Any) -> DSL[Any, Any]:
257
- return DSL(lambda alg: alg.fail(error)).describe(name=f'fail({error})', fixity='prefix')
256
+ def fail(error: Any) -> Syntax[Any, Any]:
257
+ return Syntax(lambda alg: alg.fail(error)).describe(name=f'fail({error})', fixity='prefix')
258
258
 
259
- def success(value: Any) -> DSL[Any, Any]:
260
- return DSL(lambda alg: alg.success(value)).describe(name=f'success({value})', fixity='prefix')
259
+ def success(value: Any) -> Syntax[Any, Any]:
260
+ return Syntax(lambda alg: alg.success(value)).describe(name=f'success({value})', fixity='prefix')
261
261
 
262
- def choice(*parsers: DSL[Any, S]) -> DSL[Any, S]:
262
+ def choice(*parsers: Syntax[Any, S]) -> Syntax[Any, S]:
263
263
  return reduce(lambda a, b: a | b, parsers) if len(parsers) > 0 else success(None)
264
264
 
265
265
 
@@ -269,27 +269,27 @@ def choice(*parsers: DSL[Any, S]) -> DSL[Any, S]:
269
269
 
270
270
 
271
271
 
272
- def all(*parsers: DSL[Any, S]) -> DSL[ThenResult[Any, Any], S]:
272
+ def all(*parsers: Syntax[Any, S]) -> Syntax[ThenResult[Any, Any], S]:
273
273
  return reduce(lambda a, b: a + b, parsers) if len(parsers) > 0 else success(None)
274
274
 
275
- def first(*parsers: DSL[Any, S]) -> DSL[Any, S]:
275
+ def first(*parsers: Syntax[Any, S]) -> Syntax[Any, S]:
276
276
  return reduce(lambda a, b: a // b, parsers) if len(parsers) > 0 else success(None)
277
277
 
278
- def last(*parsers: DSL[Any, S]) -> DSL[Any, S]:
278
+ def last(*parsers: Syntax[Any, S]) -> Syntax[Any, S]:
279
279
  return reduce(lambda a, b: a >> b, parsers) if len(parsers) > 0 else success(None)
280
280
 
281
- def named(* parsers: DSL[Any, S] | Tuple[str, DSL[Any, S]]) -> DSL[Any, S]:
281
+ def named(* parsers: Syntax[Any, S] | Tuple[str, Syntax[Any, S]]) -> Syntax[Any, S]:
282
282
  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], DSL)
283
+ return isinstance(x, tuple) and len(x) == 2 and isinstance(x[0], str) and isinstance(x[1], Syntax)
284
284
 
285
- def to_parser(x: DSL[Any, S] | Tuple[str, DSL[Any, S]])->DSL[Any, S]:
286
- if isinstance(x, tuple) and len(x) == 2 and isinstance(x[0], str) and isinstance(x[1], DSL):
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
287
  return x[1].bind(x[0])
288
- elif isinstance(x, DSL):
288
+ elif isinstance(x, Syntax):
289
289
  return x
290
290
  else:
291
291
  raise ValueError(f"Invalid parser or tuple: {x}", x)
292
- ret: Optional[DSL[Any, S]] = None
292
+ ret: Optional[Syntax[Any, S]] = None
293
293
  has_data = False
294
294
  for p in parsers:
295
295
  just_parser = to_parser(p)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: syncraft
3
- Version: 0.1.18
3
+ Version: 0.1.20
4
4
  Summary: Parser combinator library
5
5
  Author-email: Michael Afmokt <michael@esacca.com>
6
6
  License-Expression: MIT
@@ -31,6 +31,7 @@ pip install syncraft
31
31
 
32
32
 
33
33
  ## TODO
34
- - [ ] Test Walker.get/set for bidirectional mapping between source code and data class
35
- - [ ] Annotate sqlite3 grammar with named nodes and data classes
36
- - [ ]
34
+ - [ ] Add a collect method to AST to collect all named entries and pack them into a dict or a custom dataclass. This method will be called as the last step of my current bimap. So it shares the signature of bimap and can combine with the current bimap
35
+ - [ ] Amend all, first, last, and named helper functions to support bimap and named results.
36
+ - [ ] 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.
37
+ - [ ] Make the library as fast as possible and feasible.
@@ -0,0 +1,15 @@
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,,
@@ -1,15 +0,0 @@
1
- syncraft/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- syncraft/algebra.py,sha256=l_ptzSkXbRGSLzu46GXixcNQPdXAi1s5R9nRDI86JkY,22785
3
- syncraft/ast.py,sha256=9CuHqXRiw_7UMDLd4x2def3kHgTVQ0hnPxyIbBlx1Zc,4640
4
- syncraft/cmd.py,sha256=DzXgU8QLVOg0YTFKpmOyzbf02LKPphQ4EeKSLzqpJ_s,2012
5
- syncraft/diagnostic.py,sha256=_kLbl1LlU4Y2PtsLLZFA_OLqeZqq1f2rjT_LRXLnDaI,2813
6
- syncraft/dsl.py,sha256=AxAZKu6MBfaG7Wzss3UISbQsrFaigc1L-hS3p_CAXWQ,13620
7
- syncraft/generator.py,sha256=FpKfRPYvA0Wu-eKy7GUj_Xahz3e58sD0fZ3bXoTVIzY,11432
8
- syncraft/parser.py,sha256=c1cCCW0RNw2y4XlNIUkrMf7q5lkm8wes4v5xbjLAUGg,11573
9
- syncraft/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- syncraft/sqlite3.py,sha256=WR4wFf2CraRkb7C5rph3wsu6sGbG_7EjEZDUsPH8xqU,34677
11
- syncraft-0.1.18.dist-info/licenses/LICENSE,sha256=wHSV424U5csa3339dy1AZbsz2xsd0hrkMx2QK48CcUk,1062
12
- syncraft-0.1.18.dist-info/METADATA,sha256=Ga4qLGYK64owCQOyGXgQocU-e4FXtK2jNv5RQT3eDS0,946
13
- syncraft-0.1.18.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
14
- syncraft-0.1.18.dist-info/top_level.txt,sha256=Kq3t8ESXB2xW1Xt3uPmkENFc-c4f2pamNmaURBk7zc8,9
15
- syncraft-0.1.18.dist-info/RECORD,,