llparse 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,548 @@
1
+ import re
2
+ from dataclasses import dataclass
3
+ from typing import Optional, TypeVar, Union
4
+
5
+ Signature = ["match", "value"]
6
+
7
+
8
+ def toBuffer(value: Union[str, int]):
9
+ if isinstance(value, str):
10
+ res = value
11
+ else:
12
+ assert 0 <= value and value <= 0xFF
13
+ res = [value]
14
+ assert len(res) >= 1
15
+ return res
16
+
17
+
18
+ # TODO Add text validataion...
19
+
20
+
21
+ def validate_text(init):
22
+ def is_valid(args, kwargs):
23
+ if kwargs.get("field"):
24
+ field = kwargs["field"]
25
+ if re.search(r"[//\s\\]+", field):
26
+ raise TypeError(
27
+ f'Can\'t access internal field because the field: "{field}" conatins invalid characters'
28
+ )
29
+ return init(args, kwargs)
30
+
31
+ return is_valid
32
+
33
+
34
+ class Code:
35
+ def __init__(self, signature: str, name: str) -> None:
36
+ assert signature in Signature
37
+
38
+ self.signature = signature
39
+ self.name = name
40
+
41
+
42
+ class Field(Code):
43
+ def __init__(self, signature: str, name: str, field: str) -> None:
44
+ self.field = field
45
+ # if re.search(r"[//\s\\]+",field):
46
+ # raise TypeError(f"Can\'t access internal field from user code because the field: {name} conatins invalid characters")
47
+ super().__init__(signature, name + "_" + field)
48
+
49
+
50
+ class FieldValue(Field):
51
+ # NOTE I Added for typehinting Here as it refuses to show up on the ide I'm using (Vizonex)
52
+
53
+ def __init__(self, signature: str, name: str, field: str, value: int) -> None:
54
+ self.value = value
55
+ self.field = field
56
+ super().__init__(signature, name, field)
57
+
58
+
59
+ class And(FieldValue):
60
+ def __init__(self, field: str, value: int) -> None:
61
+ super().__init__("match", "and", field, value)
62
+
63
+
64
+ class IsEqual(FieldValue):
65
+ def __init__(self, field: str, value: int) -> None:
66
+ super().__init__("match", "is_equal", field, value)
67
+
68
+
69
+ class Load(Field):
70
+ def __init__(self, field: str) -> None:
71
+ super().__init__("match", "load", field)
72
+
73
+
74
+ class _Match(Code):
75
+ """Refers to the Code's Match Not the Node's Match"""
76
+
77
+ def __init__(self, name: str) -> None:
78
+ super().__init__("match", name)
79
+
80
+
81
+ @dataclass
82
+ class IMulAddOptions:
83
+ base: int
84
+ max: int
85
+ signed: bool = False
86
+
87
+
88
+ class MulAdd(Field):
89
+ def __init__(self, field: str, base: int, max: int, signed: bool = False) -> None:
90
+ self.options = IMulAddOptions(base, max, signed)
91
+ super().__init__("value", "mul_add", field)
92
+
93
+
94
+ class Or(FieldValue):
95
+ def __init__(self, field: str, value: int) -> None:
96
+ super().__init__("match", "or", field, value)
97
+
98
+
99
+ # class Span(Match):
100
+ # def __init__(self, name: str) -> None:
101
+ # super().__init__(name)
102
+
103
+
104
+ class Store(Field):
105
+ def __init__(self, field: str) -> None:
106
+ self.field = field
107
+ super().__init__("value", "store", field)
108
+
109
+
110
+ class Test(FieldValue):
111
+ def __init__(self, field: str, value: int) -> None:
112
+ super().__init__("match", "test", field, value)
113
+
114
+
115
+ class Update(FieldValue):
116
+ def __init__(self, field: str, value: int) -> None:
117
+ super().__init__("match", "update", field, value)
118
+
119
+
120
+ class Value(Code):
121
+ def __init__(self, name: str) -> None:
122
+ super().__init__("value", name)
123
+
124
+
125
+ # Nodes...
126
+
127
+
128
+ class Node:
129
+ def __init__(self, name: str) -> None:
130
+ self.name = name
131
+ self.otherwiseEdge: Optional["Edge"] = None
132
+ self.privEdges: list["Edge"] = []
133
+
134
+ def __hash__(self) -> int:
135
+ return hash(self.name)
136
+
137
+ def otherwise(self, node: "Node"):
138
+ if self.otherwiseEdge:
139
+ raise TypeError("Node Already has an 'otherwise' or 'skipto'")
140
+ self.otherwiseEdge = Edge(node, True, None, None)
141
+ return self
142
+
143
+ def skipTo(self, node: "Node"):
144
+ if self.otherwiseEdge:
145
+ raise TypeError("Node Already has an 'otherwise' or 'skipto'")
146
+ self.otherwiseEdge = Edge(node, False, None, None)
147
+ return self
148
+
149
+ def getOtherwiseEdge(self):
150
+ return self.otherwiseEdge
151
+
152
+ def getEdges(self):
153
+ """Returns non if object is empty"""
154
+ return None if self.privEdges == [] else self.privEdges
155
+
156
+ def getAllEdges(self):
157
+ r"Get list of all edges (including otherwise, if present)"
158
+ res = self.privEdges
159
+ if not self.otherwiseEdge:
160
+ return res
161
+ else:
162
+ # Concate DO NOT ADD TO RES!!!!
163
+
164
+ return res + [self.otherwiseEdge]
165
+
166
+ def __iter__(self):
167
+ if self.privEdges != []:
168
+ for e in self.privEdges:
169
+ yield e
170
+
171
+ def addEdge(self, edge: "Edge"):
172
+ assert isinstance(edge.key, (int, str)) or edge.key
173
+
174
+ if len(self.privEdges) > 0:
175
+ assert edge.key not in [e.key for e in self.privEdges]
176
+ self.privEdges.insert(0, edge)
177
+
178
+
179
+ # TODO Add strict type checking to \"Pause.__init__\"" parameters to prevent the
180
+ # bypassing arbtrary values
181
+
182
+
183
+ class Pause(Node):
184
+ def __init__(self, code: int, reason: str) -> None:
185
+ self.code = code
186
+ self.reason = reason
187
+ super().__init__("pause")
188
+
189
+ def skipTo(self, node: "Node"):
190
+ """`WARNING!` `Pause.skipTo()` IS NOT SUPPORTED AND WILL IMMEDIATELY THROW AN `Execption` IF YOU DO IT"""
191
+ raise Exception("Not supported in Pause Class, please use '.otherwise'")
192
+
193
+
194
+ class Comsume(Node):
195
+ def __init__(self, field: str) -> None:
196
+ self.field = field
197
+ super().__init__("consume_" + field)
198
+
199
+
200
+ class Error(Node):
201
+ def __init__(self, code: int, reason: str) -> None:
202
+ super().__init__("error")
203
+ # print(code)
204
+ assert isinstance(code, int), "code is supposed to be an int not %s" % (
205
+ type(code).__name__
206
+ )
207
+ self.code = code
208
+ self.reason = reason
209
+
210
+ def otherwise(self, node: "Node"):
211
+ raise TypeError("Not Supported")
212
+
213
+ def skipTo(self, node: "Node"):
214
+ raise TypeError("Not Supported")
215
+
216
+
217
+ class Invoke(Node):
218
+ def __init__(self, code: Code, IInvokeMap: dict[int, Node]) -> None:
219
+ self.code = code
220
+ super().__init__("invoke_" + code.name)
221
+ for numKey, targetNode in IInvokeMap.items():
222
+ if isinstance(numKey, int) and targetNode is None:
223
+ raise TypeError(
224
+ "Invoke's map keys must be integers and values must not be left blank!"
225
+ )
226
+ self.addEdge(Edge(targetNode, True, numKey, None))
227
+
228
+
229
+ # -- Transfroms --
230
+
231
+ TransformName = ["to_lower_unsafe", "to_lower"]
232
+
233
+
234
+ class Transform:
235
+ def __init__(self, name: str) -> None:
236
+ assert name in TransformName
237
+ self.name = name
238
+
239
+
240
+ class ToLower(Transform):
241
+ def __init__(self) -> None:
242
+ super().__init__("to_lower")
243
+
244
+
245
+ class ToLowerUnsafe(Transform):
246
+ def __init__(self) -> None:
247
+ super().__init__("to_lower_unsafe")
248
+
249
+
250
+ class TransfromCreator:
251
+ """API For Character transformations used in::
252
+
253
+ p.node().transform(...)"""
254
+
255
+ def toLowerUnsafe(self):
256
+ return ToLowerUnsafe()
257
+
258
+ def toLower(self):
259
+ return ToLower()
260
+
261
+
262
+ # def toBuffer(value:Union[int,str,bytes]) -> bytes:
263
+ # """Returns a bytes to use when making switch cases in C..."""
264
+ # if isinstance(value,bytes):
265
+ # res = value
266
+ # elif isinstance(value,str):
267
+ # res = value.encode("utf-8","surrogateescape")
268
+ # else:
269
+ # if not (0 <= value and value <= 0xff):
270
+ # raise BufferError("Invalid byte value")
271
+ # res = chr(value).encode("utf-8","surrogateescape")
272
+ # if len(res) >= 1:
273
+ # raise AssertionError("Invalid key length")
274
+ # return res
275
+
276
+
277
+ MatchSingleValue = TypeVar("MatchSingleValue", str, int, bytes)
278
+
279
+
280
+ class Edge:
281
+ def __init__(
282
+ self,
283
+ node: Node,
284
+ noAdvance: bool,
285
+ key: Optional[Union[int, str]],
286
+ value: Optional[int],
287
+ ) -> None:
288
+ self.node = node
289
+ self.noAdvance = noAdvance
290
+
291
+ self.key = key
292
+ self.value = value
293
+
294
+ # Validation...
295
+ if isinstance(node, Invoke):
296
+ # NOTE In python 0 is seen as none so simply checking for it is not an option!
297
+ # in llparse This bould be is it's equvilent of "if (value === undefined) {""
298
+ if (not isinstance(value, int)) and value is None:
299
+ if node.code.signature != "match":
300
+ raise TypeError(
301
+ f"Invalid invoke code signature : {node.code.signature} is not match"
302
+ )
303
+
304
+ elif node.code.signature != "value":
305
+ raise TypeError(
306
+ f"Invalid invoke code signature : {node.code.signature} is not value"
307
+ )
308
+
309
+ elif noAdvance:
310
+ if key and not isinstance(key, int) and len(key) != 1:
311
+ raise TypeError("Only 1-char keys are allowed in 'noAdvance' edges")
312
+
313
+ else:
314
+ # print(node)
315
+ if not isinstance(node, Node):
316
+ raise TypeError(
317
+ f"Attempted to pass value to non-Invoke node as :{type(node)}"
318
+ )
319
+
320
+ def __hash__(self) -> int:
321
+ return hash(self.node.name)
322
+
323
+ # Very Big Function but it works....
324
+ @staticmethod
325
+ def compare(a: "Edge", b: "Edge"):
326
+ return a.key == b.key
327
+
328
+
329
+ # This is where the fun begins...
330
+
331
+
332
+ class Match(Node):
333
+ """This node matches characters/sequences and forwards the execution according
334
+ to matched character with optional attached value (See `.select()`)"""
335
+
336
+ def __init__(self, name: str) -> None:
337
+ super().__init__(name)
338
+ self.transformFn: Optional[Transform] = None
339
+
340
+ def transform(self, transformFn: Transform):
341
+ self.transformFn = transformFn
342
+ return self
343
+
344
+ def match(self, value: Union[str, int, list[int], list[str]], next: Node):
345
+ """
346
+ Match sequence/character and forward execution to `next` on success,
347
+
348
+ consuming matched bytes of the input.
349
+
350
+ No value is attached on such execution forwarding, and the target node
351
+
352
+ **must not** be an `Invoke` node with a callback expecting the value.
353
+
354
+ Parameters
355
+ ----------
356
+
357
+ - `value` Sequence/character to be matched
358
+
359
+ - `next` Target node to be executed on success.
360
+ """
361
+ # if isinstance(value,str):
362
+ # value = value.encode("utf-8")
363
+ if isinstance(value, list) and len(value) > 1:
364
+ for i in value:
365
+ self.match(i.encode("utf-8") if isinstance(i, str) else i, next)
366
+ return self
367
+
368
+ edge = Edge(next, False, value, None)
369
+ self.addEdge(edge)
370
+ return self
371
+
372
+ def peek(self, value: Union[str, int, list[Union[str, int]]], next: Node):
373
+ """Match character and forward execution to `next` on success
374
+ without consuming one byte of the input.
375
+
376
+ No value is attached on such execution forwarding, and the target node
377
+ must not be an `Invoke` with a callback expecting the value.
378
+
379
+ Parameters
380
+ ----------
381
+
382
+ - `value` Character to be matched
383
+ - `next` Target node to be executed on success."""
384
+
385
+ if isinstance(value, list):
386
+ for i in value:
387
+ self.peek(i, next)
388
+ return self
389
+
390
+ if (isinstance(value, str)) and (len(value) != 1):
391
+ raise AssertionError(
392
+ ".peek() accepts only singular character keys "
393
+ + f"perhaps you meant to say : {value.split()}"
394
+ if isinstance(str, value)
395
+ else ""
396
+ )
397
+ edge = Edge(next, True, value, None)
398
+ self.addEdge(edge)
399
+ return self
400
+
401
+ # You may be asking why I'm not using Other types of Errors why assertion errors when there is not assert?
402
+ # It's beacuse it wanted to stay close to the orginal llparse library for better troubleshooting and error diagnosis - Vizonex
403
+ def select(
404
+ self,
405
+ keyOrDict: Union[int, str, dict[str, int]],
406
+ valueOrNext: Optional[Union[int, Node]] = None,
407
+ next: Optional[Node] = None,
408
+ ):
409
+ """Match character/sequence and forward execution to `next` on success
410
+ consumed matched bytes of the input.
411
+
412
+ Value is attached on such execution forwarding, and the target node
413
+ must be an `Invoke` with a callback expecting the value.
414
+
415
+ Possible signatures:
416
+
417
+ `.select(key, value [, next ])`
418
+ `.select({ key: value } [, next])`
419
+
420
+ - `keyOrDict` Either a sequence to match, or a dictionary from sequences to values
421
+ - `valueOrNext` Either an integer value to be forwarded to the target node, or an otherwise node
422
+ - `next` Convenience param. Same as calling `.otherwise(...)`"""
423
+ if isinstance(keyOrDict, dict):
424
+ if not isinstance(valueOrNext, Node):
425
+ raise AssertionError("Invalid next argument of '.select()'")
426
+ if next:
427
+ raise AssertionError("Invalid argument count of '.select()'")
428
+
429
+ next = valueOrNext
430
+ for numKey, key in keyOrDict.items():
431
+ # print(f"{key}:{numKey}:{next}")
432
+ self.select(numKey, keyOrDict[numKey], next)
433
+
434
+ return self
435
+
436
+ # select(key,value,next)
437
+ assert isinstance(valueOrNext, int), (
438
+ "value Argument should be an integer not, %s" % (type(valueOrNext).__name__)
439
+ )
440
+ # raise AssertionError("Invalid `value` of argument .select()")
441
+ assert next is not None, "Invalid `next` of argument .select()"
442
+ value = int(valueOrNext)
443
+ key = toBuffer(keyOrDict)
444
+ edge = Edge(next, False, key, value)
445
+ if self.name == "nmethods":
446
+ print(f"{self.name}: {key} -> {edge.node.name}")
447
+
448
+ self.addEdge(edge)
449
+ return self
450
+
451
+ def getTransform(self):
452
+ return self.transformFn
453
+
454
+
455
+ class _Span(Match):
456
+ def __init__(self, name: str) -> None:
457
+ self.name = name
458
+ super().__init__(name)
459
+
460
+
461
+ class SpanStart(Node):
462
+ def __init__(self, span: "Span") -> None:
463
+ self.span = span
464
+ super().__init__(f"span_start_{span.callback.name}")
465
+
466
+
467
+ class SpanEnd(Node):
468
+ def __init__(self, span: "Span") -> None:
469
+ self.span = span
470
+ super().__init__(f"span_end_{span.callback.name}")
471
+
472
+
473
+ class Span:
474
+ def __init__(self, callback: _Span) -> None:
475
+ self.callback = callback
476
+ # NOTE Both SpanStart and SpanEnd have hash
477
+ # functions inherited from Node so no need to
478
+ # add anything special over there...
479
+ self.startCache: dict[Node, SpanStart] = {}
480
+ self.endCache: dict[Node, SpanEnd] = {}
481
+
482
+ # NOTE The thing that I'm greatful for seen in the orginal llparse typescript library
483
+ # is the ability to fork out and create all sorts of nodes whenever possible, these two
484
+ # functions personally demonstrate just that branching out concept alone - Vizonex
485
+
486
+ def start(self, otherwise: Optional[Node] = None):
487
+ if otherwise and self.startCache.get(otherwise):
488
+ return self.startCache[otherwise]
489
+
490
+ res = SpanStart(self)
491
+
492
+ if otherwise:
493
+ res.otherwise(otherwise)
494
+ self.startCache[otherwise] = res
495
+
496
+ return res
497
+
498
+ def end(self, otherwise: Optional[Node] = None):
499
+ if otherwise and self.endCache.get(otherwise):
500
+ return self.endCache[otherwise]
501
+
502
+ res = SpanEnd(self)
503
+
504
+ if otherwise:
505
+ res.otherwise(otherwise)
506
+ self.endCache[otherwise] = res
507
+
508
+ return res
509
+
510
+
511
+ class Reachability:
512
+ @staticmethod
513
+ def build(root: Node) -> list[Node]:
514
+ res: set[Node] = set()
515
+ queue = [root]
516
+
517
+ while queue:
518
+ node = queue.pop()
519
+ if node in res:
520
+ continue
521
+
522
+ res.add(node)
523
+
524
+ for edge in node:
525
+ queue.append(edge.node)
526
+
527
+ otherwise = node.getOtherwiseEdge()
528
+ if otherwise:
529
+ queue.append(otherwise.node)
530
+
531
+ return list(res)
532
+
533
+
534
+ # On_User_Key = Match("On_User_Key")
535
+
536
+ # On_User_Value = Match("On_User_Value")
537
+
538
+ # data_extraction = Node("On_Data_Extraction").skipTo(On_User_Key)
539
+
540
+ # node = Match("On_Level_Comment").skipTo(data_extraction)
541
+
542
+
543
+ # On_User_Key.match(["0","1","2","3","4","5","6","7","8","9"],On_User_Key)\
544
+ # .peek("~",On_User_Value)\
545
+ # .otherwise(Error(1,"[BAD KEY] Some retard decided to hijack your server! Oh SHIT!"))
546
+
547
+
548
+ # print([d.node.name for d in node.getAllEdges()])
@@ -0,0 +1,37 @@
1
+
2
+ from main_code import Edge, Node
3
+
4
+
5
+ class ParserMap:
6
+ def __init__(self, root: Node) -> None:
7
+ self.root = root
8
+
9
+ def Jsonize(self):
10
+ queue = [self.root]
11
+ seen: set[Node] = set()
12
+
13
+
14
+ while len(queue) != 0:
15
+ node = queue.pop()
16
+
17
+ if node in seen:
18
+ continue
19
+
20
+ seen.add(node)
21
+
22
+ for edge in node:
23
+ queue.append(edge.node)
24
+
25
+ otherwise = node.getOtherwiseEdge()
26
+ if otherwise:
27
+ queue.append(otherwise.node)
28
+
29
+ return [(s.__dict__, list(map(self.get_edges, s))) for s in seen]
30
+
31
+ def get_edges(self, edge: Edge):
32
+ if not edge:
33
+ return {}
34
+ data = edge.__dict__
35
+ data["node"] = edge.node
36
+ # print(data["node"].name)
37
+ return data
@@ -0,0 +1,33 @@
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