syncraft 0.2.2__py3-none-any.whl → 0.2.4__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/syntax.py CHANGED
@@ -56,9 +56,7 @@ class Description:
56
56
  left = self.parameter[0].to_string(interested) if interested(self.parameter[0]) else '...'
57
57
  right = self.parameter[1].to_string(interested) if interested(self.parameter[1]) else '...'
58
58
  if self.parameter[1].meta.newline is not None:
59
- dot = '\u25cf'
60
- rarrow = '\u2794'
61
- new = '\u2570' #'\u2936'
59
+ new = '\u2570' # '\u2936'
62
60
  return f"{left}\n{new} \"{self.parameter[1].meta.newline}\" {self.name} {right}"
63
61
  return f"{left} {self.name} {right}"
64
62
  elif self.fixity == 'prefix':
@@ -80,10 +78,26 @@ class Description:
80
78
 
81
79
  @dataclass(frozen=True)
82
80
  class Syntax(Generic[A, S]):
81
+ """
82
+ The core signature of Syntax is take an Algebra Class and return an Algebra Instance.
83
+ """
83
84
  alg: Callable[[Type[Algebra[Any, Any]]], Algebra[A, S]]
84
85
  meta: Description = field(default_factory=Description, repr=False)
85
86
 
86
- def algebra(self, name: str | MethodType | FunctionType, *args: Any, **kwargs: Any)-> Syntax[A, S]:
87
+ def algebra(self, name: str | MethodType | FunctionType, *args: Any, **kwargs: Any) -> Syntax[A, S]:
88
+ """Calling method of underlying algebra.
89
+
90
+ Allows calling Algebra instance methods (e.g., cut) by name, if the method
91
+ is not exposed by Syntax.
92
+
93
+ Args:
94
+ name: Method name (string), bound method, or function to invoke.
95
+ *args: Positional arguments passed to the method.
96
+ **kwargs: Keyword arguments passed to the method.
97
+
98
+ Returns:
99
+ A new Syntax reflecting the transformed algebra.
100
+ """
87
101
  def algebra_run(cls: Type[Algebra[Any, S]]) -> Algebra[Any, S]:
88
102
  a = self.alg(cls)
89
103
  if isinstance(name, str):
@@ -91,10 +105,8 @@ class Syntax(Generic[A, S]):
91
105
  if attr is None:
92
106
  return a
93
107
  if isinstance(attr, (staticmethod, classmethod)):
94
- # These are descriptors: unwrap then call
95
108
  attr = attr.__get__(None, cls)
96
109
  elif isinstance(attr, FunctionType):
97
- # Unbound function (e.g., static method not wrapped)
98
110
  attr = MethodType(attr, a)
99
111
  else:
100
112
  return a
@@ -108,142 +120,316 @@ class Syntax(Generic[A, S]):
108
120
  else:
109
121
  return a
110
122
  return self.__class__(alg=algebra_run, meta=self.meta)
111
-
112
123
 
113
- def as_(self, typ: Type[B])->B:
114
- return cast(typ, self) # type: ignore
115
-
124
+ def as_(self, typ: Type[B]) -> B:
125
+ return cast(typ, self) # type: ignore
126
+
116
127
  def __call__(self, alg: Type[Algebra[Any, Any]]) -> Algebra[A, S]:
117
128
  return self.alg(alg)
118
-
129
+
119
130
  def to_string(self, interested: Callable[[Any], bool]) -> Optional[str]:
120
131
  return self.meta.to_string(interested)
121
132
 
122
-
123
- def describe(self,
124
- *,
125
- newline: Optional[str] = None,
126
- name: Optional[str] = None,
127
- fixity: Optional[Literal['infix', 'prefix', 'postfix']] = None,
128
- parameter: Optional[Tuple[Syntax[Any, S], ...]] = None) -> Syntax[A, S]:
129
- return self.__class__(alg=self.alg,
130
- meta=self.meta.update(name=name,
131
- newline=newline,
132
- fixity=fixity,
133
- parameter=parameter))
134
-
135
- def newline(self, info: str='')-> Syntax[A, S]:
136
- return self.describe(newline=info)
133
+ def describe(
134
+ self,
135
+ *,
136
+ newline: Optional[str] = None,
137
+ name: Optional[str] = None,
138
+ fixity: Optional[Literal['infix', 'prefix', 'postfix']] = None,
139
+ parameter: Optional[Tuple[Syntax[Any, S], ...]] = None,
140
+ ) -> Syntax[A, S]:
141
+ return self.__class__(
142
+ alg=self.alg,
143
+ meta=self.meta.update(
144
+ name=name, newline=newline, fixity=fixity, parameter=parameter
145
+ ),
146
+ )
137
147
 
138
- def terminal(self, name: str)->Syntax[A, S]:
139
- return self.describe(name=name, fixity='prefix')
148
+ def newline(self, info: str = '') -> Syntax[A, S]:
149
+ return self.describe(newline=info)
140
150
 
141
- ######################################################## value transformation ########################################################
151
+ ######################################################## value transformation ########################################################
142
152
  def map(self, f: Callable[[A], B]) -> Syntax[B, S]:
143
- return self.__class__(lambda cls: self.alg(cls).map(f), meta = self.meta) # type: ignore
144
-
153
+ """Map the produced value while preserving state and metadata.
154
+
155
+ Args:
156
+ f: Function mapping value A to B.
157
+
158
+ Returns:
159
+ Syntax yielding B with the same resulting state.
160
+ """
161
+ return self.__class__(lambda cls: self.alg(cls).map(f), meta=self.meta) # type: ignore
162
+
145
163
  def bimap(self, f: Callable[[A], B], i: Callable[[B], A]) -> Syntax[B, S]:
146
- return self.__class__(lambda cls: self.alg(cls).bimap(f, i), meta=self.meta) # type: ignore
164
+ """Bidirectionally map values with an inverse, keeping round-trip info.
165
+
166
+ Applies f to the value and adjusts internal state via inverse i so
167
+ generation/parsing stay in sync.
168
+
169
+ Args:
170
+ f: Forward mapping A -> B.
171
+ i: Inverse mapping B -> A applied to the state.
172
+
173
+ Returns:
174
+ Syntax yielding B with state alignment preserved.
175
+ """
176
+ return self.__class__(lambda cls: self.alg(cls).bimap(f, i), meta=self.meta) # type: ignore
147
177
 
148
178
  def map_all(self, f: Callable[[A, S], Tuple[B, S]]) -> Syntax[B, S]:
149
- return self.__class__(lambda cls: self.alg(cls).map_all(f), meta=self.meta) # type: ignore
150
-
179
+ """Map both value and state on success.
180
+
181
+ Args:
182
+ f: Function mapping (value, state) to (new_value, new_state).
183
+
184
+ Returns:
185
+ Syntax yielding transformed value and state.
186
+ """
187
+ return self.__class__(lambda cls: self.alg(cls).map_all(f), meta=self.meta) # type: ignore
188
+
151
189
  def map_error(self, f: Callable[[Optional[Any]], Any]) -> Syntax[A, S]:
190
+ """Transform the error payload when this syntax fails.
191
+
192
+ Args:
193
+ f: Function applied to the error payload of Left.
194
+
195
+ Returns:
196
+ Syntax that preserves successes and maps failures.
197
+ """
152
198
  return self.__class__(lambda cls: self.alg(cls).map_error(f), meta=self.meta)
153
-
154
- def pre_state(self, f: Callable[[S], S]) -> Syntax[A, S]:
155
- return self.__class__(lambda cls: self.alg(cls).pre_state(f), meta=self.meta)
156
-
157
- def post_state(self, f: Callable[[S], S]) -> Syntax[A, S]:
158
- return self.__class__(lambda cls: self.alg(cls).post_state(f), meta=self.meta)
199
+
200
+ def map_state(self, f: Callable[[S], S]) -> Syntax[A, S]:
201
+ """Map the input state before running this syntax.
202
+
203
+ Args:
204
+ f: S -> S function applied to the state prior to running.
205
+
206
+ Returns:
207
+ Syntax that runs with f(state).
208
+ """
209
+ return self.__class__(lambda cls: self.alg(cls).map_state(f), meta=self.meta)
159
210
 
160
211
  def flat_map(self, f: Callable[[A], Algebra[B, S]]) -> Syntax[B, S]:
161
- return self.__class__(lambda cls: self.alg(cls).flat_map(f)) # type: ignore
212
+ """Chain computations where the next step depends on the value.
213
+
214
+ Args:
215
+ f: Function mapping value to the next algebra to run.
216
+
217
+ Returns:
218
+ Syntax yielding the result of the chained computation.
219
+ """
220
+ return self.__class__(lambda cls: self.alg(cls).flat_map(f)) # type: ignore
162
221
 
163
222
  def many(self, *, at_least: int = 1, at_most: Optional[int] = None) -> Syntax[Many[A], S]:
164
- return self.__class__(lambda cls:self.alg(cls).many(at_least=at_least, at_most=at_most)).describe(name='*', # type: ignore
165
- fixity='prefix',
166
- parameter=(self,))
167
-
168
- ################################################ facility combinators ############################################################
223
+ """Repeat this syntax and collect results into Many.
224
+
225
+ Repeats greedily until failure or no progress. Enforces bounds.
169
226
 
227
+ Args:
228
+ at_least: Minimum number of matches (default 1).
229
+ at_most: Optional maximum number of matches.
230
+
231
+ Returns:
232
+ Syntax producing Many of values.
233
+ """
234
+ return self.__class__(
235
+ lambda cls: self.alg(cls).many(at_least=at_least, at_most=at_most) # type: ignore
236
+ ).describe(
237
+ name='*', fixity='prefix', parameter=(self,)
238
+ ) # type: ignore
239
+
240
+ ############################################################### facility combinators ############################################################
170
241
  def between(self, left: Syntax[B, S], right: Syntax[C, S]) -> Syntax[Then[B, Then[A, C]], S]:
242
+ """Parse left, then this syntax, then right; keep all.
243
+
244
+ Equivalent to left >> self // right.
245
+
246
+ Args:
247
+ left: Opening syntax.
248
+ right: Closing syntax.
249
+
250
+ Returns:
251
+ Syntax producing nested Then with all parts.
252
+ """
171
253
  return left >> self // right
172
254
 
173
- def sep_by(self,
174
- sep: Syntax[B, S]) -> Syntax[Then[A, Choice[Many[Then[B, A]], Optional[Nothing]]], S]:
175
- ret: Syntax[Then[A, Choice[Many[Then[B, A]], Optional[Nothing]]], S] = (self + (sep >> self).many().optional())
255
+ def sep_by(self, sep: Syntax[B, S]) -> Syntax[Then[A, Choice[Many[Then[B, A]], Optional[Nothing]]], S]:
256
+ """Parse one or more items separated by sep.
257
+
258
+ Returns a structure where the first item is separated from the rest,
259
+ which are collected in a Many of Then pairs.
260
+
261
+ Args:
262
+ sep: Separator syntax between items.
263
+
264
+ Returns:
265
+ Syntax describing a non-empty, separator-delimited list.
266
+ """
267
+ ret: Syntax[Then[A, Choice[Many[Then[B, A]], Optional[Nothing]]], S] = (
268
+ self + (sep >> self).many().optional()
269
+ )
270
+
176
271
  def f(a: Then[A, Choice[Many[Then[B, A]], Optional[Nothing]]]) -> Many[A]:
177
272
  match a:
178
- case Then(kind=ThenKind.BOTH, left=left, right=Choice(kind=ChoiceKind.RIGHT, value=Nothing())):
179
- return Many(value = (left,))
180
- case Then(kind=ThenKind.BOTH, left=left, right=Choice(kind=ChoiceKind.LEFT, value=Many(value=bs))):
181
- return Many(value = (left,) + tuple([b.right for b in bs]))
273
+ case Then(
274
+ kind=ThenKind.BOTH,
275
+ left=left,
276
+ right=Choice(kind=ChoiceKind.RIGHT, value=Nothing()),
277
+ ):
278
+ return Many(value=(left,))
279
+ case Then(
280
+ kind=ThenKind.BOTH,
281
+ left=left,
282
+ right=Choice(kind=ChoiceKind.LEFT, value=Many(value=bs)),
283
+ ):
284
+ return Many(value=(left,) + tuple([b.right for b in bs]))
182
285
  case _:
