syncraft 0.1.13__tar.gz → 0.1.14__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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: syncraft
3
- Version: 0.1.13
3
+ Version: 0.1.14
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.13"
3
+ version = "0.1.14"
4
4
  description = "Parser combinator library"
5
5
  license = "MIT"
6
6
  license-files = ["LICENSE"]
@@ -127,7 +127,13 @@ class NamedResult(Generic[A], StructuralResult):
127
127
  value: A
128
128
  def bimap(self, ctx: Any)->Tuple[NamedResult[Any], Callable[[NamedResult[Any]], StructuralResult]]:
129
129
  value, backward = self.value.bimap(ctx) if isinstance(self.value, StructuralResult) else (self.value, lambda x: x)
130
- return NamedResult(self.name, value), lambda data: NamedResult(self.name, backward(data))
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
131
137
 
132
138
  @dataclass(eq=True, frozen=True)
133
139
  class ManyResult(Generic[A], StructuralResult):
@@ -165,9 +171,12 @@ class ThenResult(Generic[A, B], StructuralResult):
165
171
  def bimap(self, ctx: Any) -> Tuple[Any, Callable[[Any], StructuralResult]]:
166
172
  def branch(b: Any) -> Tuple[Any, Callable[[Any], StructuralResult]]:
167
173
  if isinstance(b, ThenResult):
168
- value, backward = b.bimap(ctx)
169
- x, y = ThenResult.flat((value, backward))
170
- return x, lambda data: ThenResult(self.kind, y(data), self.right)
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
171
180
  elif isinstance(b, StructuralResult):
172
181
  return b.bimap(ctx)
173
182
  else:
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
  import re
5
5
  from typing import (
6
6
  Optional, Any, TypeVar, Tuple, runtime_checkable, Dict,
7
- Protocol, Generic, Callable, Union
7
+ Protocol, Generic, Callable, Union, cast
8
8
  )
9
9
  from syncraft.algebra import (
10
10
  OrResult,ThenResult, ManyResult, ThenKind,NamedResult, StructuralResult,
@@ -65,26 +65,32 @@ class AST(Generic[T]):
65
65
  pruned: bool = False
66
66
  parent: Optional[AST[T]] = None
67
67
 
68
+
68
69
  def bimap(self, ctx: Any) -> Tuple[Any, Callable[[Any], AST[T]]]:
69
70
  value, backward = self.focus.bimap(ctx) if isinstance(self.focus, StructuralResult) else (self.focus, lambda x: x)
70
71
  def back2ast(data: Any) -> AST[T]:
71
72
  return replace(self, focus=backward(data)) # type: ignore
72
73
  return value, back2ast
73
74
 
74
- def up(self)->Optional[AST[T]]:
75
- return self.parent
75
+ def wrapper(self)-> Callable[[Any], Any]:
76
+ if isinstance(self.focus, NamedResult):
77
+ focus = cast(NamedResult[Any], self.focus)
78
+ return lambda x: NamedResult(name = focus.name, value = x)
79
+ else:
80
+ return lambda x: x
81
+
82
+ def is_named(self) -> bool:
83
+ return isinstance(self.focus, NamedResult)
76
84
 
77
85
  def left(self) -> Optional[AST[T]]:
78
- focus = self.focus.value if isinstance(self.focus, NamedResult) else self.focus
79
- match focus:
86
+ match self.focus:
80
87
  case ThenResult(left=left, kind=kind):
81
88
  return replace(self, focus=left, parent=self, pruned = self.pruned or kind == ThenKind.RIGHT)
82
89
  case _:
83
90
  raise TypeError(f"Invalid focus type({self.focus}) for left traversal")
84
91
 
85
92
  def right(self) -> Optional[AST[T]]:
86
- focus = self.focus.value if isinstance(self.focus, NamedResult) else self.focus
87
- match focus:
93
+ match self.focus:
88
94
  case ThenResult(right=right, kind=kind):
89
95
  return replace(self, focus=right, parent=self, pruned = self.pruned or kind == ThenKind.LEFT)
90
96
  case _:
@@ -92,8 +98,7 @@ class AST(Generic[T]):
92
98
 
93
99
 
94
100
  def down(self, index: int) -> Optional[AST[T]]:
95
- focus = self.focus.value if isinstance(self.focus, NamedResult) else self.focus
96
- match focus:
101
+ match self.focus:
97
102
  case ManyResult(value=children):
98
103
  if 0 <= index < len(children):
99
104
  return replace(self, focus=children[index], parent=self, pruned=self.pruned)
@@ -104,6 +109,8 @@ class AST(Generic[T]):
104
109
  return replace(self, focus=value, parent=self, pruned=self.pruned)
105
110
  else:
106
111
  raise IndexError(f"Index {index} out of bounds for OrResult")
112
+ case NamedResult(value=value):
113
+ return replace(self, focus=value, parent=self, pruned=self.pruned)
107
114
  case _:
108
115
  raise TypeError(f"Invalid focus type({self.focus}) for down traversal")
109
116
 
@@ -4,9 +4,9 @@ from typing import (
4
4
  Optional, List, Any, TypeVar, Generic, Callable, Tuple, cast,
5
5
  Type, Literal
6
6
  )
7
- from dataclasses import dataclass, field
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
9
+ from syncraft.algebra import Algebra, Error, Either, Insptectable, ThenResult, ManyResult, ThenKind, NamedResult
10
10
  from types import MethodType, FunctionType
11
11
 
12
12
 
@@ -226,75 +226,14 @@ class DSL(Generic[A, S], Insptectable):
226
226
 
227
227
 
228
228
  ######################################################################## data processing combinators #########################################################
229
-
230
- def _attach(self,
231
- name: str,
232
- *,
233
- forward_map: Callable[[B], C] | None,
234
- backward_map: Callable[[C], B] | None,
235
- aggregator_f: Callable[..., Any] | None,
236
- ) -> DSL[NamedResult[A, C], S]:
237
- def attach_f(x: A | NamedResult[A, B]) -> NamedResult[A, C]:
238
- if isinstance(x, NamedResult):
239
- if x.backward_map is not None:
240
- b_f = x.backward_map
241
- def combined_bf(a: Any)->A:
242
- if backward_map is not None:
243
- return b_f(backward_map(a))
244
- else:
245
- return b_f(a)
246
- if x.forward_map is not None:
247
- f_f = x.forward_map
248
- def combined_ff(a: Any)->Any:
249
- if forward_map is not None:
250
- return forward_map(f_f(a))
251
- else:
252
- return f_f(a)
253
- if x.aggregator is not None:
254
- agg_f = x.aggregator
255
- def combined_agg(a: Any)->Any:
256
- if aggregator_f is not None:
257
- return aggregator_f(agg_f(a))
258
- else:
259
- return agg_f(a)
260
- return NamedResult(
261
- name=name,
262
- value=x.value,
263
- forward_map=forward_map if x.forward_map is None else combined_ff, # type: ignore
264
- backward_map=backward_map if x.backward_map is None else combined_bf, # type: ignore
265
- aggregator=aggregator_f if x.aggregator is None else combined_agg
266
- )
229
+ def bind(self, name: str) -> DSL[NamedResult[A], S]:
230
+ def bind_f(value: A) -> NamedResult[A]:
231
+ if isinstance(value, NamedResult):
232
+ return replace(value, name=name)
267
233
  else:
268
- return NamedResult(
269
- name=name,
270
- value=x,
271
- forward_map=forward_map, # type: ignore
272
- backward_map=backward_map, # type: ignore
273
- aggregator=aggregator_f,
274
-
275
- )
276
-
277
- return self.map(attach_f)
278
-
279
- def bind(self, name: str) -> DSL[NamedResult[Any, Any], S]:
280
- return self._attach(name,
281
- forward_map=None,
282
- backward_map=None,
283
- aggregator_f=None,
284
- ).describe(name=f'bind("{name}")', fixity='postfix', parameter=[self])
285
-
286
- def bimap(self, name: str, *, forward_map: Callable[[Any], Any], backward_map: Callable[[Any], Any]) -> DSL[NamedResult[Any, Any], S]:
287
- return self._attach(name,
288
- forward_map=forward_map,
289
- backward_map=backward_map,
290
- aggregator_f=None,
291
- ).describe(name=f'bimap("{name}")', fixity='postfix', parameter=[self])
292
- def to(self, name: str, aggregator_f: Callable[..., Any]) -> DSL[NamedResult[A, Any], S]:
293
- return self._attach(name,
294
- forward_map=None,
295
- backward_map=None,
296
- aggregator_f=aggregator_f,
297
- ).describe(name=f'to("{name}")', fixity='postfix', parameter=[self])
234
+ return NamedResult(name=name, value=value)
235
+ return self.map(bind_f).describe(name=f'bind("{name}")', fixity='postfix', parameter=[self])
236
+
298
237
 
299
238
  def dump_error(self, formatter: Optional[Callable[[Error], None]] = None) -> DSL[A, S]:
300
239
  def dump_error_run(err: Any)->Any:
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from typing import (
4
- Any, TypeVar, Tuple, Optional, Callable, Generic, Union, Iterable,
4
+ Any, TypeVar, Tuple, Optional, Callable, Generic, Union,
5
5
  List
6
6
  )
7
7
  from functools import cached_property
@@ -24,7 +24,7 @@ GenResult = Union[
24
24
  ThenResult['GenResult[T]', 'GenResult[T]'],
25
25
  ManyResult['GenResult[T]'],
26
26
  OrResult['GenResult[T]'],
27
- Iterable[T],
27
+
28
28
  T
29
29
  ]
30
30
 
@@ -52,7 +52,16 @@ class GenState(Generic[T], Insptectable):
52
52
  if self.ast is None:
53
53
  return None
54
54
  return self.ast.focus
55
+
56
+ @property
57
+ def is_named(self)->bool:
58
+ return self.ast is not None and self.ast.is_named()
55
59
 
60
+ def wrapper(self)->Callable[[Any], Any]:
61
+ if self.ast is not None:
62
+ return self.ast.wrapper()
63
+ else:
64
+ return lambda x: x
56
65
 
57
66
  def left(self)-> GenState[T]:
58
67
  if self.ast is None:
@@ -64,10 +73,7 @@ class GenState(Generic[T], Insptectable):
64
73
  return self
65
74
  return replace(self, ast=self.ast.right())
66
75
 
67
- def up(self)->GenState[T]:
68
- if self.ast is None:
69
- return self
70
- return replace(self, ast=self.ast.up())
76
+
71
77
 
72
78
  def down(self, index: int) -> GenState[T]:
73
79
  if self.ast is None:
@@ -142,13 +148,19 @@ class TokenGen(TokenSpec):
142
148
  class Generator(Algebra[GenResult[T], GenState[T]]):
143
149
  def flat_map(self, f: Callable[[GenResult[T]], Algebra[B, GenState[T]]]) -> Algebra[B, GenState[T]]:
144
150
  def flat_map_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[B, GenState[T]]]:
145
- lft = input.left()
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()
146
154
  match self.run(lft, use_cache=use_cache):
147
155
  case Left(error):
148
156
  return Left(error)
149
157
  case Right((value, next_input)):
150
- r = input.right()
151
- return f(value).run(r, use_cache)
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))
152
164
  raise ValueError("flat_map should always return a value or an error.")
153
165
  return Generator(run_f = flat_map_run, name=self.name) # type: ignore
154
166
 
@@ -158,6 +170,8 @@ class Generator(Algebra[GenResult[T], GenState[T]]):
158
170
  assert at_least > 0, "at_least must be greater than 0"
159
171
  assert at_most is None or at_least <= at_most, "at_least must be less than or equal to at_most"
160
172
  def many_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[ManyResult[GenResult[T]], GenState[T]]]:
173
+ wrapper = input.wrapper()
174
+ 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
161
175
  if input.pruned:
162
176
  upper = at_most if at_most is not None else at_least + 2
163
177
  count = input.rng("many").randint(at_least, upper)
@@ -169,7 +183,7 @@ class Generator(Algebra[GenResult[T], GenState[T]]):
169
183
  ret.append(value)
170
184
  case Left(_):
171
185
  pass
172
- return Right((ManyResult(tuple(ret)), input))
186
+ return Right((wrapper(ManyResult(tuple(ret))), input))
173
187
  else:
174
188
  ret = []
175
189
  for index in range(input.how_many):
@@ -190,7 +204,7 @@ class Generator(Algebra[GenResult[T], GenState[T]]):
190
204
  this=self,
191
205
  state=input.down(index)
192
206
  ))
193
- return Right((ManyResult(tuple(ret)), input))
207
+ return Right((wrapper(ManyResult(tuple(ret))), input))
194
208
  return self.__class__(many_run, name=f"many({self.name})") # type: ignore
195
209
 
196
210
 
@@ -198,21 +212,23 @@ class Generator(Algebra[GenResult[T], GenState[T]]):
198
212
  other: Algebra[GenResult[T], GenState[T]]
199
213
  ) -> Algebra[OrResult[GenResult[T]], GenState[T]]:
200
214
  def or_else_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[OrResult[GenResult[T]], GenState[T]]]:
215
+ wrapper = input.wrapper()
216
+ 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
201
217
  if input.pruned:
202
218
  forked_input = input.fork(tag="or_else")
203
219
  match forked_input.rng("or_else").choice((self, other)).run(forked_input, use_cache):
204
220
  case Right((value, next_input)):
205
- return Right((OrResult(value), next_input))
221
+ return Right((wrapper(OrResult(value)), next_input))
206
222
  case Left(error):
207
223
  return Left(error)
208
224
  else:
209
225
  match self.run(input.down(0), use_cache):
210
226
  case Right((value, next_input)):
211
- return Right((OrResult(value), next_input))
227
+ return Right((wrapper(OrResult(value)), next_input))
212
228
  case Left(error):
213
229
  match other.run(input.down(0), use_cache):
214
230
  case Right((value, next_input)):
215
- return Right((OrResult(value), next_input))
231
+ return Right((wrapper(OrResult(value)), next_input))
216
232
  case Left(error):
217
233
  return Left(error)
218
234
  raise ValueError("or_else should always return a value or an error.")
@@ -228,6 +244,8 @@ class Generator(Algebra[GenResult[T], GenState[T]]):
228
244
  gen = TokenGen(token_type=token_type, text=text, case_sensitive=case_sensitive, regex=regex)
229
245
  lazy_self: Algebra[GenResult[T], GenState[T]]
230
246
  def token_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[GenResult[Token], GenState[T]]]:
247
+ wrapper = input.wrapper()
248
+ 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
231
249
  if input.pruned:
232
250
  return Right((gen.gen(), input))
233
251
  else:
@@ -236,7 +254,7 @@ class Generator(Algebra[GenResult[T], GenState[T]]):
236
254
  return Left(Error(None,
237
255
  message=f"Expected a Token, but got {type(current)}.",
238
256
  state=input))
239
- return Right((current, input))
257
+ return Right((wrapper(current), input))
240
258
  lazy_self = cls(token_run, name=cls.__name__ + f'.token({token_type or text or regex})') # type: ignore
241
259
  return lazy_self
242
260
 
@@ -77,23 +77,23 @@ class ParserState(Generic[T], Insptectable):
77
77
 
78
78
 
79
79
  @dataclass(frozen=True)
80
- class Parser(Algebra[Tuple[T,...] | T, ParserState[T]]):
80
+ class Parser(Algebra[T, ParserState[T]]):
81
81
  @classmethod
82
82
  def token(cls,
83
83
  token_type: Optional[Enum] = None,
84
84
  text: Optional[str] = None,
85
85
  case_sensitive: bool = False,
86
86
  regex: Optional[re.Pattern[str]] = None
87
- )-> Algebra[Tuple[T,...] | T, ParserState[T]]:
87
+ )-> Algebra[T, ParserState[T]]:
88
88
  spec = TokenSpec(token_type=token_type, text=text, case_sensitive=case_sensitive, regex=regex)
89
- def token_run(state: ParserState[T], use_cache:bool) -> Either[Any, Tuple[Tuple[T,...] | T, ParserState[T]]]:
89
+ def token_run(state: ParserState[T], use_cache:bool) -> Either[Any, Tuple[T, ParserState[T]]]:
90
90
  if state.ended():
91
91
  return Left(state)
92
92
  token = state.current()
93
93
  if token is None or not spec.is_valid(token):
94
94
  return Left(state)
95
95
  return Right((Token(token_type = token.token_type, text=token.text), state.advance())) # type: ignore
96
- captured: Algebra[Tuple[T,...] | T, ParserState[T]] = cls(token_run, name=cls.__name__ + f'.token({token_type}, {text})')
96
+ captured: Algebra[T, ParserState[T]] = cls(token_run, name=cls.__name__ + f'.token({token_type}, {text})')
97
97
  def error_fn(err: Any) -> Error:
98
98
  if isinstance(err, ParserState):
99
99
  return Error(message=f"Cannot match token at {err}", this=captured, state=err)
@@ -280,7 +280,7 @@ HAVING = dsl.lift(TokenType.HAVING)
280
280
  HINT = dsl.lift(TokenType.HINT)
281
281
  IGNORE = dsl.lift(TokenType.IGNORE)
282
282
  ILIKE = dsl.lift(TokenType.ILIKE)
283
- ILIKE_ANY = dsl.lift(TokenType.ILIKE_ANY)
283
+
284
284
  IN = dsl.lift(TokenType.IN)
285
285
  INDEX = dsl.lift(TokenType.INDEX)
286
286
  INNER = dsl.lift(TokenType.INNER)
@@ -301,7 +301,7 @@ LANGUAGE = dsl.lift(TokenType.LANGUAGE)
301
301
  LATERAL = dsl.lift(TokenType.LATERAL)
302
302
  LEFT = dsl.lift(TokenType.LEFT)
303
303
  LIKE = dsl.lift(TokenType.LIKE)
304
- LIKE_ANY = dsl.lift(TokenType.LIKE_ANY)
304
+
305
305
  LIMIT = dsl.lift(TokenType.LIMIT)
306
306
  LIST = dsl.lift(TokenType.LIST)
307
307
  LOAD = dsl.lift(TokenType.LOAD)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: syncraft
3
- Version: 0.1.13
3
+ Version: 0.1.14
4
4
  Summary: Parser combinator library
5
5
  Author-email: Michael Afmokt <michael@esacca.com>
6
6
  License-Expression: MIT
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes