llparse 0.1.2__tar.gz → 0.1.4__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.
- {llparse-0.1.2 → llparse-0.1.4}/PKG-INFO +7 -4
- {llparse-0.1.2 → llparse-0.1.4}/README.md +6 -3
- {llparse-0.1.2 → llparse-0.1.4}/llparse/compilator.py +169 -8
- {llparse-0.1.2 → llparse-0.1.4}/llparse/debug.py +0 -3
- {llparse-0.1.2 → llparse-0.1.4}/llparse/frontend.py +79 -60
- {llparse-0.1.2 → llparse-0.1.4}/llparse/llparse.py +12 -2
- llparse-0.1.4/llparse/pybuilder/__init__.py +8 -0
- {llparse-0.1.2 → llparse-0.1.4}/llparse/pybuilder/builder.py +37 -1
- {llparse-0.1.2 → llparse-0.1.4}/llparse/pybuilder/main_code.py +35 -0
- {llparse-0.1.2 → llparse-0.1.4}/llparse/pyfront/front.py +0 -3
- {llparse-0.1.2 → llparse-0.1.4}/llparse/pyfront/implementation.py +3 -0
- {llparse-0.1.2 → llparse-0.1.4}/llparse/pyfront/nodes.py +33 -18
- {llparse-0.1.2 → llparse-0.1.4}/llparse/pyfront/peephole.py +1 -1
- {llparse-0.1.2 → llparse-0.1.4}/llparse/trie.py +10 -8
- {llparse-0.1.2 → llparse-0.1.4}/llparse.egg-info/PKG-INFO +7 -4
- {llparse-0.1.2 → llparse-0.1.4}/llparse.egg-info/SOURCES.txt +1 -0
- {llparse-0.1.2 → llparse-0.1.4}/pyproject.toml +1 -1
- llparse-0.1.4/tests/test_frontend.py +35 -0
- llparse-0.1.2/llparse/pybuilder/__init__.py +0 -2
- {llparse-0.1.2 → llparse-0.1.4}/LICENSE +0 -0
- {llparse-0.1.2 → llparse-0.1.4}/llparse/C_compiler.py +0 -0
- {llparse-0.1.2 → llparse-0.1.4}/llparse/__init__.py +0 -0
- {llparse-0.1.2 → llparse-0.1.4}/llparse/constants.py +0 -0
- {llparse-0.1.2 → llparse-0.1.4}/llparse/cython_builder.py +0 -0
- {llparse-0.1.2 → llparse-0.1.4}/llparse/dot.py +0 -0
- {llparse-0.1.2 → llparse-0.1.4}/llparse/enumerator.py +0 -0
- {llparse-0.1.2 → llparse-0.1.4}/llparse/errors.py +0 -0
- {llparse-0.1.2 → llparse-0.1.4}/llparse/header.py +0 -0
- {llparse-0.1.2 → llparse-0.1.4}/llparse/pybuilder/loopchecker.py +0 -0
- {llparse-0.1.2 → llparse-0.1.4}/llparse/pybuilder/parsemap.py +0 -0
- {llparse-0.1.2 → llparse-0.1.4}/llparse/pyfront/containers.py +0 -0
- {llparse-0.1.2 → llparse-0.1.4}/llparse/pyfront/namespace.py +0 -0
- {llparse-0.1.2 → llparse-0.1.4}/llparse/pyfront/transform.py +0 -0
- {llparse-0.1.2 → llparse-0.1.4}/llparse/settings.py +0 -0
- {llparse-0.1.2 → llparse-0.1.4}/llparse/spanalloc.py +0 -0
- {llparse-0.1.2 → llparse-0.1.4}/llparse/test.py +0 -0
- {llparse-0.1.2 → llparse-0.1.4}/llparse.egg-info/dependency_links.txt +0 -0
- {llparse-0.1.2 → llparse-0.1.4}/llparse.egg-info/top_level.txt +0 -0
- {llparse-0.1.2 → llparse-0.1.4}/setup.cfg +0 -0
- {llparse-0.1.2 → llparse-0.1.4}/tests/test_loop_checker.py +0 -0
- {llparse-0.1.2 → llparse-0.1.4}/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
|
+
Version: 0.1.4
|
|
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,7 @@ Dynamic: license-file
|
|
|
15
15
|
|
|
16
16
|
A python parody of the typescript library llparse.
|
|
17
17
|
|
|
18
|
-
I take no credit for the orginal work done by indutny and I was originally very nervous about making
|
|
18
|
+
I take no credit for the orginal work done by indutny and the other node-js contributors involved and I was originally very nervous about making
|
|
19
19
|
this python library that I made public...
|
|
20
20
|
|
|
21
21
|
Links to the original library
|
|
@@ -49,7 +49,7 @@ do it at your own risk. They maybe incompleted or not throughly stress-tested.
|
|
|
49
49
|
|
|
50
50
|
## New Features
|
|
51
51
|
- Throw me an issue if typescript llparse introduces something new that you want for me or another contributor to try and implement
|
|
52
|
-
just seeing llparse add new features is
|
|
52
|
+
just seeing llparse add new features is exciting to me.
|
|
53
53
|
|
|
54
54
|
- If you want a feature that typescript llparse doesn't have, be sure to try making a pull request over there as well and not just here,
|
|
55
55
|
there's a good chance they will appericate you for helping over there too and your helping make llhttp better by doing so. :)
|
|
@@ -62,7 +62,7 @@ there's a good chance they will appericate you for helping over there too and yo
|
|
|
62
62
|
- Make it easy for me or someone else to find a problem and solve it in typescript after testing it in python
|
|
63
63
|
- Typescript takes 2 commands to run a script with node-js it while python only takes one cutting the time required tremendously...
|
|
64
64
|
- The orginal project was MIT licensed.
|
|
65
|
-
- I wanted to write my own C
|
|
65
|
+
- I wanted to write my own C Parser tool with llhttp styled callbacks of my own using a language I was the most comfortable with using.
|
|
66
66
|
- I didn't like __Lemon Parser__ or __Yacc__ all that much and a good ide for handling them in Visual Studio Code with error checking to my knowlegde does not exist.
|
|
67
67
|
- The closest thing I got to what I wanted was a project named __NMFU__ shorthand for no memory for you and even I had problems with writing things using that library...
|
|
68
68
|
|
|
@@ -131,3 +131,6 @@ print(c.c)
|
|
|
131
131
|
open("http_parser.c", "w").write(c.c)
|
|
132
132
|
open("http_parser.h", "w").write(c.header)
|
|
133
133
|
```
|
|
134
|
+
|
|
135
|
+
## Video Showcasing this library
|
|
136
|
+
- https://youtu.be/YQOzJ2BghQw
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
A python parody of the typescript library llparse.
|
|
7
7
|
|
|
8
|
-
I take no credit for the orginal work done by indutny and I was originally very nervous about making
|
|
8
|
+
I take no credit for the orginal work done by indutny and the other node-js contributors involved and I was originally very nervous about making
|
|
9
9
|
this python library that I made public...
|
|
10
10
|
|
|
11
11
|
Links to the original library
|
|
@@ -39,7 +39,7 @@ do it at your own risk. They maybe incompleted or not throughly stress-tested.
|
|
|
39
39
|
|
|
40
40
|
## New Features
|
|
41
41
|
- Throw me an issue if typescript llparse introduces something new that you want for me or another contributor to try and implement
|
|
42
|
-
just seeing llparse add new features is
|
|
42
|
+
just seeing llparse add new features is exciting to me.
|
|
43
43
|
|
|
44
44
|
- If you want a feature that typescript llparse doesn't have, be sure to try making a pull request over there as well and not just here,
|
|
45
45
|
there's a good chance they will appericate you for helping over there too and your helping make llhttp better by doing so. :)
|
|
@@ -52,7 +52,7 @@ there's a good chance they will appericate you for helping over there too and yo
|
|
|
52
52
|
- Make it easy for me or someone else to find a problem and solve it in typescript after testing it in python
|
|
53
53
|
- Typescript takes 2 commands to run a script with node-js it while python only takes one cutting the time required tremendously...
|
|
54
54
|
- The orginal project was MIT licensed.
|
|
55
|
-
- I wanted to write my own C
|
|
55
|
+
- I wanted to write my own C Parser tool with llhttp styled callbacks of my own using a language I was the most comfortable with using.
|
|
56
56
|
- I didn't like __Lemon Parser__ or __Yacc__ all that much and a good ide for handling them in Visual Studio Code with error checking to my knowlegde does not exist.
|
|
57
57
|
- The closest thing I got to what I wanted was a project named __NMFU__ shorthand for no memory for you and even I had problems with writing things using that library...
|
|
58
58
|
|
|
@@ -121,3 +121,6 @@ print(c.c)
|
|
|
121
121
|
open("http_parser.c", "w").write(c.c)
|
|
122
122
|
open("http_parser.h", "w").write(c.header)
|
|
123
123
|
```
|
|
124
|
+
|
|
125
|
+
## Video Showcasing this library
|
|
126
|
+
- https://youtu.be/YQOzJ2BghQw
|
|
@@ -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};")
|
|
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
|
|
|
@@ -572,7 +573,7 @@ class Single(Node):
|
|
|
572
573
|
else:
|
|
573
574
|
ch = f"'{chr(e.key)}'"
|
|
574
575
|
|
|
575
|
-
out.append(f" case {ch}:" + "{")
|
|
576
|
+
out.append(f" case {ch}: " + "{")
|
|
576
577
|
tmp: list[str] = []
|
|
577
578
|
|
|
578
579
|
# For now debug everything....
|
|
@@ -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
|
-
|
|
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__}"'
|
|
@@ -12,11 +12,15 @@ 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
|
|
15
|
+
from .trie import Trie, TrieEmpty, TrieNode, TrieSequence, TrieSingle, ITrieSingleChild
|
|
16
16
|
|
|
17
17
|
DEFAULT_MIN_TABLE_SIZE = 32
|
|
18
18
|
DEFAULT_MAX_TABLE_WIDTH = 4
|
|
19
19
|
|
|
20
|
+
from logging import getLogger
|
|
21
|
+
|
|
22
|
+
log = getLogger("llparse.frontend")
|
|
23
|
+
|
|
20
24
|
|
|
21
25
|
WrappedNode = IWrap[_frontend.node.Node]
|
|
22
26
|
WrappedCode = IWrap[_frontend.code.Code]
|
|
@@ -166,7 +170,7 @@ class Frontend:
|
|
|
166
170
|
trieNode = trie.build(list(node))
|
|
167
171
|
|
|
168
172
|
if not trieNode:
|
|
169
|
-
|
|
173
|
+
log.debug("TrieNode was nonexistant")
|
|
170
174
|
return self.implementation.node.Empty(
|
|
171
175
|
_frontend.node.Empty(self.Id.id(node.name))
|
|
172
176
|
)
|
|
@@ -178,7 +182,7 @@ class Frontend:
|
|
|
178
182
|
|
|
179
183
|
return children
|
|
180
184
|
|
|
181
|
-
def registerNode(self, node: WrappedNode):
|
|
185
|
+
def registerNode(self, node: WrappedNode) -> None:
|
|
182
186
|
# NOTE NO Implementations required here since this is python!
|
|
183
187
|
if isinstance(
|
|
184
188
|
node.ref,
|
|
@@ -207,7 +211,7 @@ class Frontend:
|
|
|
207
211
|
result = nodeImpl.Error(_frontend.node.Error(ID(), node.code, node.reason))
|
|
208
212
|
|
|
209
213
|
elif isinstance(node, source.code.Pause):
|
|
210
|
-
result = nodeImpl.Pause(_frontend.node.
|
|
214
|
+
result = nodeImpl.Pause(_frontend.node.Pause(ID(), node.code, node.reason))
|
|
211
215
|
|
|
212
216
|
elif isinstance(node, source.code.Comsume):
|
|
213
217
|
result = nodeImpl.Consume(_frontend.node.Consume(ID(), node.field))
|
|
@@ -240,6 +244,10 @@ class Frontend:
|
|
|
240
244
|
|
|
241
245
|
elif isinstance(node, source.code.Match):
|
|
242
246
|
result = self.translateMatch(node)
|
|
247
|
+
|
|
248
|
+
elif isinstance(node, source.node.Int):
|
|
249
|
+
result = self.translateInt(node)
|
|
250
|
+
|
|
243
251
|
else:
|
|
244
252
|
raise Exception(f'Unknown Node Type for :"{node.name}" {type(node)}')
|
|
245
253
|
|
|
@@ -247,24 +255,26 @@ class Frontend:
|
|
|
247
255
|
|
|
248
256
|
if isinstance(result, list):
|
|
249
257
|
# result:list[WrappedNode]
|
|
250
|
-
assert isinstance(node, source.code.Match)
|
|
251
|
-
_match = node
|
|
252
258
|
|
|
253
|
-
|
|
254
|
-
|
|
259
|
+
assert isinstance(node, (source.code.Match, source.node.Int))
|
|
260
|
+
_match = node
|
|
261
|
+
|
|
262
|
+
assert otherwise, (f'Node "{node.name}" has no ".otherwise()"')
|
|
255
263
|
|
|
256
|
-
|
|
264
|
+
if isinstance(node, source.node.Match):
|
|
257
265
|
for child in result:
|
|
258
266
|
if not child.ref.otherwise:
|
|
259
267
|
child.ref.setOtherwise(
|
|
260
268
|
self.translate(otherwise.node), otherwise.noAdvance
|
|
261
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)
|
|
262
274
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
child.ref.setTransform(transform)
|
|
267
|
-
|
|
275
|
+
|
|
276
|
+
else:
|
|
277
|
+
result[-1].ref.setOtherwise(self.translate(otherwise.node), otherwise.noAdvance)
|
|
268
278
|
assert len(result) >= 1
|
|
269
279
|
return result[0]
|
|
270
280
|
|
|
@@ -287,7 +297,6 @@ class Frontend:
|
|
|
287
297
|
|
|
288
298
|
if isinstance(single.ref, _frontend.node.Invoke):
|
|
289
299
|
for edge in node:
|
|
290
|
-
# print(edge.key)
|
|
291
300
|
single.ref.addEdge(
|
|
292
301
|
ord(edge.key) if isinstance(edge.key, str) else edge.key,
|
|
293
302
|
self.translate(edge.node),
|
|
@@ -296,6 +305,23 @@ class Frontend:
|
|
|
296
305
|
assert len(list(node)) == 0
|
|
297
306
|
|
|
298
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
|
+
|
|
299
325
|
|
|
300
326
|
def maybeTableLookup(
|
|
301
327
|
self, node: source.code.Match, trie: TrieSingle, children: MatchChildren
|
|
@@ -304,52 +330,48 @@ class Frontend:
|
|
|
304
330
|
return None
|
|
305
331
|
|
|
306
332
|
targets: dict[source.code.Node, ITableLookupTarget] = {}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
empty
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
bailout = False
|
|
323
|
-
continue
|
|
333
|
+
|
|
334
|
+
def check_child(child: ITrieSingleChild):
|
|
335
|
+
nonlocal targets
|
|
336
|
+
if not isinstance(child.node, TrieEmpty):
|
|
337
|
+
log.debug(
|
|
338
|
+
'non-leaf trie child of "%s" prevents table allocation' % node.name
|
|
339
|
+
)
|
|
340
|
+
return False
|
|
341
|
+
empty = child.node
|
|
342
|
+
if empty.value is not None:
|
|
343
|
+
log.debug(
|
|
344
|
+
'value passing trie leaf of "%s" prevents table allocation'
|
|
345
|
+
% node.name
|
|
346
|
+
)
|
|
347
|
+
return False
|
|
324
348
|
|
|
325
349
|
target = empty.node
|
|
326
|
-
if not targets
|
|
350
|
+
if target not in targets:
|
|
327
351
|
targets[target] = ITableLookupTarget(
|
|
328
352
|
keys=[child.key], noAdvance=child.noAdvance, trie=empty
|
|
329
353
|
)
|
|
330
|
-
|
|
331
|
-
break
|
|
354
|
+
return True
|
|
332
355
|
|
|
333
356
|
existing = targets[target]
|
|
334
|
-
|
|
335
357
|
if existing.noAdvance != child.noAdvance:
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
358
|
+
log.debug(
|
|
359
|
+
f'noAdvance mismatch in a trie leaf of "{node.name}" prevents '
|
|
360
|
+
"table allocation"
|
|
361
|
+
)
|
|
362
|
+
return False
|
|
340
363
|
existing.keys.append(child.key)
|
|
364
|
+
return True
|
|
341
365
|
|
|
342
|
-
|
|
343
|
-
bailout = True
|
|
344
|
-
break
|
|
345
|
-
|
|
346
|
-
# assert len(trie.children) == len(targets), "Something went wrong"
|
|
347
|
-
if bailout:
|
|
366
|
+
if not all([check_child(child) for child in trie.children]):
|
|
348
367
|
return
|
|
349
368
|
|
|
350
369
|
# Weave width limit for optimization...
|
|
351
|
-
if len(targets
|
|
352
|
-
|
|
370
|
+
if len(targets) >= (1 << self.options["maxTableElemWidth"]):
|
|
371
|
+
log.debug(
|
|
372
|
+
'too many different trie targets of "%s" for a table allocation'
|
|
373
|
+
% node.name
|
|
374
|
+
)
|
|
353
375
|
return
|
|
354
376
|
|
|
355
377
|
table = self.implementation.node.TableLookup(
|
|
@@ -358,11 +380,11 @@ class Frontend:
|
|
|
358
380
|
children.append(table)
|
|
359
381
|
|
|
360
382
|
# Break Loop
|
|
361
|
-
if self.Map.get(node):
|
|
383
|
+
if not self.Map.get(node):
|
|
362
384
|
self.Map[node] = table
|
|
363
385
|
|
|
364
386
|
for target in targets.values():
|
|
365
|
-
_next = self.translateTrie(node, target, children)
|
|
387
|
+
_next = self.translateTrie(node, target.trie, children)
|
|
366
388
|
table.ref.addEdge(
|
|
367
389
|
ITableEdge(keys=target.keys, noAdvance=target.noAdvance, node=_next)
|
|
368
390
|
)
|
|
@@ -408,9 +430,7 @@ class Frontend:
|
|
|
408
430
|
self, node: source.code.Match, trie: TrieSingle, children: MatchChildren
|
|
409
431
|
):
|
|
410
432
|
# Check if Tablelookup could be a valid option to Optimze our code up...
|
|
411
|
-
maybeTable
|
|
412
|
-
|
|
413
|
-
if maybeTable:
|
|
433
|
+
if maybeTable := self.maybeTableLookup(node, trie, children):
|
|
414
434
|
return maybeTable
|
|
415
435
|
|
|
416
436
|
single = self.implementation.node.Single(
|
|
@@ -432,8 +452,7 @@ class Frontend:
|
|
|
432
452
|
value=child.node.value if isinstance(child.node, TrieEmpty) else None,
|
|
433
453
|
)
|
|
434
454
|
|
|
435
|
-
otherwise
|
|
436
|
-
if otherwise:
|
|
455
|
+
if otherwise := trie.otherwise:
|
|
437
456
|
single.ref.setOtherwise(
|
|
438
457
|
self.translateTrie(node, otherwise, children), True, otherwise.value
|
|
439
458
|
)
|
|
@@ -442,8 +461,9 @@ class Frontend:
|
|
|
442
461
|
def translateSpanCode(self, code: source.code._Span):
|
|
443
462
|
return self.translateCode(code)
|
|
444
463
|
|
|
445
|
-
|
|
446
|
-
|
|
464
|
+
def translateCode(
|
|
465
|
+
self, code: source.code.Code
|
|
466
|
+
):
|
|
447
467
|
"""Translates Builder Classes to Frontend Classes..."""
|
|
448
468
|
|
|
449
469
|
prefixed = self.codeId.id(code.name).name
|
|
@@ -499,9 +519,8 @@ class Frontend:
|
|
|
499
519
|
else:
|
|
500
520
|
raise Exception(f'UnSupported code:"{code.name}" type: "{type(code)}"')
|
|
501
521
|
|
|
502
|
-
if self.codeCache.get(res.ref.cacheKey):
|
|
503
|
-
return
|
|
504
|
-
|
|
522
|
+
if _res := self.codeCache.get(res.ref.cacheKey):
|
|
523
|
+
return _res
|
|
505
524
|
self.codeCache[res.ref.cacheKey] = res
|
|
506
525
|
return res
|
|
507
526
|
|
|
@@ -67,12 +67,20 @@ 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
71
|
):
|
|
71
72
|
"""Creates the C and header file..."""
|
|
72
73
|
info = self.to_frontend(root, properties, Impl)
|
|
73
74
|
hb = HeaderBuilder(self.prefix, self.headerGuard, properties, info.spans)
|
|
75
|
+
cdata = CCompiler(header_name, self.debug).compile(info)
|
|
76
|
+
if override_llparse_name:
|
|
77
|
+
# sometimes users want to combine parsers together when compiling with C
|
|
78
|
+
# to make up for conflicts with other parsers example: llhttp
|
|
79
|
+
# there should be a fair way of compiling everything.
|
|
80
|
+
cdata = cdata.replace('llparse', self.prefix)
|
|
81
|
+
|
|
74
82
|
return CompilerResult(
|
|
75
|
-
|
|
83
|
+
cdata , hb.build()
|
|
76
84
|
)
|
|
77
85
|
|
|
78
86
|
|
|
@@ -122,6 +130,7 @@ class LLParse(source.Builder):
|
|
|
122
130
|
maxTableElemWidth: Optional[int] = None,
|
|
123
131
|
minTableSize: Optional[int] = None,
|
|
124
132
|
header_name: Optional[str] = None,
|
|
133
|
+
override_llparse_name:bool = False
|
|
125
134
|
):
|
|
126
135
|
"""Builds Graph and then compiles the data into C code , returns with the header and C file inside of a Dataclass"""
|
|
127
136
|
|
|
@@ -133,7 +142,8 @@ class LLParse(source.Builder):
|
|
|
133
142
|
minTableSize if minTableSize else DEFAULT_MIN_TABLE_SIZE,
|
|
134
143
|
)
|
|
135
144
|
|
|
136
|
-
return compiler.compile(root, self.properties(), header_name=header_name)
|
|
145
|
+
return compiler.compile(root, self.properties(), header_name=header_name, override_llparse_name=override_llparse_name)
|
|
146
|
+
|
|
137
147
|
|
|
138
148
|
def to_frontend(
|
|
139
149
|
self,
|
|
@@ -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"]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from dataclasses import dataclass, field
|
|
2
|
-
from typing import Any, Optional
|
|
2
|
+
from typing import Any, Optional, Callable
|
|
3
3
|
|
|
4
4
|
from ..pyfront.front import Code, IWrap, Span, SpanField
|
|
5
5
|
from ..pyfront.transform import Transform
|
|
@@ -8,7 +8,9 @@ from ..pyfront.transform import Transform
|
|
|
8
8
|
class Slot:
|
|
9
9
|
# ONLY NODE SHOULD BE ALLOWED TO BE SEEN
|
|
10
10
|
|
|
11
|
-
def __init__(
|
|
11
|
+
def __init__(
|
|
12
|
+
self, node: IWrap["Node"], value: Callable[[IWrap["Node"]], None]
|
|
13
|
+
) -> None:
|
|
12
14
|
self.privNode = node
|
|
13
15
|
"""Same as calling it from get and setting the value etc..."""
|
|
14
16
|
self.privUpdate = value
|
|
@@ -19,12 +21,17 @@ class Slot:
|
|
|
19
21
|
# I spent 4 hours trying to figure this how this could be implemented
|
|
20
22
|
# so this is my only ideal sloution
|
|
21
23
|
def __hash__(self) -> int:
|
|
22
|
-
return hash(self.
|
|
24
|
+
return hash(self.privNode.ref.id.name)
|
|
23
25
|
|
|
24
26
|
@property
|
|
25
27
|
def node(self):
|
|
26
28
|
return self.privNode
|
|
27
29
|
|
|
30
|
+
@node.setter
|
|
31
|
+
def node(self, value: IWrap["Node"]):
|
|
32
|
+
self.privNode = value
|
|
33
|
+
self.privUpdate(value)
|
|
34
|
+
|
|
28
35
|
|
|
29
36
|
@dataclass(unsafe_hash=True)
|
|
30
37
|
class IUniqueName:
|
|
@@ -121,9 +128,6 @@ class Invoke(Node):
|
|
|
121
128
|
|
|
122
129
|
|
|
123
130
|
class Empty(Node):
|
|
124
|
-
# def __init__(self, id: IUniqueName) -> None:
|
|
125
|
-
# super().__init__(id)
|
|
126
|
-
|
|
127
131
|
def __hash__(self):
|
|
128
132
|
return hash(self.id)
|
|
129
133
|
|
|
@@ -149,6 +153,26 @@ class Pause(Error):
|
|
|
149
153
|
super().__init__(id, code, reason)
|
|
150
154
|
|
|
151
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
|
+
|
|
152
176
|
@dataclass
|
|
153
177
|
class ISeqEdge:
|
|
154
178
|
node: IWrap[Node]
|
|
@@ -237,16 +261,7 @@ class TableLookup(Match):
|
|
|
237
261
|
self.privEdges.append(edge)
|
|
238
262
|
|
|
239
263
|
def buildSlots(self):
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
for e in super().buildSlots():
|
|
244
|
-
yield e
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
# Ident = Identifier("llComment")
|
|
248
|
-
|
|
249
|
-
# node = Node(Ident.id("__On_Pagesum"))
|
|
264
|
+
for e in self.privEdges:
|
|
265
|
+
yield Slot(e.node, lambda value: setattr(e, "node", value))
|
|
266
|
+
yield from super().buildSlots()
|
|
250
267
|
|
|
251
|
-
# node2 = Invoke(Ident.id("Check_Flag"),IsEqual("Flag","Check_Flag",0))
|
|
252
|
-
# node2.setOtherwise(IWrap(node),True,0)
|
|
@@ -58,7 +58,7 @@ class Trie:
|
|
|
58
58
|
internalEdges: list[IEdge] = []
|
|
59
59
|
|
|
60
60
|
for edge in edges:
|
|
61
|
-
key =
|
|
61
|
+
key = chr(edge.key) if isinstance(edge.key, int) else edge.key
|
|
62
62
|
internalEdges.append(
|
|
63
63
|
IEdge(
|
|
64
64
|
key=key.encode("utf-8") if isinstance(key, str) else key,
|
|
@@ -73,8 +73,8 @@ class Trie:
|
|
|
73
73
|
def level(self, edges: list[IEdge], path: list[bytes] = []):
|
|
74
74
|
first = edges[0].key
|
|
75
75
|
last = edges[-1].key
|
|
76
|
-
# print("level",edges, first)
|
|
77
|
-
if len(edges) == 1 and len(edges[0].key) == 0:
|
|
76
|
+
# print("level", edges, first)
|
|
77
|
+
if len(edges) == 1 and (len(edges[0].key) == 0):
|
|
78
78
|
return TrieEmpty(edges[0].node, edges[0].value)
|
|
79
79
|
|
|
80
80
|
i = 0
|
|
@@ -112,17 +112,17 @@ class Trie:
|
|
|
112
112
|
+ (b", ".join(path).decode("utf-8"))
|
|
113
113
|
+ "]"
|
|
114
114
|
)
|
|
115
|
-
|
|
115
|
+
|
|
116
116
|
keys: dict[int, list[IEdge]] = {}
|
|
117
117
|
otherwise = None
|
|
118
118
|
for edge in edges:
|
|
119
|
-
if not
|
|
119
|
+
if not edge.key:
|
|
120
120
|
otherwise = TrieEmpty(edge.node, edge.value)
|
|
121
121
|
continue
|
|
122
122
|
|
|
123
123
|
key = edge.key[0]
|
|
124
124
|
|
|
125
|
-
if keys
|
|
125
|
+
if key in keys:
|
|
126
126
|
keys[key].append(edge)
|
|
127
127
|
else:
|
|
128
128
|
keys[key] = [edge]
|
|
@@ -146,7 +146,9 @@ class Trie:
|
|
|
146
146
|
+ "]"
|
|
147
147
|
)
|
|
148
148
|
raise TypeError(err)
|
|
149
|
-
|
|
150
|
-
children.append(
|
|
149
|
+
|
|
150
|
+
children.append(
|
|
151
|
+
ITrieSingleChild(key, noAdvance, self.level(sliced, subPath))
|
|
152
|
+
)
|
|
151
153
|
|
|
152
154
|
return TrieSingle(children, otherwise)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: llparse
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
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,7 @@ Dynamic: license-file
|
|
|
15
15
|
|
|
16
16
|
A python parody of the typescript library llparse.
|
|
17
17
|
|
|
18
|
-
I take no credit for the orginal work done by indutny and I was originally very nervous about making
|
|
18
|
+
I take no credit for the orginal work done by indutny and the other node-js contributors involved and I was originally very nervous about making
|
|
19
19
|
this python library that I made public...
|
|
20
20
|
|
|
21
21
|
Links to the original library
|
|
@@ -49,7 +49,7 @@ do it at your own risk. They maybe incompleted or not throughly stress-tested.
|
|
|
49
49
|
|
|
50
50
|
## New Features
|
|
51
51
|
- Throw me an issue if typescript llparse introduces something new that you want for me or another contributor to try and implement
|
|
52
|
-
just seeing llparse add new features is
|
|
52
|
+
just seeing llparse add new features is exciting to me.
|
|
53
53
|
|
|
54
54
|
- If you want a feature that typescript llparse doesn't have, be sure to try making a pull request over there as well and not just here,
|
|
55
55
|
there's a good chance they will appericate you for helping over there too and your helping make llhttp better by doing so. :)
|
|
@@ -62,7 +62,7 @@ there's a good chance they will appericate you for helping over there too and yo
|
|
|
62
62
|
- Make it easy for me or someone else to find a problem and solve it in typescript after testing it in python
|
|
63
63
|
- Typescript takes 2 commands to run a script with node-js it while python only takes one cutting the time required tremendously...
|
|
64
64
|
- The orginal project was MIT licensed.
|
|
65
|
-
- I wanted to write my own C
|
|
65
|
+
- I wanted to write my own C Parser tool with llhttp styled callbacks of my own using a language I was the most comfortable with using.
|
|
66
66
|
- I didn't like __Lemon Parser__ or __Yacc__ all that much and a good ide for handling them in Visual Studio Code with error checking to my knowlegde does not exist.
|
|
67
67
|
- The closest thing I got to what I wanted was a project named __NMFU__ shorthand for no memory for you and even I had problems with writing things using that library...
|
|
68
68
|
|
|
@@ -131,3 +131,6 @@ print(c.c)
|
|
|
131
131
|
open("http_parser.c", "w").write(c.c)
|
|
132
132
|
open("http_parser.h", "w").write(c.header)
|
|
133
133
|
```
|
|
134
|
+
|
|
135
|
+
## Video Showcasing this library
|
|
136
|
+
- https://youtu.be/YQOzJ2BghQw
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
|
|
2
|
+
from llparse import LLParse
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_build_tables():
|
|
7
|
+
# There was a bug with 1.2 of our version that doesn't affect the node-js one where
|
|
8
|
+
# it wouldn't building tables, this attempts to simulate the problem Currenlty this
|
|
9
|
+
# bug is patched now :)
|
|
10
|
+
p = LLParse("lltable")
|
|
11
|
+
start = p.node('start')
|
|
12
|
+
loop = p.node('loop')
|
|
13
|
+
loop.skipTo(start)
|
|
14
|
+
start.match(
|
|
15
|
+
[48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122,
|
|
16
|
+
65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 58,
|
|
17
|
+
59, 60, 61, 62, 63, 64, 91, 92, 93, 94, 95, 96, 123, 124, 125, 126, 32, 9, 10, 13, 11, 12],
|
|
18
|
+
loop
|
|
19
|
+
).otherwise(p.error(0, 'im a little teapot'))
|
|
20
|
+
|
|
21
|
+
# If there is not a lookup_table this then it has failed me ;-;
|
|
22
|
+
assert "lookup_table" in p.build(start).c
|
|
23
|
+
|
|
24
|
+
|
|
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
|
+
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|