183
286
  raise ValueError(f"Bad data shape {a}")
184
-
185
- def i(a: Many[A]) -> Then[A, Choice[Many[Then[B|None, A]], Optional[Nothing]]]:
287
+
288
+ def i(a: Many[A]) -> Then[A, Choice[Many[Then[B | None, A]], Optional[Nothing]]]:
186
289
  if not isinstance(a, Many) or len(a.value) < 1:
187
- raise ValueError(f"sep_by inverse expect Many with at least one element, got {a}")
290
+ raise ValueError(
291
+ f"sep_by inverse expect Many with at least one element, got {a}"
292
+ )
188
293
  if len(a.value) == 1:
189
- return Then(kind=ThenKind.BOTH, left=a.value[0], right=Choice(kind=ChoiceKind.RIGHT, value=Nothing()))
294
+ return Then(
295
+ kind=ThenKind.BOTH,
296
+ left=a.value[0],
297
+ right=Choice(kind=ChoiceKind.RIGHT, value=Nothing()),
298
+ )
190
299
  else:
191
- v: List[Then[B|None, A]] = [Then(kind=ThenKind.RIGHT, right=x, left=None) for x in a.value[1:]]
192
- return Then(kind= ThenKind.BOTH,
193
- left=a.value[0],
194
- right=Choice(kind=ChoiceKind.LEFT,
195
- value=Many(value=tuple(v))))
196
- ret = ret.bimap(f,i) # type: ignore
197
- return ret.describe(
198
- name='sep_by',
199
- fixity='prefix',
200
- parameter=(self, sep))
201
-
202
- def parens(self,
203
- sep: Syntax[C, S],
204
- open: Syntax[B, S],
205
- close: Syntax[D, S]) -> Syntax[Then[B, Then[Then[A, Choice[Many[Then[C, A]], Optional[Nothing]]], D]], S]:
300
+ v: List[Then[B | None, A]] = [
301
+ Then(kind=ThenKind.RIGHT, right=x, left=None) for x in a.value[1:]
302
+ ]
303
+ return Then(
304
+ kind=ThenKind.BOTH,
305
+ left=a.value[0],
306
+ right=Choice(kind=ChoiceKind.LEFT, value=Many(value=tuple(v))),
307
+ )
308
+
309
+ ret = ret.bimap(f, i) # type: ignore
310
+ return ret.describe(name='sep_by', fixity='prefix', parameter=(self, sep))
311
+
312
+ def parens(
313
+ self,
314
+ sep: Syntax[C, S],
315
+ open: Syntax[B, S],
316
+ close: Syntax[D, S],
317
+ ) -> Syntax[Then[B, Then[Then[A, Choice[Many[Then[C, A]], Optional[Nothing]]], D]], S]:
318
+ """Parse a parenthesized, separator-delimited list.
319
+
320
+ Shorthand for self.sep_by(sep).between(open, close).
321
+
322
+ Args:
323
+ sep: Separator between elements.
324
+ open: Opening delimiter.
325
+ close: Closing delimiter.
326
+
327
+ Returns:
328
+ Syntax producing all three parts with the list nested inside.
329
+ """
206
330
  return self.sep_by(sep=sep).between(left=open, right=close)
207
-
331
+
208
332
  def optional(self) -> Syntax[Choice[A, Optional[Nothing]], S]:
209
- return (self | success(Nothing())).describe(name='~', fixity='prefix', parameter=(self,))
333
+ """Make this syntax optional.
334
+
335
+ Returns a Choice of the value or Nothing when absent.
210
336
 
337
+ Returns:
338
+ Syntax producing Choice of value or Nothing.
339
+ """
340
+ return (self | success(Nothing())).describe(
341
+ name='~', fixity='prefix', parameter=(self,)
342
+ )
211
343
 
212
344
  def cut(self) -> Syntax[A, S]:
213
- return self.__class__(lambda cls:self.alg(cls).cut())
345
+ """Commit this branch: on failure, prevent trying alternatives.
214
346
 
347
+ Wraps the underlying algebra's cut.
215
348
 
216
- ####################################################### operator overloading #############################################
349
+ Returns:
350
+ Syntax that marks downstream failures as committed.
351
+ """
352
+ return self.__class__(lambda cls: self.alg(cls).cut())
353
+
354
+ ###################################################### operator overloading #############################################
217
355
  def __floordiv__(self, other: Syntax[B, S]) -> Syntax[Then[A, B], S]:
356
+ """Then-left: run both and prefer the left in the result kind.
357
+
358
+ Returns Then(kind=LEFT) with both left and right values.
359
+
360
+ Args:
361
+ other: Syntax to run after this one.
362
+
363
+ Returns:
364
+ Syntax producing Then(left, right, kind=LEFT).
365
+ """
218
366
  other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
219
- ret: Syntax[Then[A, B], S] = self.__class__(lambda cls: self.alg(cls).then_left(other.alg(cls))) # type: ignore
220
- return ret.describe(name=ThenKind.LEFT.value, fixity='infix', parameter=(self, other)).as_(Syntax[Then[A, B], S])
221
-
367
+ ret: Syntax[Then[A, B], S] = self.__class__(
368
+ lambda cls: self.alg(cls).then_left(other.alg(cls)) # type: ignore
369
+ ) # type: ignore
370
+ return ret.describe(name=ThenKind.LEFT.value, fixity='infix', parameter=(self, other)).as_(Syntax[Then[A, B], S])
371
+
222
372
  def __rfloordiv__(self, other: Syntax[B, S]) -> Syntax[Then[B, A], S]:
223
373
  other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
224
374
  return other.__floordiv__(self)
225
-
375
+
226
376
  def __add__(self, other: Syntax[B, S]) -> Syntax[Then[A, B], S]:
377
+ """Then-both: run both and keep both values.
378
+
379
+ Returns Then(kind=BOTH).
380
+
381
+ Args:
382
+ other: Syntax to run after this one.
383
+
384
+ Returns:
385
+ Syntax producing Then(left, right, kind=BOTH).
386
+ """
227
387
  other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
228
- ret: Syntax[Then[A, B], S] = self.__class__(lambda cls: self.alg(cls).then_both(other.alg(cls))) # type: ignore
229
- return ret.describe(name=ThenKind.BOTH.value, fixity='infix', parameter=(self, other)).as_(Syntax[Then[A, B], S])
388
+ ret: Syntax[Then[A, B], S] = self.__class__(
389
+ lambda cls: self.alg(cls).then_both(other.alg(cls)) # type: ignore
390
+ ) # type: ignore
391
+ return ret.describe(name=ThenKind.BOTH.value, fixity='infix', parameter=(self, other)).as_(Syntax[Then[A, B], S])
230
392
 
231
393
  def __radd__(self, other: Syntax[B, S]) -> Syntax[Then[B, A], S]:
232
394
  other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
233
395
  return other.__add__(self)
234
396
 
235
397
  def __rshift__(self, other: Syntax[B, S]) -> Syntax[Then[A, B], S]:
398
+ """Then-right: run both and prefer the right in the result kind.
399
+
400
+ Returns Then(kind=RIGHT).
401
+
402
+ Args:
403
+ other: Syntax to run after this one.
404
+
405
+ Returns:
406
+ Syntax producing Then(left, right, kind=RIGHT).
407
+ """
236
408
  other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
237
- ret: Syntax[Then[A, B], S] = self.__class__(lambda cls: self.alg(cls).then_right(other.alg(cls))) # type: ignore
238
- return ret.describe(name=ThenKind.RIGHT.value, fixity='infix', parameter=(self, other)).as_(Syntax[Then[A, B], S])
409
+ ret: Syntax[Then[A, B], S] = self.__class__(
410
+ lambda cls: self.alg(cls).then_right(other.alg(cls)) # type: ignore
411
+ ) # type: ignore
412
+ return ret.describe(name=ThenKind.RIGHT.value, fixity='infix', parameter=(self, other)).as_(Syntax[Then[A, B], S])
239
413
 
240
414
  def __rrshift__(self, other: Syntax[B, S]) -> Syntax[Then[B, A], S]:
241
415
  other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
242
- return other.__rshift__(self)
243
-
416
+ return other.__rshift__(self)
417
+
244
418
  def __or__(self, other: Syntax[B, S]) -> Syntax[Choice[A, B], S]:
419
+ """Alternative: try this syntax; if it fails uncommitted, try the other.
420
+
421
+ Returns a Choice indicating which branch succeeded.
422
+
423
+ Args:
424
+ other: Alternative syntax to try on failure.
425
+
426
+ Returns:
427
+ Syntax producing Choice.LEFT or Choice.RIGHT.
428
+ """
245
429
  other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
246
- ret: Syntax[Choice[A, B], S] = self.__class__(lambda cls: self.alg(cls).or_else(other.alg(cls))) # type: ignore
430
+ ret: Syntax[Choice[A, B], S] = self.__class__(
431
+ lambda cls: self.alg(cls).or_else(other.alg(cls)) # type: ignore
432
+ ) # type: ignore
247
433
  return ret.describe(name='|', fixity='infix', parameter=(self, other))
248
434
 
249
435
  def __ror__(self, other: Syntax[B, S]) -> Syntax[Choice[B, A], S]:
@@ -251,59 +437,101 @@ class Syntax(Generic[A, S]):
251
437
  return other.__or__(self)
252
438
 
253
439
  def __invert__(self) -> Syntax[Choice[A, Optional[Nothing]], S]:
440
+ """Syntactic sugar for optional() (tilde operator)."""
254
441
  return self.optional()
255
442
 
256
-
257
- ######################################################################## data processing combinators #########################################################
443
+ ######################################################################## data processing combinators #########################################################
258
444
  def bind(self, name: Optional[str] = None) -> Syntax[A, S]:
445
+ """Bind the produced value to the name.
446
+
447
+ If name is None and the value is Marked, the name of Marked is used.
448
+ If name is None and the value if Collect, the name of the collector is used.
449
+
450
+ Args:
451
+ name: Optional binding name; must be a valid identifier if provided.
452
+
453
+ Returns:
454
+ Syntax that writes the value into the state's binding table.
455
+ """
259
456
  if name:
260
457
  assert valid_name(name), f"Invalid mark name: {name}"
261
- def bind_v(v: Any, s: S)->Tuple[Any, S]:
458
+
459
+ def bind_v(v: Any, s: S) -> Tuple[Any, S]:
262
460
  if name:
263
- return v, s.bind(name, v)
461
+ return v, s.bind(name, v)
264
462
  elif isinstance(v, Marked):
265
463
  return v.value, s.bind(v.name, v.value)
464
+ elif isinstance(v, Collect) and isinstance(v.collector, type):
465
+ return v.value, s.bind(v.collector.__name__, v.value)
266
466
  else:
267
467
  return v, s
468
+
268
469
  return self.map_all(bind_v).describe(name=f'bind({name})', fixity='postfix', parameter=(self,))
269
470
 
270
- def to(self, f: Collector[E])-> Syntax[Collect[A, E], S]:
471
+ def to(self, f: Collector[E]) -> Syntax[Collect[A, E], S]:
472
+ """Attach a collector to the produced value.
473
+ A collector can be a dataclass, and the Marked nodes will be
474
+ mapped to the fields of the dataclass.
475
+
476
+ Wraps the value in Collect or updates an existing one.
477
+
478
+ Args:
479
+ f: Collector invoked during generation/printing.
480
+
481
+ Returns:
482
+ Syntax producing Collect(value, collector=f).
483
+ """
271
484
  def to_f(v: A) -> Collect[A, E]:
272
485
  if isinstance(v, Collect):
273
486
  return replace(v, collector=f)
274
487
  else:
275
488
  return Collect(collector=f, value=v)
489
+
276
490
  def ito_f(c: Collect[A, E]) -> A:
277
491
  return c.value if isinstance(c, Collect) else c
278
- return self.bimap(to_f, ito_f).describe(name=f'to({f})', fixity='postfix', parameter=(self,))
279
492
 
493
+ return self.bimap(to_f, ito_f).describe(name=f'to({f})', fixity='postfix', parameter=(self,))
280
494
 
281
495
  def mark(self, name: str) -> Syntax[Marked[A], S]:
496
+ """Mark the produced value with a name.
497
+
498
+ Useful for later bind operations.
499
+
500
+ Args:
501
+ name: Identifier to attach to the value.
502
+
503
+ Returns:
504
+ Syntax producing Marked(name, value).
505
+ """
282
506
  assert valid_name(name), f"Invalid mark name: {name}"
507
+
283
508
  def mark_s(value: A) -> Marked[A]:
284
509
  if isinstance(value, Marked):
285
- return replace(value, name=name)
510
+ return replace(value, name=name)
286
511
  else:
287
512
  return Marked(name=name, value=value)
288
- def imark_s(m : Marked[A]) -> A:
289
- return m.value if isinstance(m, Marked) else m
290
-
291
- return self.bimap(mark_s, imark_s).describe(name=f'mark("{name}")', fixity='postfix', parameter=(self,))
292
513
 
514
+ def imark_s(m: Marked[A]) -> A:
515
+ return m.value if isinstance(m, Marked) else m
293
516
 
517
+ return self.bimap(mark_s, imark_s).describe(name=f'mark("{name}")', fixity='postfix', parameter=(self,))
294
518
 
295
519
  def dump_error(self, formatter: Optional[Callable[[Error], None]] = None) -> Syntax[A, S]:
296
- def dump_error_run(err: Any)->Any:
520
+ def dump_error_run(err: Any) -> Any:
297
521
  if isinstance(err, Error) and formatter is not None:
298
- formatter(err)
522
+ formatter(err)
299
523
  return err
300
- return self.__class__(lambda cls: self.alg(cls).map_error(dump_error_run))
301
524
 
525
+ return self.__class__(lambda cls: self.alg(cls).map_error(dump_error_run))
302
526
 
303
- def debug(self,
304
- label: str,
305
- formatter: Optional[Callable[[Algebra[Any, S], S, Either[Any, Tuple[Any, S]]], None]] = None) -> Syntax[A, S]:
306
- return self.__class__(lambda cls:self.alg(cls).debug(label, formatter), meta=self.meta)
527
+ def debug(
528
+ self,
529
+ label: str,
530
+ formatter: Optional[
531
+ Callable[[Algebra[Any, S], S, Either[Any, Tuple[Any, S]]], None]
532
+ ] = None,
533
+ ) -> Syntax[A, S]:
534
+ return self.__class__(lambda cls: self.alg(cls).debug(label, formatter), meta=self.meta)
307
535
 
308
536
 
309
537
 
@@ -317,9 +545,15 @@ def success(value: Any) -> Syntax[Any, Any]:
317
545
  return Syntax(lambda alg: alg.success(value)).describe(name=f'success({value})', fixity='prefix')
318
546
 
319
547
  def choice(*parsers: Syntax[Any, S]) -> Syntax[Any, S]:
548
+ """
549
+ A shorthand for writing a chain of syntax combined with '|'.
550
+ """
320
551
  return reduce(lambda a, b: a | b, parsers) if len(parsers) > 0 else success(Nothing())
321
552
 
322
553
  def run(syntax: Syntax[A, S], alg: Type[Algebra[A, S]], use_cache:bool, *args: Any, **kwargs: Any) -> Tuple[Any, FrozenDict[str, Tuple[Any, ...]]] | Tuple[Any, None]:
554
+ """
555
+ Run the syntax over the given algebra, and return the result and bind.
556
+ """
323
557
  parser = syntax(alg)
324
558
  input: Optional[S] = alg.state(*args, **kwargs)
325
559
  if input: