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.
- {llparse-0.1.8 → llparse-0.1.9}/PKG-INFO +1 -1
- {llparse-0.1.8 → llparse-0.1.9}/llparse/C_compiler.py +1 -1
- {llparse-0.1.8 → llparse-0.1.9}/llparse/__init__.py +2 -0
- {llparse-0.1.8 → llparse-0.1.9}/llparse/compilator.py +32 -29
- {llparse-0.1.8 → llparse-0.1.9}/llparse/cython_builder.py +1 -1
- {llparse-0.1.8 → llparse-0.1.9}/llparse/enumerator.py +3 -2
- {llparse-0.1.8 → llparse-0.1.9}/llparse/frontend.py +35 -23
- {llparse-0.1.8 → llparse-0.1.9}/llparse/llparse.py +14 -12
- llparse-0.1.9/llparse/pybuilder/__init__.py +7 -0
- {llparse-0.1.8 → llparse-0.1.9}/llparse/pybuilder/builder.py +1 -2
- {llparse-0.1.8 → llparse-0.1.9}/llparse/pybuilder/loopchecker.py +2 -3
- {llparse-0.1.8 → llparse-0.1.9}/llparse/pybuilder/main_code.py +0 -18
- llparse-0.1.9/llparse/pyfront/__init__.py +3 -0
- llparse-0.1.8/llparse/pyfront/front.py → llparse-0.1.9/llparse/pyfront/code.py +5 -1
- {llparse-0.1.8 → llparse-0.1.9}/llparse/pyfront/implementation.py +4 -4
- llparse-0.1.9/llparse/pyfront/namespace.py +9 -0
- {llparse-0.1.8 → llparse-0.1.9}/llparse/pyfront/nodes.py +5 -10
- llparse-0.1.9/llparse/pyfront/peephole.py +47 -0
- {llparse-0.1.8 → llparse-0.1.9}/llparse/spanalloc.py +17 -12
- {llparse-0.1.8 → llparse-0.1.9}/llparse/trie.py +10 -9
- {llparse-0.1.8 → llparse-0.1.9}/llparse.egg-info/PKG-INFO +1 -1
- {llparse-0.1.8 → llparse-0.1.9}/llparse.egg-info/SOURCES.txt +2 -3
- {llparse-0.1.8 → llparse-0.1.9}/pyproject.toml +1 -1
- llparse-0.1.8/llparse/pybuilder/__init__.py +0 -8
- llparse-0.1.8/llparse/pyfront/containers.py +0 -33
- llparse-0.1.8/llparse/pyfront/namespace.py +0 -3
- llparse-0.1.8/llparse/pyfront/peephole.py +0 -45
- llparse-0.1.8/llparse/test.py +0 -233
- {llparse-0.1.8 → llparse-0.1.9}/LICENSE +0 -0
- {llparse-0.1.8 → llparse-0.1.9}/README.md +0 -0
- {llparse-0.1.8 → llparse-0.1.9}/llparse/constants.py +0 -0
- {llparse-0.1.8 → llparse-0.1.9}/llparse/debug.py +0 -0
- {llparse-0.1.8 → llparse-0.1.9}/llparse/dot.py +0 -0
- {llparse-0.1.8 → llparse-0.1.9}/llparse/errors.py +0 -0
- {llparse-0.1.8 → llparse-0.1.9}/llparse/header.py +0 -0
- {llparse-0.1.8 → llparse-0.1.9}/llparse/pybuilder/parsemap.py +0 -0
- {llparse-0.1.8 → llparse-0.1.9}/llparse/pyfront/transform.py +0 -0
- {llparse-0.1.8 → llparse-0.1.9}/llparse/settings.py +0 -0
- {llparse-0.1.8 → llparse-0.1.9}/llparse.egg-info/dependency_links.txt +0 -0
- {llparse-0.1.8 → llparse-0.1.9}/llparse.egg-info/top_level.txt +0 -0
- {llparse-0.1.8 → llparse-0.1.9}/setup.cfg +0 -0
- {llparse-0.1.8 → llparse-0.1.9}/tests/test_frontend.py +0 -0
- {llparse-0.1.8 → llparse-0.1.9}/tests/test_loop_checker.py +0 -0
- {llparse-0.1.8 → llparse-0.1.9}/tests/test_span_allocator.py +0 -0
|
@@ -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) +
|
|
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()}"]
|
|
@@ -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
|
|
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(
|
|
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) ==
|
|
818
|
-
raise ValueError(
|
|
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
|
-
|
|
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
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
from .pyfront.
|
|
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,
|
|
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.
|
|
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
|
|
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(
|
|
26
|
-
NodeT = TypeVar(
|
|
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,
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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(
|
|
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(
|
|
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...)"""
|
|
@@ -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
|
|
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
|
|
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()])
|
|
@@ -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",
|
|
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
|
|
1
|
+
from . import code as code
|
|
2
2
|
from ..pyfront import nodes as node
|
|
3
3
|
from ..pyfront import transform
|
|
4
|
-
from
|
|
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
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from dataclasses import dataclass, field
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Callable, Optional
|
|
3
3
|
|
|
4
|
-
from
|
|
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] =
|
|
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
|
|
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 =
|
|
152
|
-
for _ in range(self._mx + 1):
|
|
153
|
-
|
|
154
|
-
|
|
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)
|
|
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
|
|
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(
|
|
102
|
-
|
|
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.
|
|
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
|
)
|
|
@@ -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/
|
|
30
|
-
llparse/pyfront/
|
|
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,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,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
|
llparse-0.1.8/llparse/test.py
DELETED
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|