llparse 0.1.3__tar.gz → 0.1.5__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.
Files changed (41) hide show
  1. {llparse-0.1.3 → llparse-0.1.5}/PKG-INFO +1 -1
  2. {llparse-0.1.3 → llparse-0.1.5}/llparse/compilator.py +168 -7
  3. {llparse-0.1.3 → llparse-0.1.5}/llparse/frontend.py +34 -11
  4. llparse-0.1.5/llparse/pybuilder/__init__.py +8 -0
  5. {llparse-0.1.3 → llparse-0.1.5}/llparse/pybuilder/builder.py +37 -1
  6. {llparse-0.1.3 → llparse-0.1.5}/llparse/pybuilder/main_code.py +35 -0
  7. {llparse-0.1.3 → llparse-0.1.5}/llparse/pyfront/front.py +0 -3
  8. {llparse-0.1.3 → llparse-0.1.5}/llparse/pyfront/implementation.py +3 -0
  9. {llparse-0.1.3 → llparse-0.1.5}/llparse/pyfront/nodes.py +20 -0
  10. {llparse-0.1.3 → llparse-0.1.5}/llparse.egg-info/PKG-INFO +1 -1
  11. {llparse-0.1.3 → llparse-0.1.5}/pyproject.toml +1 -1
  12. {llparse-0.1.3 → llparse-0.1.5}/tests/test_frontend.py +11 -1
  13. llparse-0.1.3/llparse/pybuilder/__init__.py +0 -2
  14. {llparse-0.1.3 → llparse-0.1.5}/LICENSE +0 -0
  15. {llparse-0.1.3 → llparse-0.1.5}/README.md +0 -0
  16. {llparse-0.1.3 → llparse-0.1.5}/llparse/C_compiler.py +0 -0
  17. {llparse-0.1.3 → llparse-0.1.5}/llparse/__init__.py +0 -0
  18. {llparse-0.1.3 → llparse-0.1.5}/llparse/constants.py +0 -0
  19. {llparse-0.1.3 → llparse-0.1.5}/llparse/cython_builder.py +0 -0
  20. {llparse-0.1.3 → llparse-0.1.5}/llparse/debug.py +0 -0
  21. {llparse-0.1.3 → llparse-0.1.5}/llparse/dot.py +0 -0
  22. {llparse-0.1.3 → llparse-0.1.5}/llparse/enumerator.py +0 -0
  23. {llparse-0.1.3 → llparse-0.1.5}/llparse/errors.py +0 -0
  24. {llparse-0.1.3 → llparse-0.1.5}/llparse/header.py +0 -0
  25. {llparse-0.1.3 → llparse-0.1.5}/llparse/llparse.py +0 -0
  26. {llparse-0.1.3 → llparse-0.1.5}/llparse/pybuilder/loopchecker.py +0 -0
  27. {llparse-0.1.3 → llparse-0.1.5}/llparse/pybuilder/parsemap.py +0 -0
  28. {llparse-0.1.3 → llparse-0.1.5}/llparse/pyfront/containers.py +0 -0
  29. {llparse-0.1.3 → llparse-0.1.5}/llparse/pyfront/namespace.py +0 -0
  30. {llparse-0.1.3 → llparse-0.1.5}/llparse/pyfront/peephole.py +0 -0
  31. {llparse-0.1.3 → llparse-0.1.5}/llparse/pyfront/transform.py +0 -0
  32. {llparse-0.1.3 → llparse-0.1.5}/llparse/settings.py +0 -0
  33. {llparse-0.1.3 → llparse-0.1.5}/llparse/spanalloc.py +0 -0
  34. {llparse-0.1.3 → llparse-0.1.5}/llparse/test.py +0 -0
  35. {llparse-0.1.3 → llparse-0.1.5}/llparse/trie.py +0 -0
  36. {llparse-0.1.3 → llparse-0.1.5}/llparse.egg-info/SOURCES.txt +0 -0
  37. {llparse-0.1.3 → llparse-0.1.5}/llparse.egg-info/dependency_links.txt +0 -0
  38. {llparse-0.1.3 → llparse-0.1.5}/llparse.egg-info/top_level.txt +0 -0
  39. {llparse-0.1.3 → llparse-0.1.5}/setup.cfg +0 -0
  40. {llparse-0.1.3 → llparse-0.1.5}/tests/test_loop_checker.py +0 -0
  41. {llparse-0.1.3 → llparse-0.1.5}/tests/test_span_allocator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: llparse
3
- Version: 0.1.3
3
+ Version: 0.1.5
4
4
  Summary: A Parody of llparse written for writing C Parsers with Python
5
5
  Author-email: Vizonex <VizonexBusiness@gmail.com>
6
6
  Requires-Python: >=3.9
@@ -337,7 +337,7 @@ class Node:
337
337
  out: list[str],
338
338
  node: IWrap[_frontend.node.Node],
339
339
  noAdvance: bool,
340
- value: Optional[int],
340
+ value: Optional[int] = None,
341
341
  ):
342
342
  ctx = self.compilation
343
343
  target = ctx.unwrapNode(node).build(ctx)
@@ -501,7 +501,7 @@ class Pause(Error):
501
501
 
502
502
  assert self.ref.otherwise
503
503
  otherwise = ctx.unwrapNode(self.ref.otherwise.node)
504
- out.append(f"{ctx.currentField()} = (void*) (intptr_t) {otherwise};")
504
+ out.append(f"{ctx.currentField()} = (void*) (intptr_t) {otherwise.cachedDecel or ('s_n_' + otherwise.ref.id.name)};")
505
505
  out.append(f"return {STATE_ERROR};")
506
506
 
507
507
 
@@ -512,7 +512,8 @@ class Sequence(Node):
512
512
 
513
513
  def doBuild(self, out: list[str]):
514
514
  ctx = self.compilation
515
-
515
+ # TODO: llparse_match_t could be easily changed around to
516
+ # Something that can't be overlapped with when compiled with other parsers...
516
517
  out.append("llparse_match_t match_seq;")
517
518
  out.append("")
518
519
 
@@ -639,7 +640,7 @@ class SpanEnd(Node):
639
640
  # Invoke callback
640
641
  callback = ctx.buildCode(ctx.unwrapCode(self.ref.callback, True))
641
642
 
642
- out.append(f"err = {callback}({ctx.stateArg()}, start,{ctx.posArg()});")
643
+ out.append(f"err = {callback}({ctx.stateArg()}, start, {ctx.posArg()});")
643
644
 
644
645
  out.append("if (err != 0) {")
645
646
  tmp = []
@@ -676,6 +677,163 @@ class SpanEnd(Node):
676
677
  out.append(f"return {STATE_ERROR};")
677
678
 
678
679
 
680
+ # Based off arthurschreiber's work with Indutny's Tips and requests added to the mix.
681
+
682
+ # 0x80 I8
683
+ # 0x8000 I16
684
+ # 0x800000 I24
685
+ # 0x1000000 U24
686
+
687
+ class Int(Node):
688
+ def __init__(self, ref: _frontend.node.Int):
689
+ super().__init__(ref)
690
+ self.ref = ref
691
+ self.offset = ref.byteOffset
692
+ # I'm going to deviate from arthurschreiber's work a bit with indutny's suggestions.
693
+ # we should really be using bitwise operators like rshift and lshift
694
+ @property
695
+ def pair(self):
696
+ return self.compilation, self.compilation.stateField(self.ref.field)
697
+
698
+ def readInt8(self, out: list[str]) -> None:
699
+ ctx, index = self.pair
700
+ out.append(f"{index} = ((*{ctx.posArg()}) & 0x80);")
701
+
702
+ def readUInt8(self, out: list[str]) -> None:
703
+ ctx, index = self.pair
704
+ out.append(f"{index} = (*{ctx.posArg()});")
705
+
706
+ # LITTLE ENDIAN
707
+
708
+ def readInt16LE(self, out: list[str]) -> None:
709
+ ctx, index = self.pair
710
+ if self.offset == 0:
711
+ out.append(f"{index} = (*{ctx.posArg()});")
712
+ else:
713
+ # Since BE Belongs to performing << aka left shifts we do >> right shifts
714
+ out.append(f"{index} = ({index} >> 8) | ((*{ctx.posArg()}) & 0x80);")
715
+
716
+ def readUInt16LE(self, out: list[str]) -> None:
717
+ ctx, index = self.pair
718
+ if self.offset == 0:
719
+ out.append(f"{index} = (*{ctx.posArg()});")
720
+ else:
721
+ out.append(f"{index} = ({index} >> 8) | (*{ctx.posArg()});")
722
+
723
+ def readInt24LE(self, out: list[str]) -> None:
724
+ ctx, index = self.pair
725
+ if self.offset == 0:
726
+ out.append(f"{index} = (*{ctx.posArg()});")
727
+ elif self.offset == 1:
728
+ out.append(f"{index} = ({index} >> 8) | (*{ctx.posArg()});")
729
+ else:
730
+ out.append(f"{index} = ({index} >> 8) | ((*{ctx.posArg()}) & 0x80);")
731
+
732
+ def readUInt24LE(self, out: list[str]) -> None:
733
+ ctx, index = self.pair
734
+ if self.offset == 0:
735
+ out.append(f"{index} = (*{ctx.posArg()});")
736
+ else:
737
+ out.append(f"{index} = ({index} >> 8) | (*{ctx.posArg()});")
738
+
739
+ def readInt32LE(self, out: list[str]) -> None:
740
+ ctx, index = self.pair
741
+ if self.offset == 0:
742
+ out.append(f"{index} = (*{ctx.posArg()});")
743
+ elif self.offset in (1, 2):
744
+ out.append(f"{index} = ({index} >> 8) | (*{ctx.posArg()});")
745
+ else:
746
+ out.append(f"{index} = ({index} >> 8) | ((*{ctx.posArg()}) & 0x80);")
747
+
748
+ def readUInt32LE(self, out: list[str]) -> None:
749
+ ctx, index = self.pair
750
+ if self.offset == 0:
751
+ out.append(f"{index} = (*{ctx.posArg()});")
752
+ else:
753
+ out.append(f"{index} = ({index} >> 8) | (*{ctx.posArg()});")
754
+
755
+ # BIG ENDIAN
756
+
757
+ def readInt16BE(self, out: list[str]) -> None:
758
+ ctx, index = self.pair
759
+ if self.offset == 0:
760
+ out.append(f"{index} = (*{ctx.posArg()});")
761
+ else:
762
+ # Since LE Belongs to >> we do "<<" instead
763
+ out.append(f"{index} = ({index} << 8) | ((*{ctx.posArg()}) & 0x80);")
764
+
765
+ def readUInt16BE(self, out: list[str]) -> None:
766
+ ctx, index = self.pair
767
+ if self.offset == 0:
768
+ out.append(f"{index} = (*{ctx.posArg()});")
769
+ else:
770
+ out.append(f"{index} = ({index} << 8) | (*{ctx.posArg()});")
771
+
772
+ def readInt24BE(self, out: list[str]) -> None:
773
+ ctx, index = self.pair
774
+ if self.offset == 0:
775
+ out.append(f"{index} = (*{ctx.posArg()});")
776
+ elif self.offset == 1:
777
+ out.append(f"{index} = ({index} << 8) | (*{ctx.posArg()});")
778
+ else:
779
+ out.append(f"{index} = ({index} << 8) | ((*{ctx.posArg()}) & 0x80);")
780
+
781
+ def readUInt24BE(self, out: list[str]) -> None:
782
+ ctx, index = self.pair
783
+ if self.offset == 0:
784
+ out.append(f"{index} = (*{ctx.posArg()});")
785
+ else:
786
+ out.append(f"{index} = ({index} << 8) | (*{ctx.posArg()});")
787
+
788
+ def readInt32BE(self, out: list[str]) -> None:
789
+ ctx, index = self.pair
790
+ if self.offset == 0:
791
+ out.append(f"{index} = (*{ctx.posArg()});")
792
+ elif self.offset in (1, 2):
793
+ out.append(f"{index} = ({index} << 8) | (*{ctx.posArg()});")
794
+ else:
795
+ out.append(f"{index} = ({index} << 8) | ((*{ctx.posArg()}) & 0x80);")
796
+
797
+ def readUInt32BE(self, out: list[str]) -> None:
798
+ ctx, index = self.pair
799
+ if self.offset == 0:
800
+ out.append(f"{index} = (*{ctx.posArg()});")
801
+ else:
802
+ out.append(f"{index} = ({index} << 8) | (*{ctx.posArg()});")
803
+
804
+
805
+ def doBuild(self, out:list[str]):
806
+ self.prologue(out)
807
+ # I'm still supporting 3.9 but I plan to drop it's support in favor of match case soon...
808
+ bits = self.ref.bits
809
+
810
+ if self.compilation.getFieldType(self.ref.field) == 'ptr':
811
+ raise ValueError(f'property {self.ref.field} should not use pointers but it was given \"ptr\"')
812
+
813
+ if bits == 1:
814
+ self.readInt8(out) if self.ref.signed else self.readUInt8(out)
815
+ elif bits == 2:
816
+ if self.ref.littleEndian:
817
+ self.readInt16LE(out) if self.ref.signed else self.readUInt16LE(out)
818
+ else:
819
+ self.readInt16BE(out) if self.ref.signed else self.readUInt16BE(out)
820
+ elif bits == 3:
821
+ if self.ref.littleEndian:
822
+ self.readInt24LE(out) if self.ref.signed else self.readUInt24LE(out)
823
+ else:
824
+ self.readInt24BE(out) if self.ref.signed else self.readUInt24BE(out)
825
+ else:
826
+ if self.ref.littleEndian:
827
+ self.readInt32LE(out) if self.ref.signed else self.readUInt32LE(out)
828
+ else:
829
+ self.readInt32BE(out) if self.ref.signed else self.readUInt32BE(out)
830
+ # TODO: uint64 & int64
831
+
832
+ self.tailTo(out, self.ref.otherwise.node, self.ref.otherwise.noAdvance, None)
833
+
834
+
835
+
836
+
679
837
  MAX_CHAR = 0xFF
680
838
  TABLE_GROUP = 16
681
839
 
@@ -1077,13 +1235,14 @@ class Compilation:
1077
1235
  r = Consume(ref)
1078
1236
  elif isinstance(ref, _frontend.node.Empty):
1079
1237
  r = Empty(ref)
1238
+ elif isinstance(ref, _frontend.node.Pause):
1239
+ r = Pause(ref)
1240
+
1080
1241
  elif isinstance(ref, _frontend.node.Error):
1081
1242
  r = Error(ref)
1082
1243
  elif isinstance(ref, _frontend.node.Invoke):
1083
1244
  r = Invoke(ref)
1084
- elif isinstance(ref, _frontend.node.Pause):
1085
- r = Pause(ref)
1086
-
1245
+
1087
1246
  elif isinstance(ref, _frontend.node.SpanStart):
1088
1247
  r = SpanStart(ref)
1089
1248
 
@@ -1096,6 +1255,8 @@ class Compilation:
1096
1255
  r = Sequence(ref)
1097
1256
  elif isinstance(ref, _frontend.node.TableLookup):
1098
1257
  r = TableLookup(ref)
1258
+ elif isinstance(ref, _frontend.node.Int):
1259
+ r = Int(ref)
1099
1260
  else:
1100
1261
  raise TypeError(
1101
1262
  f'refrence "{ref}" is an Invalid Code Type , TypeName:"{ref.__class__.__name__}"'
@@ -211,7 +211,7 @@ class Frontend:
211
211
  result = nodeImpl.Error(_frontend.node.Error(ID(), node.code, node.reason))
212
212
 
213
213
  elif isinstance(node, source.code.Pause):
214
- result = nodeImpl.Pause(_frontend.node.Error(ID(), node.code, node.reason))
214
+ result = nodeImpl.Pause(_frontend.node.Pause(ID(), node.code, node.reason))
215
215
 
216
216
  elif isinstance(node, source.code.Comsume):
217
217
  result = nodeImpl.Consume(_frontend.node.Consume(ID(), node.field))
@@ -244,6 +244,10 @@ class Frontend:
244
244
 
245
245
  elif isinstance(node, source.code.Match):
246
246
  result = self.translateMatch(node)
247
+
248
+ elif isinstance(node, source.node.Int):
249
+ result = self.translateInt(node)
250
+
247
251
  else:
248
252
  raise Exception(f'Unknown Node Type for :"{node.name}" {type(node)}')
249
253
 
@@ -251,24 +255,26 @@ class Frontend:
251
255
 
252
256
  if isinstance(result, list):
253
257
  # result:list[WrappedNode]
254
- assert isinstance(node, source.code.Match)
255
- _match = node
256
258
 
257
- if not otherwise:
258
- raise Exception(f'Node "{node.name}" has no ".otherwise()"')
259
+ assert isinstance(node, (source.code.Match, source.node.Int))
260
+ _match = node
261
+
262
+ assert otherwise, (f'Node "{node.name}" has no ".otherwise()"')
259
263
 
260
- else:
264
+ if isinstance(node, source.node.Match):
261
265
  for child in result:
262
266
  if not child.ref.otherwise:
263
267
  child.ref.setOtherwise(
264
268
  self.translate(otherwise.node), otherwise.noAdvance
265
269
  )
270
+ transform = self.translateTransform(_match.getTransform())
271
+ for child in result:
272
+ # TODO Vizonex : This might break , be sure to make a workaround function here...
273
+ child.ref.setTransform(transform)
266
274
 
267
- transform = self.translateTransform(_match.getTransform())
268
- for child in result:
269
- # TODO Vizonex : This might break , be sure to make a workaround function here...
270
- child.ref.setTransform(transform)
271
-
275
+
276
+ else:
277
+ result[-1].ref.setOtherwise(self.translate(otherwise.node), otherwise.noAdvance)
272
278
  assert len(result) >= 1
273
279
  return result[0]
274
280
 
@@ -299,6 +305,23 @@ class Frontend:
299
305
  assert len(list(node)) == 0
300
306
 
301
307
  return single
308
+
309
+ def translateInt(self, node: source.node.Int) -> list[IWrap[_frontend.node.Int]]:
310
+ inner = _frontend.node.Int(self.Id.id(node.name), node.field, node.bits, node.signed, node.little_endian, 0)
311
+ result = [self.implementation.node.Int(inner)]
312
+ # front is to avoid overlapping with python's functions (aka next)
313
+ front = self.Map[node] = result[0]
314
+
315
+ for offset in range(1, node.bits):
316
+ unique_name = self.Id.id(f"{node.name}_byte{offset + 1}")
317
+ inner = _frontend.node.Int(unique_name, node.field, node.bits, node.signed, node.little_endian, offset)
318
+ outer = self.implementation.node.Int(inner)
319
+ result.append(outer)
320
+ # Integers will advance since they are unpacking values...
321
+ front.ref.setOtherwise(outer, False)
322
+ front = result[-1]
323
+ return result
324
+
302
325
 
303
326
  def maybeTableLookup(
304
327
  self, node: source.code.Match, trie: TrieSingle, children: MatchChildren
@@ -0,0 +1,8 @@
1
+ from ..pybuilder.builder import *
2
+ from ..pybuilder.loopchecker import *
3
+ from ..pybuilder.main_code import (
4
+ # I'll add more soon I feel a little lazy at the moment.
5
+ Node,
6
+ Match,
7
+ Int
8
+ )
@@ -1,7 +1,8 @@
1
1
  from typing import Literal, Optional, Union
2
-
3
2
  from ..pybuilder import main_code as code
4
3
 
4
+ # typehinting node and code (TODO: Vizonex) Lets seperate the modules soon...
5
+ node = code
5
6
  # from pydot import graph_from_dot_data
6
7
 
7
8
 
@@ -316,3 +317,38 @@ class Builder:
316
317
  def properties(self) -> list[Property]:
317
318
  """Return list of all allocated properties in parser's state."""
318
319
  return list(self.privProperties.values())
320
+
321
+ def intBE(self, field: str, bits:int):
322
+ """
323
+ :param field: State's property name
324
+ :param bits: Number of bits to use
325
+ """
326
+ return code.Int(field, bits, True, False)
327
+
328
+ def intLE(self, field: str, bits: int):
329
+ """
330
+ return a node for unpacking arrays to integers
331
+
332
+ :param field: State's property name
333
+ :param bits: Number of bits to use
334
+ """
335
+ return code.Int(field, bits, True, True)
336
+
337
+ def uintBE(self, field: str, bits: int):
338
+ """
339
+ return a node for unpacking arrays to integers
340
+
341
+ :param field: State's property name
342
+ :param bits: Number of bits to use
343
+ """
344
+ return code.Int(field, bits, False, False)
345
+
346
+ def uintLE(self, field: str, bits: int):
347
+ """
348
+ return a node for unpacking arrays to integers
349
+
350
+ :param field: State's property name
351
+ :param bits: Number of bits to use
352
+ """
353
+ return code.Int(field, bits, False, True)
354
+
@@ -92,6 +92,8 @@ class _Match(Code):
92
92
  super().__init__("match", name)
93
93
 
94
94
 
95
+
96
+
95
97
  @dataclass
96
98
  class IMulAddOptions:
97
99
  base: int
@@ -244,6 +246,39 @@ class Invoke(Node):
244
246
  self.addEdge(Edge(targetNode, True, numKey, None))
245
247
 
246
248
 
249
+
250
+ # Not in llparse node-js (yet) But I wanted to implement
251
+ # this into my version since I am making a very important
252
+ # http2 frame parser
253
+
254
+ # SEE: https://github.com/nodejs/llparse-frontend/pull/1
255
+
256
+ def build_name(field:str, bits: int, signed:bool, little_endian:bool) -> str:
257
+ result = f"{field}_{'int' if signed else 'uint'}_{bits * 8}"
258
+ if bits > 1:
259
+ return result + ('_le' if little_endian else 'be')
260
+ else:
261
+ return result
262
+
263
+
264
+ class Int(Node):
265
+ """Used for parsing bytes via unpacking"""
266
+ def __init__(self, field: str, bits: int, signed: bool, little_endian: bool) -> None:
267
+ """
268
+ :param field: State's property name
269
+ :param bits: Number of bits to use
270
+ :param signed: Number is signed
271
+ :param little_endian: true if le, false if be
272
+ """
273
+ if bits < 0:
274
+ raise ValueError("bits should be a positive integer")
275
+ self.field = field
276
+ self.bits = bits
277
+ self.signed = signed
278
+ self.little_endian = little_endian
279
+ super().__init__(build_name(field, bits, signed, little_endian))
280
+
281
+
247
282
  # -- Transfroms --
248
283
 
249
284
  TransformName = ["to_lower_unsafe", "to_lower"]
@@ -14,9 +14,6 @@ Signature = TypeVar("Signature", bytes, str)
14
14
  class IWrap(Generic[T]):
15
15
  ref: T
16
16
 
17
- # def __hash__(self) -> int:
18
- # return hash(self.ref)
19
-
20
17
 
21
18
  def toCacheKey(value: Union[int, bool]) -> str:
22
19
  if isinstance(value, int):
@@ -37,6 +37,9 @@ class INodeImplementation:
37
37
 
38
38
  def TableLookup(self, n: node.TableLookup):
39
39
  return IWrap(n)
40
+
41
+ def Int(self, n: node.Int):
42
+ return IWrap(n)
40
43
 
41
44
 
42
45
  class ITransformImplementation:
@@ -153,6 +153,26 @@ class Pause(Error):
153
153
  super().__init__(id, code, reason)
154
154
 
155
155
 
156
+ # Not in llparse node-js (yet) But I wanted to implement
157
+ # this into my version since I am making a very important
158
+ # http2 frame parser
159
+
160
+ # SEE: https://github.com/nodejs/llparse-frontend/pull/1
161
+
162
+ @dataclass
163
+ class Int(Node):
164
+ field: str
165
+ bits: int
166
+ signed: bool
167
+ littleEndian: bool
168
+ byteOffset: int
169
+
170
+ def __hash__(self):
171
+ return hash(self.id)
172
+
173
+
174
+
175
+
156
176
  @dataclass
157
177
  class ISeqEdge:
158
178
  node: IWrap[Node]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: llparse
3
- Version: 0.1.3
3
+ Version: 0.1.5
4
4
  Summary: A Parody of llparse written for writing C Parsers with Python
5
5
  Author-email: Vizonex <VizonexBusiness@gmail.com>
6
6
  Requires-Python: >=3.9
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "llparse"
3
- version = "0.1.3"
3
+ version = "0.1.5"
4
4
  description = "A Parody of llparse written for writing C Parsers with Python"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -22,4 +22,14 @@ def test_build_tables():
22
22
  assert "lookup_table" in p.build(start).c
23
23
 
24
24
 
25
-
25
+ def test_pausing():
26
+ # Ensure frotentend LoopChecker does not mark off against Pausing
27
+ p = LLParse("lltest")
28
+ s = p.node('start')
29
+ s2 = p.node('start2')
30
+ s.match('p', p.pause(1, 'parser was asked to pause').otherwise(s2)).skipTo(s)
31
+ s2.match('p', p.pause(2, 'parser was asked to pause again').otherwise(s)).skipTo(s2)
32
+
33
+ p.build(s)
34
+
35
+
@@ -1,2 +0,0 @@
1
- from ..pybuilder.builder import *
2
- from ..pybuilder.loopchecker import *
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes