llparse 0.1.8__tar.gz → 0.1.9__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 (44) hide show
  1. {llparse-0.1.8 → llparse-0.1.9}/PKG-INFO +1 -1
  2. {llparse-0.1.8 → llparse-0.1.9}/llparse/C_compiler.py +1 -1
  3. {llparse-0.1.8 → llparse-0.1.9}/llparse/__init__.py +2 -0
  4. {llparse-0.1.8 → llparse-0.1.9}/llparse/compilator.py +32 -29
  5. {llparse-0.1.8 → llparse-0.1.9}/llparse/cython_builder.py +1 -1
  6. {llparse-0.1.8 → llparse-0.1.9}/llparse/enumerator.py +3 -2
  7. {llparse-0.1.8 → llparse-0.1.9}/llparse/frontend.py +35 -23
  8. {llparse-0.1.8 → llparse-0.1.9}/llparse/llparse.py +14 -12
  9. llparse-0.1.9/llparse/pybuilder/__init__.py +7 -0
  10. {llparse-0.1.8 → llparse-0.1.9}/llparse/pybuilder/builder.py +1 -2
  11. {llparse-0.1.8 → llparse-0.1.9}/llparse/pybuilder/loopchecker.py +2 -3
  12. {llparse-0.1.8 → llparse-0.1.9}/llparse/pybuilder/main_code.py +0 -18
  13. llparse-0.1.9/llparse/pyfront/__init__.py +3 -0
  14. llparse-0.1.8/llparse/pyfront/front.py → llparse-0.1.9/llparse/pyfront/code.py +5 -1
  15. {llparse-0.1.8 → llparse-0.1.9}/llparse/pyfront/implementation.py +4 -4
  16. llparse-0.1.9/llparse/pyfront/namespace.py +9 -0
  17. {llparse-0.1.8 → llparse-0.1.9}/llparse/pyfront/nodes.py +5 -10
  18. llparse-0.1.9/llparse/pyfront/peephole.py +47 -0
  19. {llparse-0.1.8 → llparse-0.1.9}/llparse/spanalloc.py +17 -12
  20. {llparse-0.1.8 → llparse-0.1.9}/llparse/trie.py +10 -9
  21. {llparse-0.1.8 → llparse-0.1.9}/llparse.egg-info/PKG-INFO +1 -1
  22. {llparse-0.1.8 → llparse-0.1.9}/llparse.egg-info/SOURCES.txt +2 -3
  23. {llparse-0.1.8 → llparse-0.1.9}/pyproject.toml +1 -1
  24. llparse-0.1.8/llparse/pybuilder/__init__.py +0 -8
  25. llparse-0.1.8/llparse/pyfront/containers.py +0 -33
  26. llparse-0.1.8/llparse/pyfront/namespace.py +0 -3
  27. llparse-0.1.8/llparse/pyfront/peephole.py +0 -45
  28. llparse-0.1.8/llparse/test.py +0 -233
  29. {llparse-0.1.8 → llparse-0.1.9}/LICENSE +0 -0
  30. {llparse-0.1.8 → llparse-0.1.9}/README.md +0 -0
  31. {llparse-0.1.8 → llparse-0.1.9}/llparse/constants.py +0 -0
  32. {llparse-0.1.8 → llparse-0.1.9}/llparse/debug.py +0 -0
  33. {llparse-0.1.8 → llparse-0.1.9}/llparse/dot.py +0 -0
  34. {llparse-0.1.8 → llparse-0.1.9}/llparse/errors.py +0 -0
  35. {llparse-0.1.8 → llparse-0.1.9}/llparse/header.py +0 -0
  36. {llparse-0.1.8 → llparse-0.1.9}/llparse/pybuilder/parsemap.py +0 -0
  37. {llparse-0.1.8 → llparse-0.1.9}/llparse/pyfront/transform.py +0 -0
  38. {llparse-0.1.8 → llparse-0.1.9}/llparse/settings.py +0 -0
  39. {llparse-0.1.8 → llparse-0.1.9}/llparse.egg-info/dependency_links.txt +0 -0
  40. {llparse-0.1.8 → llparse-0.1.9}/llparse.egg-info/top_level.txt +0 -0
  41. {llparse-0.1.8 → llparse-0.1.9}/setup.cfg +0 -0
  42. {llparse-0.1.8 → llparse-0.1.9}/tests/test_frontend.py +0 -0
  43. {llparse-0.1.8 → llparse-0.1.9}/tests/test_loop_checker.py +0 -0
  44. {llparse-0.1.8 → llparse-0.1.9}/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.8
3
+ Version: 0.1.9
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
@@ -182,7 +182,7 @@ class CCompiler:
182
182
  else:
183
183
  # TODO (Vizonex) Merge lines 139 & 140 together in a future update
184
184
  callback = (
185
- f"(({info.prefix}__span_cb)" + ctx.spanCbField(span.index) + f")"
185
+ f"(({info.prefix}__span_cb)" + ctx.spanCbField(span.index) + ")"
186
186
  )
187
187
 
188
188
  args = [ctx.stateArg(), posField, f"(const char*) {ctx.endPosArg()}"]
@@ -1,2 +1,4 @@
1
1
  from .dot import Dot
2
2
  from .llparse import LLParse
3
+
4
+ __all__ = ("Dot", "LLParse")
@@ -273,12 +273,13 @@ class Update(Field):
273
273
  out.append(f"{self.field(ctx)} = {self.ref.value};")
274
274
  out.append("return 0;")
275
275
 
276
+
276
277
  class Operator(Field):
277
278
  def __init__(self, ref: _frontend.code.Operator):
278
279
  self.ref = ref
279
-
280
+
280
281
  def doBuild(self, ctx: "Compilation", out: list[str]):
281
- out.append(f'return {self.field(ctx)} {self.ref.op} {self.ref.value};')
282
+ out.append(f"return {self.field(ctx)} {self.ref.op} {self.ref.value};")
282
283
 
283
284
 
284
285
  @dataclass
@@ -508,7 +509,9 @@ class Pause(Error):
508
509
 
509
510
  assert self.ref.otherwise
510
511
  otherwise = ctx.unwrapNode(self.ref.otherwise.node)
511
- out.append(f"{ctx.currentField()} = (void*) (intptr_t) {otherwise.cachedDecel or ('s_n_' + otherwise.ref.id.name)};")
512
+ out.append(
513
+ f"{ctx.currentField()} = (void*) (intptr_t) {otherwise.cachedDecel or ('s_n_' + otherwise.ref.id.name)};"
514
+ )
512
515
  out.append(f"return {STATE_ERROR};")
513
516
 
514
517
 
@@ -519,7 +522,7 @@ class Sequence(Node):
519
522
 
520
523
  def doBuild(self, out: list[str]):
521
524
  ctx = self.compilation
522
- # TODO: llparse_match_t could be easily changed around to
525
+ # TODO: llparse_match_t could be easily changed around to
523
526
  # Something that can't be overlapped with when compiled with other parsers...
524
527
  out.append("llparse_match_t match_seq;")
525
528
  out.append("")
@@ -691,11 +694,13 @@ class SpanEnd(Node):
691
694
  # 0x800000 I24
692
695
  # 0x1000000 U24
693
696
 
697
+
694
698
  class Int(Node):
695
699
  def __init__(self, ref: _frontend.node.Int):
696
700
  super().__init__(ref)
697
701
  self.ref = ref
698
702
  self.offset = ref.byteOffset
703
+
699
704
  # I'm going to deviate from arthurschreiber's work a bit with indutny's suggestions.
700
705
  # we should really be using bitwise operators like rshift and lshift
701
706
  @property
@@ -705,11 +710,11 @@ class Int(Node):
705
710
  def readInt8(self, out: list[str]) -> None:
706
711
  ctx, index = self.pair
707
712
  out.append(f"{index} = ((*{ctx.posArg()}) & 0x80);")
708
-
713
+
709
714
  def readUInt8(self, out: list[str]) -> None:
710
715
  ctx, index = self.pair
711
716
  out.append(f"{index} = (*{ctx.posArg()});")
712
-
717
+
713
718
  # LITTLE ENDIAN
714
719
 
715
720
  def readInt16LE(self, out: list[str]) -> None:
@@ -717,7 +722,7 @@ class Int(Node):
717
722
  if self.offset == 0:
718
723
  out.append(f"{index} = (*{ctx.posArg()});")
719
724
  else:
720
- # Since BE Belongs to performing << aka left shifts we do >> right shifts
725
+ # Since BE Belongs to performing << aka left shifts we do >> right shifts
721
726
  out.append(f"{index} = ({index} >> 8) | ((*{ctx.posArg()}) & 0x80);")
722
727
 
723
728
  def readUInt16LE(self, out: list[str]) -> None:
@@ -742,7 +747,7 @@ class Int(Node):
742
747
  out.append(f"{index} = (*{ctx.posArg()});")
743
748
  else:
744
749
  out.append(f"{index} = ({index} >> 8) | (*{ctx.posArg()});")
745
-
750
+
746
751
  def readInt32LE(self, out: list[str]) -> None:
747
752
  ctx, index = self.pair
748
753
  if self.offset == 0:
@@ -758,17 +763,17 @@ class Int(Node):
758
763
  out.append(f"{index} = (*{ctx.posArg()});")
759
764
  else:
760
765
  out.append(f"{index} = ({index} >> 8) | (*{ctx.posArg()});")
761
-
762
- # BIG ENDIAN
763
-
766
+
767
+ # BIG ENDIAN
768
+
764
769
  def readInt16BE(self, out: list[str]) -> None:
765
770
  ctx, index = self.pair
766
771
  if self.offset == 0:
767
772
  out.append(f"{index} = (*{ctx.posArg()});")
768
773
  else:
769
- # Since LE Belongs to >> we do "<<" instead
774
+ # Since LE Belongs to >> we do "<<" instead
770
775
  out.append(f"{index} = ({index} << 8) | ((*{ctx.posArg()}) & 0x80);")
771
-
776
+
772
777
  def readUInt16BE(self, out: list[str]) -> None:
773
778
  ctx, index = self.pair
774
779
  if self.offset == 0:
@@ -791,7 +796,7 @@ class Int(Node):
791
796
  out.append(f"{index} = (*{ctx.posArg()});")
792
797
  else:
793
798
  out.append(f"{index} = ({index} << 8) | (*{ctx.posArg()});")
794
-
799
+
795
800
  def readInt32BE(self, out: list[str]) -> None:
796
801
  ctx, index = self.pair
797
802
  if self.offset == 0:
@@ -807,15 +812,16 @@ class Int(Node):
807
812
  out.append(f"{index} = (*{ctx.posArg()});")
808
813
  else:
809
814
  out.append(f"{index} = ({index} << 8) | (*{ctx.posArg()});")
810
-
811
-
812
- def doBuild(self, out:list[str]):
815
+
816
+ def doBuild(self, out: list[str]):
813
817
  self.prologue(out)
814
818
  # I'm still supporting 3.9 but I plan to drop it's support in favor of match case soon...
815
819
  bits = self.ref.bits
816
-
817
- if self.compilation.getFieldType(self.ref.field) == 'ptr':
818
- raise ValueError(f'property {self.ref.field} should not use pointers but it was given \"ptr\"')
820
+
821
+ if self.compilation.getFieldType(self.ref.field) == "ptr":
822
+ raise ValueError(
823
+ f'property {self.ref.field} should not use pointers but it was given "ptr"'
824
+ )
819
825
 
820
826
  if bits == 1:
821
827
  self.readInt8(out) if self.ref.signed else self.readUInt8(out)
@@ -835,10 +841,8 @@ class Int(Node):
835
841
  else:
836
842
  self.readInt32BE(out) if self.ref.signed else self.readUInt32BE(out)
837
843
  # TODO: uint64 & int64
838
-
839
- self.tailTo(out, self.ref.otherwise.node, self.ref.otherwise.noAdvance, None)
840
-
841
844
 
845
+ self.tailTo(out, self.ref.otherwise.node, self.ref.otherwise.noAdvance, None)
842
846
 
843
847
 
844
848
  MAX_CHAR = 0xFF
@@ -1193,12 +1197,11 @@ class Compilation:
1193
1197
  raise LookupError(f'Field "{field}" not found')
1194
1198
 
1195
1199
  # Helpers are different since in python we have duck typing - Vizonex
1196
- def unwrapCode(
1197
- self, code: IWrap[_frontend.code.Code], allow_continue: bool = False
1198
- ):
1199
- if self.CodeContainer.get(code):
1200
+ def unwrapCode(self, code: IWrap[_frontend.code.Code]):
1201
+ if __code := self.CodeContainer.get(code):
1200
1202
  # Give some indication that the element has already been built...
1201
- return self.CodeContainer[code]
1203
+ # return self.CodeContainer[code]
1204
+ return __code
1202
1205
 
1203
1206
  ref = code.ref
1204
1207
 
@@ -1251,7 +1254,7 @@ class Compilation:
1251
1254
  r = Error(ref)
1252
1255
  elif isinstance(ref, _frontend.node.Invoke):
1253
1256
  r = Invoke(ref)
1254
-
1257
+
1255
1258
  elif isinstance(ref, _frontend.node.SpanStart):
1256
1259
  r = SpanStart(ref)
1257
1260
 
@@ -4,7 +4,7 @@ from contextlib import contextmanager
4
4
  from typing import Optional
5
5
 
6
6
  from .frontend import IFrontendResult
7
- from .pyfront.front import Match
7
+ from .pyfront.code import Match
8
8
  from .pyfront.nodes import Invoke
9
9
 
10
10
  # Inspired by Cython's Writer
@@ -1,10 +1,11 @@
1
- from .pyfront.front import IWrap
1
+ from .pyfront.code import IWrap
2
2
  from .pyfront.nodes import Node
3
3
 
4
4
 
5
5
  class Enumerator:
6
+ # TODO: Remove from staticmethod and make one seperate function
6
7
  @staticmethod
7
- def getAllNodes(root: IWrap[Node]):
8
+ def getAllNodes(root: IWrap[Node]) -> list[IWrap[Node]]:
8
9
  nodes: set[IWrap[Node]] = set()
9
10
  queue: list[IWrap[Node]] = [root]
10
11
 
@@ -1,5 +1,5 @@
1
1
  from dataclasses import dataclass, field
2
- from typing import Literal, Optional, Union, TypeVar
2
+ from typing import Literal, Optional, TypeVar, Union
3
3
 
4
4
  from .enumerator import Enumerator
5
5
  from .pybuilder import LoopChecker
@@ -7,12 +7,12 @@ from .pybuilder import builder as source
7
7
 
8
8
  # from pyfront.namespace import code, node , transform
9
9
  from .pyfront import namespace as _frontend
10
- from .pyfront.front import Identifier, IWrap, SpanField
10
+ from .pyfront.code import Identifier, IWrap, SpanField
11
11
  from .pyfront.implementation import IImplementation
12
12
  from .pyfront.nodes import ITableEdge
13
13
  from .pyfront.peephole import Peephole
14
14
  from .spanalloc import SpanAllocator
15
- from .trie import Trie, TrieEmpty, TrieNode, TrieSequence, TrieSingle, ITrieSingleChild
15
+ from .trie import ITrieSingleChild, Trie, TrieEmpty, TrieNode, TrieSequence, TrieSingle
16
16
 
17
17
  DEFAULT_MIN_TABLE_SIZE = 32
18
18
  DEFAULT_MAX_TABLE_WIDTH = 4
@@ -22,8 +22,8 @@ from logging import getLogger
22
22
  log = getLogger("llparse.frontend")
23
23
 
24
24
 
25
- CodeT = TypeVar('CodeT', bound=source.code.Code)
26
- NodeT = TypeVar('NodeT', bound=source.node.Node)
25
+ CodeT = TypeVar("CodeT", bound=source.code.Code)
26
+ NodeT = TypeVar("NodeT", bound=source.node.Node)
27
27
 
28
28
  WrappedNode = IWrap[_frontend.node.Node]
29
29
  WrappedCode = IWrap[_frontend.code.Code]
@@ -196,7 +196,7 @@ class Frontend:
196
196
  _frontend.node.Sequence,
197
197
  _frontend.node.Single,
198
198
  _frontend.node.TableLookup,
199
- _frontend.node.SpanStart
199
+ _frontend.node.SpanStart,
200
200
  ),
201
201
  ):
202
202
  self.resumptionTargets.add(node)
@@ -263,8 +263,8 @@ class Frontend:
263
263
 
264
264
  assert isinstance(node, (source.code.Match, source.node.Int))
265
265
  _match = node
266
-
267
- assert otherwise, (f'Node "{node.name}" has no ".otherwise()"')
266
+
267
+ assert otherwise, f'Node "{node.name}" has no ".otherwise()"'
268
268
 
269
269
  if isinstance(node, source.node.Match):
270
270
  for child in result:
@@ -277,9 +277,10 @@ class Frontend:
277
277
  # TODO Vizonex : This might break , be sure to make a workaround function here...
278
278
  child.ref.setTransform(transform)
279
279
 
280
-
281
280
  else:
282
- result[-1].ref.setOtherwise(self.translate(otherwise.node), otherwise.noAdvance)
281
+ result[-1].ref.setOtherwise(
282
+ self.translate(otherwise.node), otherwise.noAdvance
283
+ )
283
284
  assert len(result) >= 1
284
285
  return result[0]
285
286
 
@@ -310,16 +311,30 @@ class Frontend:
310
311
  assert len(list(node)) == 0
311
312
 
312
313
  return single
313
-
314
+
314
315
  def translateInt(self, node: source.node.Int) -> list[IWrap[_frontend.node.Int]]:
315
- inner = _frontend.node.Int(self.Id.id(node.name), node.field, node.bits, node.signed, node.little_endian, 0)
316
+ inner = _frontend.node.Int(
317
+ self.Id.id(node.name),
318
+ node.field,
319
+ node.bits,
320
+ node.signed,
321
+ node.little_endian,
322
+ 0,
323
+ )
316
324
  result = [self.implementation.node.Int(inner)]
317
325
  # front is to avoid overlapping with python's functions (aka next)
318
326
  front = self.Map[node] = result[0]
319
-
327
+
320
328
  for offset in range(1, node.bits):
321
329
  unique_name = self.Id.id(f"{node.name}_byte{offset + 1}")
322
- inner = _frontend.node.Int(unique_name, node.field, node.bits, node.signed, node.little_endian, offset)
330
+ inner = _frontend.node.Int(
331
+ unique_name,
332
+ node.field,
333
+ node.bits,
334
+ node.signed,
335
+ node.little_endian,
336
+ offset,
337
+ )
323
338
  outer = self.implementation.node.Int(inner)
324
339
  result.append(outer)
325
340
  # Integers will advance since they are unpacking values...
@@ -327,7 +342,6 @@ class Frontend:
327
342
  front = result[-1]
328
343
  return result
329
344
 
330
-
331
345
  def maybeTableLookup(
332
346
  self, node: source.code.Match, trie: TrieSingle, children: MatchChildren
333
347
  ):
@@ -466,9 +480,7 @@ class Frontend:
466
480
  def translateSpanCode(self, code: source.code._Span):
467
481
  return self.translateCode(code)
468
482
 
469
- def translateCode(
470
- self, code: CodeT
471
- ):
483
+ def translateCode(self, code: CodeT):
472
484
  """Translates Builder Classes to Frontend Classes..."""
473
485
 
474
486
  prefixed = self.codeId.id(code.name).name
@@ -479,13 +491,13 @@ class Frontend:
479
491
  res = codeImpl.IsEqual(
480
492
  _frontend.code.IsEqual(prefixed, code.field, code.value)
481
493
  )
482
- # custom thing I added in 0.1.6 I encourage the typescript
494
+ # custom thing I added in 0.1.6 I encourage the typescript
483
495
  # maintainers and developers of llparse to look into this :)
484
496
  elif isinstance(code, source.code.Operator):
485
- res = codeImpl.Operator(_frontend.code.Operator(
486
- prefixed, code.field, code.value, code.op
487
- ))
488
-
497
+ res = codeImpl.Operator(
498
+ _frontend.code.Operator(prefixed, code.field, code.value, code.op)
499
+ )
500
+
489
501
  elif isinstance(code, source.code.Load):
490
502
  res = codeImpl.Load(_frontend.code.Load(prefixed, code.field))
491
503
 
@@ -67,21 +67,19 @@ class Compiler:
67
67
  properties: list[source.Property],
68
68
  header_name: Optional[str] = None,
69
69
  Impl: Optional[IImplementation] = IImplementation(),
70
- override_llparse_name: bool = False
70
+ override_llparse_name: bool = False,
71
71
  ):
72
72
  """Creates the C and header file..."""
73
73
  info = self.to_frontend(root, properties, Impl)
74
74
  hb = HeaderBuilder(self.prefix, self.headerGuard, properties, info.spans)
75
- cdata = CCompiler(header_name, self.debug).compile(info)
75
+ cdata = CCompiler(header_name, self.debug).compile(info)
76
76
  if override_llparse_name:
77
77
  # sometimes users want to combine parsers together when compiling with C
78
78
  # to make up for conflicts with other parsers example: llhttp
79
79
  # there should be a fair way of compiling everything.
80
- cdata = cdata.replace('llparse', self.prefix)
80
+ cdata = cdata.replace("llparse", self.prefix)
81
81
 
82
- return CompilerResult(
83
- cdata , hb.build()
84
- )
82
+ return CompilerResult(cdata, hb.build())
85
83
 
86
84
 
87
85
  class LLParse(source.Builder):
@@ -113,7 +111,7 @@ class LLParse(source.Builder):
113
111
  debug: Optional[str] = None,
114
112
  maxTableElemWidth: Optional[int] = None,
115
113
  minTableSize: Optional[int] = None,
116
- ):
114
+ ) -> Compiler:
117
115
  return Compiler(
118
116
  self.prefix,
119
117
  headerGuard,
@@ -130,8 +128,8 @@ class LLParse(source.Builder):
130
128
  maxTableElemWidth: Optional[int] = None,
131
129
  minTableSize: Optional[int] = None,
132
130
  header_name: Optional[str] = None,
133
- override_llparse_name:bool = False
134
- ):
131
+ override_llparse_name: bool = False,
132
+ ) -> CompilerResult:
135
133
  """Builds Graph and then compiles the data into C code , returns with the header and C file inside of a Dataclass"""
136
134
 
137
135
  compiler = Compiler(
@@ -142,8 +140,12 @@ class LLParse(source.Builder):
142
140
  minTableSize if minTableSize else DEFAULT_MIN_TABLE_SIZE,
143
141
  )
144
142
 
145
- return compiler.compile(root, self.properties(), header_name=header_name, override_llparse_name=override_llparse_name)
146
-
143
+ return compiler.compile(
144
+ root,
145
+ self.properties(),
146
+ header_name=header_name,
147
+ override_llparse_name=override_llparse_name,
148
+ )
147
149
 
148
150
  def to_frontend(
149
151
  self,
@@ -152,7 +154,7 @@ class LLParse(source.Builder):
152
154
  debug: Optional[str] = None,
153
155
  maxTableElemWidth: Optional[int] = None,
154
156
  minTableSize: Optional[int] = None,
155
- ):
157
+ ) -> Compiler:
156
158
  """Used as an external hack to get access to the frontend of llparse and extract
157
159
  it's contents to compile the libraries you make other things like cython, This is not in llparse
158
160
  specifically (Yet...)"""
@@ -0,0 +1,7 @@
1
+ from ..pybuilder.builder import *
2
+ from ..pybuilder.loopchecker import *
3
+ from ..pybuilder.main_code import ( # I'll add more soon I feel a little lazy at the moment.
4
+ Int,
5
+ Match,
6
+ Node,
7
+ )
@@ -1,10 +1,9 @@
1
1
  from typing import Literal, Optional, Union
2
+
2
3
  from ..pybuilder import main_code as code
3
4
 
4
5
  # typehinting node and code (TODO: Vizonex) Lets seperate the modules soon...
5
6
  node = code
6
- # from pydot import graph_from_dot_data
7
-
8
7
 
9
8
  i8 = "i8"
10
9
  i16 = "i16"
@@ -118,15 +118,14 @@ class Reachability:
118
118
  def build(self, root: Node) -> list[Node]:
119
119
  res: set[Node] = set()
120
120
  queue = [root]
121
- while len(queue) != 0:
121
+ while queue:
122
122
  node = queue.pop()
123
123
  if node in res:
124
124
  continue
125
125
  res.add(node)
126
126
  for edge in node:
127
127
  queue.append(edge.node)
128
- otherwise = node.getOtherwiseEdge()
129
- if otherwise:
128
+ if otherwise := node.getOtherwiseEdge():
130
129
  queue.append(otherwise.node)
131
130
 
132
131
  # Reverse the order so that we always
@@ -196,7 +196,6 @@ class Node:
196
196
  return res
197
197
  else:
198
198
  # Concate DO NOT ADD TO RES!!!!
199
-
200
199
  return res + [self.otherwiseEdge]
201
200
 
202
201
  def __iter__(self):
@@ -601,20 +600,3 @@ class Reachability:
601
600
  queue.append(otherwise.node)
602
601
 
603
602
  return list(res)
604
-
605
-
606
- # On_User_Key = Match("On_User_Key")
607
-
608
- # On_User_Value = Match("On_User_Value")
609
-
610
- # data_extraction = Node("On_Data_Extraction").skipTo(On_User_Key)
611
-
612
- # node = Match("On_Level_Comment").skipTo(data_extraction)
613
-
614
-
615
- # On_User_Key.match(["0","1","2","3","4","5","6","7","8","9"],On_User_Key)\
616
- # .peek("~",On_User_Value)\
617
- # .otherwise(Error(1,"[BAD KEY] Some retard decided to hijack your server! Oh SHIT!"))
618
-
619
-
620
- # print([d.node.name for d in node.getAllEdges()])
@@ -0,0 +1,3 @@
1
+ from . import code as code
2
+ from . import nodes as node
3
+
@@ -89,7 +89,11 @@ class IsEqual(FieldValue):
89
89
  class Operator(FieldValue):
90
90
  def __init__(self, name: str, field: str, value: int, op: str):
91
91
  super().__init__(
92
- "match", f"is_{field}_{name}_{toCacheKey(value)}", f'{name}_{toCacheKey(value)}', field, value
92
+ "match",
93
+ f"is_{field}_{name}_{toCacheKey(value)}",
94
+ f"{name}_{toCacheKey(value)}",
95
+ field,
96
+ value,
93
97
  )
94
98
  self.op = op
95
99
 
@@ -1,7 +1,7 @@
1
- from ..pyfront import front as code
1
+ from . import code as code
2
2
  from ..pyfront import nodes as node
3
3
  from ..pyfront import transform
4
- from ..pyfront.front import IWrap
4
+ from .code import IWrap
5
5
 
6
6
 
7
7
  class INodeImplementation:
@@ -37,7 +37,7 @@ class INodeImplementation:
37
37
 
38
38
  def TableLookup(self, n: node.TableLookup):
39
39
  return IWrap(n)
40
-
40
+
41
41
  def Int(self, n: node.Int):
42
42
  return IWrap(n)
43
43
 
@@ -93,7 +93,7 @@ class ICodeImplementation:
93
93
 
94
94
  def Value(self, c: code.Value):
95
95
  return IWrap(c)
96
-
96
+
97
97
  def Operator(self, c: code.Operator):
98
98
  return IWrap(c)
99
99
 
@@ -0,0 +1,9 @@
1
+ from ..pyfront import transform as transform
2
+ from ..pyfront import nodes as node
3
+ from ..pyfront import code as code
4
+
5
+ __all__ = (
6
+ "node",
7
+ "code",
8
+ "transform"
9
+ )
@@ -1,7 +1,7 @@
1
1
  from dataclasses import dataclass, field
2
- from typing import Any, Optional, Callable
2
+ from typing import Callable, Optional
3
3
 
4
- from ..pyfront.front import Code, IWrap, Span, SpanField
4
+ from .code import Code, IWrap, Span, SpanField
5
5
  from ..pyfront.transform import Transform
6
6
 
7
7
 
@@ -38,9 +38,6 @@ class IUniqueName:
38
38
  name: str
39
39
  originalName: str
40
40
 
41
- # def __hash__(self):
42
- # return hash(self.name)
43
-
44
41
 
45
42
  @dataclass
46
43
  class IOtherwiseEdge:
@@ -153,12 +150,13 @@ class Pause(Error):
153
150
  super().__init__(id, code, reason)
154
151
 
155
152
 
156
- # Not in llparse node-js (yet) But I wanted to implement
157
- # this into my version since I am making a very important
153
+ # Not in llparse node-js (yet) But I wanted to implement
154
+ # this into my version since I am making a very important
158
155
  # http2 frame parser
159
156
 
160
157
  # SEE: https://github.com/nodejs/llparse-frontend/pull/1
161
158
 
159
+
162
160
  @dataclass
163
161
  class Int(Node):
164
162
  field: str
@@ -171,8 +169,6 @@ class Int(Node):
171
169
  return hash(self.id)
172
170
 
173
171
 
174
-
175
-
176
172
  @dataclass
177
173
  class ISeqEdge:
178
174
  node: IWrap[Node]
@@ -264,4 +260,3 @@ class TableLookup(Match):
264
260
  for e in self.privEdges:
265
261
  yield Slot(e.node, lambda value: setattr(e, "node", value))
266
262
  yield from super().buildSlots()
267
-
@@ -0,0 +1,47 @@
1
+ from .code import IWrap
2
+ from ..pyfront.nodes import Empty, Node
3
+
4
+ WrapNode = IWrap[Node]
5
+ WrapList = list[WrapNode]
6
+
7
+
8
+ # TODO (Vizonex) Make peephole into 2 seperate functions instead of a class to
9
+ # Optimize everything down further...
10
+ class Peephole:
11
+ def optimize(self, root: WrapNode, nodes: WrapList) -> WrapNode:
12
+ changed = set(nodes)
13
+
14
+ while changed:
15
+ previous = changed.copy()
16
+ changed.clear()
17
+
18
+ for node in previous:
19
+ # Combined 2 functions from node-js llparse to
20
+ # just needing 1 this refactoring change allows for more speed. and less costly calls..
21
+ altered = False
22
+
23
+ for slot in node.ref.getSlots():
24
+ if (
25
+ not isinstance(slot.node.ref, Empty)
26
+ or not slot.node.ref.otherwise
27
+ ):
28
+ continue
29
+
30
+ otherwise = slot.node.ref.otherwise
31
+
32
+ # Node skips so we cannot optimize
33
+ if not otherwise.noAdvance:
34
+ continue
35
+
36
+ slot.node.ref = otherwise.node.ref
37
+ altered = True
38
+
39
+ if altered:
40
+ changed.add(node)
41
+
42
+ while isinstance(root.ref, Empty):
43
+ if not root.ref.otherwise or not root.ref.otherwise.noAdvance:
44
+ break
45
+ root = root.ref.otherwise.node
46
+
47
+ return root
@@ -1,4 +1,3 @@
1
- from collections import OrderedDict
2
1
  from dataclasses import dataclass, field
3
2
  from typing import Union
4
3
 
@@ -8,7 +7,7 @@ from .pybuilder.main_code import Node, Reachability, Span, SpanEnd, SpanStart
8
7
  SpanSet = set[Span]
9
8
 
10
9
 
11
- def _id(node: Union[SpanStart, SpanEnd]):
10
+ def _id(node: Union[SpanStart, SpanEnd]) -> Span:
12
11
  return node.span
13
12
 
14
13
 
@@ -29,6 +28,12 @@ class ISpanAllocatorResult:
29
28
 
30
29
 
31
30
  class SpanAllocator:
31
+ __slots__ = (
32
+ "_mx",
33
+ "_colors",
34
+ "_overlapMap"
35
+ )
36
+
32
37
  def __init__(self) -> None:
33
38
  return
34
39
 
@@ -70,8 +75,8 @@ class SpanAllocator:
70
75
  if span not in spans:
71
76
  raise Error(f'unmatched span end for "{span.callback.name}"')
72
77
 
73
- def computeActive(self, nodes: list[Node]):
74
- activeMap: dict[Node, SpanSet] = dict()
78
+ def computeActive(self, nodes: list[Node]) -> ISpanActiveInfo:
79
+ activeMap: dict[Node, SpanSet] = {}
75
80
  for node in nodes:
76
81
  activeMap[node] = set()
77
82
 
@@ -110,18 +115,18 @@ class SpanAllocator:
110
115
 
111
116
  return ISpanActiveInfo(active=activeMap, spans=list(spans))
112
117
 
113
- def computeOverlap(self, info: ISpanActiveInfo):
118
+ def computeOverlap(self, info: ISpanActiveInfo) -> dict[Span, set[Span]]:
114
119
  active = info.active
115
120
 
116
121
  overlap: dict[Span, set[Span]] = {span: set() for span in info.spans}
117
- for _, spans in active.items():
122
+ for spans in active.values():
118
123
  for one in spans:
119
124
  for other in spans:
120
125
  if other != one:
121
126
  overlap[one].add(other)
122
127
  return overlap
123
128
 
124
- def _allocate(self, span: Span):
129
+ def _allocate(self, span: Span) -> int:
125
130
  if span in self._colors:
126
131
  return self._colors[span]
127
132
 
@@ -139,7 +144,7 @@ class SpanAllocator:
139
144
  self._colors[span] = i
140
145
  return i
141
146
 
142
- def color(self, spans: list[Span], overlapDict: SpanOverlap):
147
+ def color(self, spans: list[Span], overlapDict: SpanOverlap) -> ISpanAllocatorResult:
143
148
  # Used _max instead of max because max() is an api called function needed in a bit...
144
149
  self._mx = -1
145
150
  self._colors: dict[Span, int] = {}
@@ -148,10 +153,10 @@ class SpanAllocator:
148
153
 
149
154
  colors = {span: self._allocate(span) for span in spans}
150
155
 
151
- concurrency = list()
152
- for _ in range(self._mx + 1):
153
- # NOTE : concurrency[i] = [] doesn't work but this does :P
154
- concurrency.append([])
156
+ concurrency:list[list[Span]] = [[] for _ in range(self._mx + 1)]
157
+ # for _ in range(self._mx + 1):
158
+ # # NOTE : concurrency[i] = [] doesn't work but this does :P
159
+ # concurrency.append([])
155
160
 
156
161
  for s in sorted(spans, key=lambda s: s.callback.name):
157
162
  concurrency[self._allocate(s)].append(s)
@@ -74,11 +74,10 @@ class Trie:
74
74
  first = edges[0].key
75
75
  last = edges[-1].key
76
76
  # print("level", edges, first)
77
- if len(edges) == 1 and (len(edges[0].key) == 0):
77
+ if len(edges) == 1 and (not len(edges[0].key)):
78
78
  return TrieEmpty(edges[0].node, edges[0].value)
79
79
 
80
80
  i = 0
81
- # print(first)
82
81
  for i in range(len(first)):
83
82
  if first[i] != last[i]:
84
83
  break
@@ -91,20 +90,22 @@ class Trie:
91
90
 
92
91
  return self.single(edges, path)
93
92
 
94
- def Slice(self, edges: list[IEdge], off: int):
93
+ def slice(self, edges: list[IEdge], off: int):
95
94
  _slice = [
96
95
  IEdge(edge.key[off:], edge.node, edge.noAdvance, edge.value)
97
96
  for edge in edges
98
97
  ]
99
98
  return sorted(_slice, key=lambda k: k.key)
100
99
 
101
- def sequence(self, edges: list[IEdge], prefix: bytes, path: list[bytes]):
102
- sliced = self.Slice(edges, len(prefix))
100
+ def sequence(
101
+ self, edges: list[IEdge], prefix: bytes, path: list[bytes]
102
+ ) -> TrieSequence:
103
+ sliced = self.slice(edges, len(prefix))
103
104
  assert not any([edge.noAdvance for edge in edges])
104
105
  child = self.level(sliced, path + [prefix])
105
106
  return TrieSequence(prefix, child)
106
107
 
107
- def single(self, edges: list[IEdge], path: list[bytes]):
108
+ def single(self, edges: list[IEdge], path: list[bytes]) -> TrieSingle:
108
109
  if not len(edges[0].key):
109
110
  assert not path, f'Empty root entry at "{self.name}"'
110
111
  assert not (len(edges) == 1 or len(edges[1].key) != 0), (
@@ -112,7 +113,7 @@ class Trie:
112
113
  + (b", ".join(path).decode("utf-8"))
113
114
  + "]"
114
115
  )
115
-
116
+
116
117
  keys: dict[int, list[IEdge]] = {}
117
118
  otherwise = None
118
119
  for edge in edges:
@@ -132,7 +133,7 @@ class Trie:
132
133
  for key, subEdges in keys.items():
133
134
  # TODO LOG FUNCTION's ARGUMENTS TO DETERMINE WEATHER OR NOT IT'S the Problem...
134
135
  # I think this maybe the problem now that I think about it...
135
- sliced = self.Slice(subEdges, 1)
136
+ sliced = self.slice(subEdges, 1)
136
137
 
137
138
  subPath = path + [chr(key).encode("utf-8")]
138
139
 
@@ -146,7 +147,7 @@ class Trie:
146
147
  + "]"
147
148
  )
148
149
  raise TypeError(err)
149
-
150
+
150
151
  children.append(
151
152
  ITrieSingleChild(key, noAdvance, self.level(sliced, subPath))
152
153
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: llparse
3
- Version: 0.1.8
3
+ Version: 0.1.9
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
@@ -15,7 +15,6 @@ llparse/header.py
15
15
  llparse/llparse.py
16
16
  llparse/settings.py
17
17
  llparse/spanalloc.py
18
- llparse/test.py
19
18
  llparse/trie.py
20
19
  llparse.egg-info/PKG-INFO
21
20
  llparse.egg-info/SOURCES.txt
@@ -26,8 +25,8 @@ llparse/pybuilder/builder.py
26
25
  llparse/pybuilder/loopchecker.py
27
26
  llparse/pybuilder/main_code.py
28
27
  llparse/pybuilder/parsemap.py
29
- llparse/pyfront/containers.py
30
- llparse/pyfront/front.py
28
+ llparse/pyfront/__init__.py
29
+ llparse/pyfront/code.py
31
30
  llparse/pyfront/implementation.py
32
31
  llparse/pyfront/namespace.py
33
32
  llparse/pyfront/nodes.py
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "llparse"
3
- version = "0.1.8"
3
+ version = "0.1.9"
4
4
  description = "A Parody of llparse written for writing C Parsers with Python"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -1,8 +0,0 @@
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,33 +0,0 @@
1
- from typing import Generic, TypeVar
2
-
3
- from implementation import IImplementation
4
- from pyfront.front import IWrap, T
5
-
6
- R = TypeVar("R")
7
-
8
-
9
- class ContainerWrap(Generic[T]):
10
- def __init__(self, ref: T) -> None:
11
- self.ref = ref
12
- self.map: dict[str, IWrap[T]] = {}
13
-
14
- def get(self, R: type[R], key: str) -> IWrap[R]:
15
- """Changes and Alters the refrence to the orginal object..."""
16
-
17
- # UnPack Object to Do Conversion From Since We aren't Using Typescript...
18
- return IWrap(R(**self.map.get[key].__dict__))
19
-
20
-
21
- # TODO Vizonex Figure out how containers
22
- # should be implemented in python Spcae...
23
- # Since Interfaces and other thinsg in it
24
- # are tricky to just simply determine
25
- class Container:
26
- def __init__(self) -> None:
27
- self.map: dict[str, IImplementation] = {}
28
-
29
- def build(self):
30
- return IImplementation()
31
-
32
- def buildCode(self):
33
- return
@@ -1,3 +0,0 @@
1
- from ..pyfront import front as code
2
- from ..pyfront import nodes as node
3
- from ..pyfront import transform as transform
@@ -1,45 +0,0 @@
1
- from ..pyfront.front import IWrap
2
- from ..pyfront.nodes import Empty, Node
3
-
4
- WrapNode = IWrap[Node]
5
- WrapList = list[WrapNode]
6
-
7
-
8
- # TODO (Vizonex) Make peephole into 2 seperate functions instead of a class to
9
- # Optimize everything down further...
10
- class Peephole:
11
- def optimize(self, root: WrapNode, nodes: WrapList):
12
- changed = set(nodes)
13
-
14
- while changed:
15
- previous = changed
16
- changed = set()
17
-
18
- for node in previous:
19
- if self.optimizeNode(node):
20
- changed.add(node)
21
-
22
- while isinstance(root.ref, Empty):
23
- if not root.ref.otherwise or not root.ref.otherwise.noAdvance:
24
- break
25
- root = root.ref.otherwise.node
26
-
27
- return root
28
-
29
- def optimizeNode(self, node: WrapNode):
30
- changed = False
31
- for slot in node.ref.getSlots():
32
- # TODO Find an Actual way to check that a Node maybe empty...
33
- if not isinstance(slot.node.ref, Empty) or not slot.node.ref.otherwise:
34
- continue
35
-
36
- otherwise = slot.node.ref.otherwise
37
-
38
- # Node skips so we cannot optimize
39
- if not otherwise.noAdvance:
40
- continue
41
-
42
- slot.node.ref = otherwise.node.ref
43
-
44
- changed = True
45
- return changed
@@ -1,233 +0,0 @@
1
- from .C_compiler import Compilation, ICompilerOptions
2
- from .frontend import Frontend
3
- from .llparse import LLParse
4
- from .pybuilder import Builder
5
-
6
- # TODO: Remove and make pytest for it.
7
-
8
-
9
- def smaller_test():
10
- # from compilator import Compilation, ICompilerOptions
11
- p = Builder()
12
- simple = p.node("simple")
13
- complete = p.invoke(
14
- p.code.match("On_Complete"), {0: simple}, p.error(1, "On_Complete FAILED!")
15
- )
16
-
17
- simple.peek("\n", complete).skipTo(simple)
18
-
19
- front = Frontend("http_parser")
20
- f = front.compile(simple, properties=p.properties)
21
- comp = Compilation(
22
- "http_parser", f.properties, f.resumptionTargets, ICompilerOptions(None, None)
23
- )
24
- # for r in comp.resumptionTargets:
25
- # print(r)
26
- out = []
27
- root = comp.unwrapNode(f.root)
28
- root.build(comp)
29
- comp.reserveSpans(f.spans)
30
- comp.buildGlobals(out)
31
- comp.buildResumptionStates(out)
32
- print(comp.stateDict)
33
- # print("\n".join(out))
34
-
35
-
36
- def test1():
37
- # I will be using indutny's mini http parser to demonstrate as it's
38
- # medium sized and uses all major tricks , bells and whistles...
39
-
40
- p = Builder()
41
- method = p.node("method")
42
- beforeUrl = p.node("before_url")
43
- urlSpan = p.span(p.code.span("on_url"))
44
- url = p.node("url")
45
- http = p.node("http")
46
-
47
- # print(sliced)(method)
48
-
49
- # // Add custom uint8_t property to the state
50
- p.property("i8", "method")
51
-
52
- # Store method inside a custom property
53
- onMethod = p.invoke(p.code.store("method"), beforeUrl)
54
-
55
- # Invoke custom C function
56
- complete = p.invoke(
57
- p.code.match("on_complete"),
58
- {
59
- # Restart
60
- 0: method
61
- },
62
- p.error(4, "`on_complete` error"),
63
- )
64
-
65
- method.select(
66
- {
67
- "HEAD": 0,
68
- "GET": 1,
69
- "POST": 2,
70
- "PUT": 3,
71
- "DELETE": 4,
72
- "OPTIONS": 5,
73
- "CONNECT": 6,
74
- "TRACE": 7,
75
- "PATCH": 8,
76
- },
77
- onMethod,
78
- ).otherwise(p.error(5, "Expected method"))
79
-
80
- beforeUrl.match(" ", beforeUrl).otherwise(urlSpan.start(url))
81
-
82
- url.peek(" ", urlSpan.end(http)).skipTo(url)
83
-
84
- http.match(" HTTP/1.1\r\n\r\n", complete).otherwise(
85
- p.error(6, "Expected HTTP/1.1 and two newlines")
86
- )
87
-
88
- front = Frontend("http_parser")
89
- f = front.compile(
90
- method,
91
- properties=p.properties,
92
- )
93
- # r = f.root.ref.id.name
94
- # NOTE Check that root was addded , this can be a problem laster down the line if it isn't a resumption target...
95
- assert f.root in f.resumptionTargets
96
- comp = Compilation(
97
- "http_parser", p.properties(), f.resumptionTargets, ICompilerOptions()
98
- )
99
- root = comp.unwrapNode(f.root)
100
- root.build(comp)
101
-
102
- assert "s_n_http_parser__n_method" in comp.resumptionTargets
103
- out = []
104
- comp.reserveSpans(f.spans)
105
- comp.buildGlobals(out)
106
- out.append("")
107
- out.append("/*--RESUMPTION STATES--*/")
108
- comp.buildResumptionStates(out)
109
- out.append("/*--INTERNAL STATES--*/")
110
- comp.buildInternalStates(out)
111
- # for k , v in comp.stateDict.items():
112
- # print(k)
113
- print("\n".join(out))
114
-
115
-
116
- def test2():
117
- from llparse import LLParse
118
-
119
- p = LLParse("http_parser")
120
- method = p.node("method")
121
- beforeUrl = p.node("before_url")
122
- urlSpan = p.span(p.code.span("on_url"))
123
- url = p.node("url")
124
- http = p.node("http")
125
-
126
- # Add custom uint8_t property to the state
127
- p.property("i8", "method")
128
-
129
- # Store method inside a custom property
130
- onMethod = p.invoke(p.code.store("method"), beforeUrl)
131
-
132
- # Invoke custom C function
133
- complete = p.invoke(
134
- p.code.match("on_complete"),
135
- {
136
- # Restart
137
- 0: method
138
- },
139
- p.error(4, "`on_complete` error"),
140
- )
141
-
142
- method.select(
143
- {
144
- "HEAD": 0,
145
- "GET": 1,
146
- "POST": 2,
147
- "PUT": 3,
148
- "DELETE": 4,
149
- "OPTIONS": 5,
150
- "CONNECT": 6,
151
- "TRACE": 7,
152
- "PATCH": 8,
153
- },
154
- onMethod,
155
- ).otherwise(p.error(5, "Expected method"))
156
-
157
- beforeUrl.match(" ", beforeUrl).otherwise(urlSpan.start(url))
158
-
159
- url.peek(" ", urlSpan.end(http)).skipTo(url)
160
-
161
- http.match(" HTTP/1.1\r\n\r\n", complete).otherwise(
162
- p.error(6, "Expected HTTP/1.1 and two newlines")
163
- )
164
-
165
- c = p.build(method)
166
- print(c.c)
167
- open("http_parser.c", "w").write(c.c)
168
- open("http_parser.h", "w").write(c.header)
169
- # # sp_alloc = SpanAllocator().allocate(method)
170
-
171
- # TODO Validate Moving parts as they should be compiled correctly...
172
-
173
-
174
- def test3():
175
- """Moving Part Validations..."""
176
- # A~Sample~Of~Dual Spans...
177
- p = LLParse("dual_spans")
178
-
179
- span_a = p.span(p.code.span("on_a"))
180
- span_b = p.span(p.code.span("on_b"))
181
-
182
- a = p.node("a_node")
183
- b = p.node("b_node")
184
-
185
- start = p.node("start")
186
- start.otherwise(span_a.start(a))
187
-
188
- a.peek("~", span_a.end().skipTo(span_b.start(b))).skipTo(a)
189
- b.peek("~", span_b.end().skipTo(span_a.start(a))).skipTo(b)
190
-
191
- compiled = p.build(start)
192
- print(compiled.c)
193
-
194
-
195
- def test4():
196
- """Same Dual Spans but with flags..."""
197
- # A~Sample~Of~Dual Spans...
198
-
199
- p = LLParse("dual_spans")
200
-
201
- # Flag
202
- p.property("i8", "flag")
203
-
204
- span_a = p.span(p.code.span("on_a"))
205
- span_b = p.span(p.code.span("on_b"))
206
-
207
- a = p.node("a_node")
208
- b = p.node("b_node")
209
-
210
- # Invoke Updates to Flag
211
- # 1 is a , 2 is b
212
-
213
- update_a = p.invoke(p.code.update("flag", 1), span_a.start(a))
214
- update_b = p.invoke(p.code.update("flag", 2), span_b.start(b))
215
- start = p.node("start")
216
- start.otherwise(update_a)
217
-
218
- a.peek("~", span_a.end().skipTo(update_a)).skipTo(a)
219
- b.peek("~", span_b.end().skipTo(update_b)).skipTo(b)
220
-
221
- compiled = p.build(start)
222
- print(compiled.c)
223
-
224
-
225
- # TODO Try to Throw more complicated and advanced things at pyllparse to try and solve
226
- # This will make the code sturdier and more mature as time goes on
227
- # Make a Test Bag on github for all of these tests to go into incase a single file gets too big
228
-
229
- # I encourage anyone to find valid compiled llparse sequences and then translate them over to
230
- # python to testrun , I will try to make a validator tool whenever I can get to it - Vizonex
231
-
232
- if __name__ == "__main__":
233
- test2()
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