llparse 0.1.4__tar.gz → 0.1.6__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.4 → llparse-0.1.6}/PKG-INFO +1 -1
- {llparse-0.1.4 → llparse-0.1.6}/llparse/compilator.py +10 -1
- {llparse-0.1.4 → llparse-0.1.6}/llparse/frontend.py +12 -3
- {llparse-0.1.4 → llparse-0.1.6}/llparse/pybuilder/builder.py +103 -72
- {llparse-0.1.4 → llparse-0.1.6}/llparse/pybuilder/main_code.py +31 -12
- {llparse-0.1.4 → llparse-0.1.6}/llparse/pyfront/front.py +8 -0
- {llparse-0.1.4 → llparse-0.1.6}/llparse/pyfront/implementation.py +4 -0
- {llparse-0.1.4 → llparse-0.1.6}/llparse.egg-info/PKG-INFO +1 -1
- {llparse-0.1.4 → llparse-0.1.6}/pyproject.toml +1 -1
- llparse-0.1.6/tests/test_frontend.py +164 -0
- {llparse-0.1.4 → llparse-0.1.6}/tests/test_span_allocator.py +15 -9
- llparse-0.1.4/tests/test_frontend.py +0 -35
- {llparse-0.1.4 → llparse-0.1.6}/LICENSE +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/README.md +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/llparse/C_compiler.py +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/llparse/__init__.py +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/llparse/constants.py +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/llparse/cython_builder.py +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/llparse/debug.py +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/llparse/dot.py +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/llparse/enumerator.py +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/llparse/errors.py +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/llparse/header.py +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/llparse/llparse.py +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/llparse/pybuilder/__init__.py +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/llparse/pybuilder/loopchecker.py +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/llparse/pybuilder/parsemap.py +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/llparse/pyfront/containers.py +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/llparse/pyfront/namespace.py +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/llparse/pyfront/nodes.py +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/llparse/pyfront/peephole.py +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/llparse/pyfront/transform.py +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/llparse/settings.py +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/llparse/spanalloc.py +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/llparse/test.py +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/llparse/trie.py +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/llparse.egg-info/SOURCES.txt +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/llparse.egg-info/dependency_links.txt +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/llparse.egg-info/top_level.txt +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/setup.cfg +0 -0
- {llparse-0.1.4 → llparse-0.1.6}/tests/test_loop_checker.py +0 -0
|
@@ -273,6 +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
|
+
class Operator(Field):
|
|
277
|
+
def __init__(self, ref: _frontend.code.Operator):
|
|
278
|
+
self.ref = ref
|
|
279
|
+
|
|
280
|
+
def doBuild(self, ctx: "Compilation", out: list[str]):
|
|
281
|
+
out.append(f'return {self.field(ctx)} {self.ref.op} {self.ref.value};')
|
|
282
|
+
|
|
276
283
|
|
|
277
284
|
@dataclass
|
|
278
285
|
class INodeEdge:
|
|
@@ -501,7 +508,7 @@ class Pause(Error):
|
|
|
501
508
|
|
|
502
509
|
assert self.ref.otherwise
|
|
503
510
|
otherwise = ctx.unwrapNode(self.ref.otherwise.node)
|
|
504
|
-
out.append(f"{ctx.currentField()} = (void*) (intptr_t) {otherwise.cachedDecel};")
|
|
511
|
+
out.append(f"{ctx.currentField()} = (void*) (intptr_t) {otherwise.cachedDecel or ('s_n_' + otherwise.ref.id.name)};")
|
|
505
512
|
out.append(f"return {STATE_ERROR};")
|
|
506
513
|
|
|
507
514
|
|
|
@@ -1218,6 +1225,8 @@ class Compilation:
|
|
|
1218
1225
|
r = Test(ref)
|
|
1219
1226
|
elif isinstance(ref, _frontend.code.Update):
|
|
1220
1227
|
r = Update(ref)
|
|
1228
|
+
elif isinstance(ref, _frontend.code.Operator):
|
|
1229
|
+
r = Operator(ref)
|
|
1221
1230
|
else:
|
|
1222
1231
|
raise Exception(
|
|
1223
1232
|
f'refrence "{ref.name}" is an Invalid Code Type , TypeName:"{ref.__class__.__name__}"'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from dataclasses import dataclass, field
|
|
2
|
-
from typing import Literal, Optional, Union
|
|
2
|
+
from typing import Literal, Optional, Union, TypeVar
|
|
3
3
|
|
|
4
4
|
from .enumerator import Enumerator
|
|
5
5
|
from .pybuilder import LoopChecker
|
|
@@ -22,6 +22,9 @@ from logging import getLogger
|
|
|
22
22
|
log = getLogger("llparse.frontend")
|
|
23
23
|
|
|
24
24
|
|
|
25
|
+
CodeT = TypeVar('CodeT', bound=source.code.Code)
|
|
26
|
+
NodeT = TypeVar('NodeT', bound=source.node.Node)
|
|
27
|
+
|
|
25
28
|
WrappedNode = IWrap[_frontend.node.Node]
|
|
26
29
|
WrappedCode = IWrap[_frontend.code.Code]
|
|
27
30
|
|
|
@@ -462,7 +465,7 @@ class Frontend:
|
|
|
462
465
|
return self.translateCode(code)
|
|
463
466
|
|
|
464
467
|
def translateCode(
|
|
465
|
-
self, code:
|
|
468
|
+
self, code: CodeT
|
|
466
469
|
):
|
|
467
470
|
"""Translates Builder Classes to Frontend Classes..."""
|
|
468
471
|
|
|
@@ -474,7 +477,13 @@ class Frontend:
|
|
|
474
477
|
res = codeImpl.IsEqual(
|
|
475
478
|
_frontend.code.IsEqual(prefixed, code.field, code.value)
|
|
476
479
|
)
|
|
477
|
-
|
|
480
|
+
# custom thing I added in 0.1.6 I encourage the typescript
|
|
481
|
+
# maintainers and developers of llparse to look into this :)
|
|
482
|
+
elif isinstance(code, source.code.Operator):
|
|
483
|
+
res = codeImpl.Operator(_frontend.code.Operator(
|
|
484
|
+
prefixed, code.field, code.value, code.op
|
|
485
|
+
))
|
|
486
|
+
|
|
478
487
|
elif isinstance(code, source.code.Load):
|
|
479
488
|
res = codeImpl.Load(_frontend.code.Load(prefixed, code.field))
|
|
480
489
|
|
|
@@ -36,10 +36,13 @@ class Creator:
|
|
|
36
36
|
def __init__(self) -> None:
|
|
37
37
|
return
|
|
38
38
|
|
|
39
|
-
# TODO
|
|
39
|
+
# TODO (Vizonex): I think we could add a node for property copying from a to b
|
|
40
|
+
# and addition and subtraction nodes like a counter unlike consume and I think the typescript
|
|
41
|
+
# Library could have the same features as us. I seem to come up with new nodes often
|
|
42
|
+
# which is a rare thing to see. :)
|
|
40
43
|
|
|
41
44
|
# Thank goodness I can do C example documentation in here :) - Vizonex
|
|
42
|
-
def match(self, name: str):
|
|
45
|
+
def match(self, name: str) -> code.Match:
|
|
43
46
|
"""Create an external callback that **has no** `value` argument.
|
|
44
47
|
|
|
45
48
|
This callback can be used in all `Invoke` nodes except those that are
|
|
@@ -53,14 +56,11 @@ class Creator:
|
|
|
53
56
|
|
|
54
57
|
Where `llparse_t` is parser state's type name.
|
|
55
58
|
|
|
56
|
-
|
|
57
|
-
----------
|
|
58
|
-
----------
|
|
59
|
-
- `name` External function name.
|
|
59
|
+
:param name: External function name.
|
|
60
60
|
"""
|
|
61
61
|
return code._Match(name)
|
|
62
62
|
|
|
63
|
-
def value(self, name: str):
|
|
63
|
+
def value(self, name: str) -> code.Value:
|
|
64
64
|
"""
|
|
65
65
|
|
|
66
66
|
Create an external callback that **has** `value` argument.
|
|
@@ -76,15 +76,12 @@ class Creator:
|
|
|
76
76
|
|
|
77
77
|
Where `llparse_t` is parser state's type name.
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
----------
|
|
81
|
-
----------
|
|
82
|
-
- `name` External function name.
|
|
79
|
+
:param name: External function name.
|
|
83
80
|
|
|
84
81
|
"""
|
|
85
82
|
return code.Value(name)
|
|
86
83
|
|
|
87
|
-
def span(self, name: str):
|
|
84
|
+
def span(self, name: str) -> code._Span:
|
|
88
85
|
"""Create an external span callback.
|
|
89
86
|
|
|
90
87
|
This callback can be used only in `Span` constructor.
|
|
@@ -100,30 +97,25 @@ class Creator:
|
|
|
100
97
|
|
|
101
98
|
NOTE: non-zero return value is treated as resumable error.
|
|
102
99
|
|
|
103
|
-
|
|
104
|
-
----------
|
|
105
|
-
- `name` External function name.
|
|
100
|
+
:param name: External function name.
|
|
106
101
|
"""
|
|
107
102
|
return code._Span(name)
|
|
108
103
|
|
|
109
|
-
def store(self, field: str):
|
|
104
|
+
def store(self, field: str) -> code.Store:
|
|
110
105
|
"""
|
|
111
106
|
Intrinsic operation. Stores `value` from `.select()` node into the state's
|
|
112
107
|
property with the name specified by `field`, returns zero.
|
|
113
108
|
|
|
114
109
|
```c
|
|
115
|
-
|
|
116
|
-
|
|
110
|
+
state[field] = value;
|
|
111
|
+
return 0;
|
|
117
112
|
```
|
|
118
113
|
|
|
119
|
-
|
|
120
|
-
----------
|
|
121
|
-
|
|
122
|
-
- `field` Property name
|
|
114
|
+
:param field: Property name
|
|
123
115
|
"""
|
|
124
116
|
return code.Store(field)
|
|
125
117
|
|
|
126
|
-
def load(self, field: str):
|
|
118
|
+
def load(self, field: str) -> code.Load:
|
|
127
119
|
"""Intrinsic operation. Loads and returns state's property with the name
|
|
128
120
|
specified by `field`.
|
|
129
121
|
|
|
@@ -132,15 +124,14 @@ class Creator:
|
|
|
132
124
|
```c
|
|
133
125
|
return state[field];
|
|
134
126
|
```
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
`field` Property name.
|
|
127
|
+
|
|
128
|
+
:param field: Property name.
|
|
138
129
|
"""
|
|
139
130
|
return code.Load(field)
|
|
140
131
|
|
|
141
132
|
def mulAdd(
|
|
142
133
|
self, field: str, base: int, max: Optional[int] = None, signed: bool = False
|
|
143
|
-
):
|
|
134
|
+
) -> code.MulAdd:
|
|
144
135
|
"""Intrinsic operation. Takes `value` from `.select()`, state's property
|
|
145
136
|
with the name `field` and does:
|
|
146
137
|
```c
|
|
@@ -155,24 +146,21 @@ class Creator:
|
|
|
155
146
|
- 0 - success
|
|
156
147
|
- 1 - overflow
|
|
157
148
|
|
|
158
|
-
Parameters
|
|
159
|
-
----------
|
|
160
|
-
----------
|
|
161
149
|
Unlike in Typescript, The values of `IMulAddOptions` have been added here
|
|
162
150
|
since it's Python , not Typescript
|
|
163
151
|
|
|
164
|
-
|
|
152
|
+
:param field: Property name
|
|
165
153
|
|
|
166
|
-
|
|
154
|
+
:param base: Value to multiply the property with in the first step
|
|
167
155
|
|
|
168
|
-
|
|
156
|
+
:param max: Maximum value of the property. If at any point of computation the
|
|
169
157
|
intermediate result exceeds it - `mulAdd` returns 1 (overflow).
|
|
170
158
|
|
|
171
|
-
|
|
159
|
+
:param signed: If `true` - all arithmetics perfomed by `mulAdd` will be signed.
|
|
172
160
|
Default value: `false`"""
|
|
173
161
|
return code.MulAdd(field, base, max, signed)
|
|
174
162
|
|
|
175
|
-
def update(self, field: str, value: int):
|
|
163
|
+
def update(self, field: str, value: int) -> code.Update:
|
|
176
164
|
"""
|
|
177
165
|
|
|
178
166
|
Intrinsic operation. Puts `value` integer into the state's property with
|
|
@@ -181,75 +169,119 @@ class Creator:
|
|
|
181
169
|
state[field] = value;
|
|
182
170
|
return 0;
|
|
183
171
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
----------
|
|
187
|
-
- `field` Property name
|
|
188
|
-
- `value` Integer value to be stored into the property.
|
|
172
|
+
:param field: Property name
|
|
173
|
+
:param value: Integer value to be stored into the property.
|
|
189
174
|
"""
|
|
190
175
|
return code.Update(field, value)
|
|
191
176
|
|
|
192
|
-
def isEqual(self, field: str, value: str):
|
|
177
|
+
def isEqual(self, field: str, value: str) -> code.IsEqual:
|
|
193
178
|
"""Intrinsic operation.
|
|
194
179
|
|
|
195
|
-
|
|
196
|
-
|
|
180
|
+
```c
|
|
181
|
+
state[field] &= value
|
|
182
|
+
return 0;
|
|
183
|
+
```
|
|
197
184
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
----------
|
|
201
|
-
- `field` Property name
|
|
202
|
-
- `value` Integer value
|
|
185
|
+
:param field: Property name
|
|
186
|
+
:param value: Integer value
|
|
203
187
|
"""
|
|
204
188
|
return code.IsEqual(field, value)
|
|
205
189
|
|
|
206
190
|
# NOTE : Unlike in typescript lowercase "and" & "or"
|
|
207
191
|
# cannot be used this might have to be
|
|
208
192
|
# throughly addressed - Vizonex
|
|
209
|
-
def And(self, field: str, value: int):
|
|
193
|
+
def And(self, field: str, value: int) -> code.And:
|
|
210
194
|
"""Intrinsic operation.
|
|
211
195
|
|
|
212
|
-
|
|
213
|
-
|
|
196
|
+
```c
|
|
197
|
+
state[field] &= value
|
|
198
|
+
return 0;
|
|
199
|
+
```
|
|
214
200
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
- `field` Property name
|
|
219
|
-
- `value` Integer value"""
|
|
201
|
+
:param field: Property name
|
|
202
|
+
:param value: Integer value
|
|
203
|
+
"""
|
|
220
204
|
|
|
221
205
|
return code.And(field, value)
|
|
222
206
|
|
|
223
|
-
def Or(self, field: str, value: int):
|
|
207
|
+
def Or(self, field: str, value: int) -> code.Or:
|
|
224
208
|
"""
|
|
225
209
|
Intrinsic operation.
|
|
226
210
|
|
|
227
211
|
state[field] |= value
|
|
228
212
|
return 0;
|
|
229
213
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
----------
|
|
233
|
-
- `field` Property name
|
|
234
|
-
- `value` Integer value
|
|
214
|
+
:param field: Property name
|
|
215
|
+
:param value: Integer value
|
|
235
216
|
|
|
236
217
|
This will allow us to set our own flags at will
|
|
237
218
|
"""
|
|
238
219
|
return code.Or(field, value)
|
|
239
220
|
|
|
240
|
-
def test(self, field: str, value:
|
|
221
|
+
def test(self, field: str, value: int) -> code.Test:
|
|
241
222
|
"""Intrinsic operation.
|
|
242
223
|
|
|
224
|
+
```c
|
|
243
225
|
return (state[field] & value) == value ? 1 : 0;
|
|
226
|
+
```
|
|
244
227
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
----------
|
|
248
|
-
- `field` Property name
|
|
249
|
-
- `value` Integer value
|
|
228
|
+
:param field: Property name
|
|
229
|
+
:param value: Integer value
|
|
250
230
|
"""
|
|
251
231
|
return code.Test(field, value)
|
|
252
232
|
|
|
233
|
+
def is_gt(self, field: str, value: int) -> code.Operator:
|
|
234
|
+
"""Intrinsic operation.
|
|
235
|
+
|
|
236
|
+
```c
|
|
237
|
+
return (state[field] > value);
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
:param field: Property name
|
|
241
|
+
:param value: Integer value
|
|
242
|
+
"""
|
|
243
|
+
|
|
244
|
+
return code.Operator(">", field, value)
|
|
245
|
+
|
|
246
|
+
def is_lt(self, field: str, value: int) -> code.Operator:
|
|
247
|
+
"""Intrinsic operation.
|
|
248
|
+
|
|
249
|
+
```c
|
|
250
|
+
return (state[field] < value);
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
:param field: Property name
|
|
254
|
+
:param value: Integer value
|
|
255
|
+
"""
|
|
256
|
+
|
|
257
|
+
return code.Operator("<", field, value)
|
|
258
|
+
|
|
259
|
+
def is_le(self, field: str, value: int) -> code.Operator:
|
|
260
|
+
"""Intrinsic operation.
|
|
261
|
+
|
|
262
|
+
```c
|
|
263
|
+
return (state[field] <= value);
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
:param field: Property name
|
|
267
|
+
:param value: Integer value
|
|
268
|
+
"""
|
|
269
|
+
|
|
270
|
+
return code.Operator("<=", field, value)
|
|
271
|
+
|
|
272
|
+
def is_ge(self, field: str, value: int) -> code.Operator:
|
|
273
|
+
"""Intrinsic operation.
|
|
274
|
+
|
|
275
|
+
```c
|
|
276
|
+
return (state[field] >= value);
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
:param field: Property name
|
|
280
|
+
:param value: Integer value
|
|
281
|
+
"""
|
|
282
|
+
|
|
283
|
+
return code.Operator(">=", field, value)
|
|
284
|
+
|
|
253
285
|
|
|
254
286
|
# NOTE: I have Nodes and Codes in the same file called `main_code`
|
|
255
287
|
# as a tiny convienience for the sake a protability of not wanting
|
|
@@ -318,7 +350,7 @@ class Builder:
|
|
|
318
350
|
"""Return list of all allocated properties in parser's state."""
|
|
319
351
|
return list(self.privProperties.values())
|
|
320
352
|
|
|
321
|
-
def intBE(self, field: str, bits:int):
|
|
353
|
+
def intBE(self, field: str, bits: int):
|
|
322
354
|
"""
|
|
323
355
|
:param field: State's property name
|
|
324
356
|
:param bits: Number of bits to use
|
|
@@ -337,7 +369,7 @@ class Builder:
|
|
|
337
369
|
def uintBE(self, field: str, bits: int):
|
|
338
370
|
"""
|
|
339
371
|
return a node for unpacking arrays to integers
|
|
340
|
-
|
|
372
|
+
|
|
341
373
|
:param field: State's property name
|
|
342
374
|
:param bits: Number of bits to use
|
|
343
375
|
"""
|
|
@@ -346,9 +378,8 @@ class Builder:
|
|
|
346
378
|
def uintLE(self, field: str, bits: int):
|
|
347
379
|
"""
|
|
348
380
|
return a node for unpacking arrays to integers
|
|
349
|
-
|
|
381
|
+
|
|
350
382
|
:param field: State's property name
|
|
351
383
|
:param bits: Number of bits to use
|
|
352
384
|
"""
|
|
353
385
|
return code.Int(field, bits, False, True)
|
|
354
|
-
|
|
@@ -80,6 +80,24 @@ class IsEqual(FieldValue):
|
|
|
80
80
|
super().__init__("match", "is_equal", field, value)
|
|
81
81
|
|
|
82
82
|
|
|
83
|
+
OperatorsMap = {"<": "lt", ">": "gt", ">=": "ge", "<=": "le"}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class Operator(FieldValue):
|
|
87
|
+
"""An Operator such as `>, <, >=, <=` made for dealing with
|
|
88
|
+
properties with infinate sizes"""
|
|
89
|
+
|
|
90
|
+
def __init__(self, op: str, field: str, value: int) -> None:
|
|
91
|
+
if name := OperatorsMap.get(op):
|
|
92
|
+
super().__init__("match", name, field, value)
|
|
93
|
+
# Make sure our operator can be remebered for later...
|
|
94
|
+
self.op = op
|
|
95
|
+
else:
|
|
96
|
+
raise NotImplementedError(
|
|
97
|
+
f"operator {op} not implemented or doesnt exist yet"
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
83
101
|
class Load(Field):
|
|
84
102
|
def __init__(self, field: str) -> None:
|
|
85
103
|
super().__init__("match", "load", field)
|
|
@@ -92,8 +110,6 @@ class _Match(Code):
|
|
|
92
110
|
super().__init__("match", name)
|
|
93
111
|
|
|
94
112
|
|
|
95
|
-
|
|
96
|
-
|
|
97
113
|
@dataclass
|
|
98
114
|
class IMulAddOptions:
|
|
99
115
|
base: int
|
|
@@ -246,38 +262,41 @@ class Invoke(Node):
|
|
|
246
262
|
self.addEdge(Edge(targetNode, True, numKey, None))
|
|
247
263
|
|
|
248
264
|
|
|
249
|
-
|
|
250
|
-
#
|
|
251
|
-
# this into my version since I am making a very important
|
|
265
|
+
# Not in llparse node-js (yet) But I wanted to implement
|
|
266
|
+
# this into my version since I am making a very important
|
|
252
267
|
# http2 frame parser
|
|
253
268
|
|
|
254
269
|
# SEE: https://github.com/nodejs/llparse-frontend/pull/1
|
|
255
270
|
|
|
256
|
-
|
|
271
|
+
|
|
272
|
+
def build_name(field: str, bits: int, signed: bool, little_endian: bool) -> str:
|
|
257
273
|
result = f"{field}_{'int' if signed else 'uint'}_{bits * 8}"
|
|
258
274
|
if bits > 1:
|
|
259
|
-
return result + (
|
|
275
|
+
return result + ("_le" if little_endian else "be")
|
|
260
276
|
else:
|
|
261
277
|
return result
|
|
262
278
|
|
|
263
279
|
|
|
264
280
|
class Int(Node):
|
|
265
281
|
"""Used for parsing bytes via unpacking"""
|
|
266
|
-
|
|
282
|
+
|
|
283
|
+
def __init__(
|
|
284
|
+
self, field: str, bits: int, signed: bool, little_endian: bool
|
|
285
|
+
) -> None:
|
|
267
286
|
"""
|
|
268
287
|
:param field: State's property name
|
|
269
288
|
:param bits: Number of bits to use
|
|
270
|
-
:param signed: Number is signed
|
|
289
|
+
:param signed: Number is signed
|
|
271
290
|
:param little_endian: true if le, false if be
|
|
272
291
|
"""
|
|
273
292
|
if bits < 0:
|
|
274
293
|
raise ValueError("bits should be a positive integer")
|
|
275
|
-
self.field =
|
|
294
|
+
self.field = field
|
|
276
295
|
self.bits = bits
|
|
277
296
|
self.signed = signed
|
|
278
|
-
self.little_endian
|
|
297
|
+
self.little_endian = little_endian
|
|
279
298
|
super().__init__(build_name(field, bits, signed, little_endian))
|
|
280
|
-
|
|
299
|
+
|
|
281
300
|
|
|
282
301
|
# -- Transfroms --
|
|
283
302
|
|
|
@@ -86,6 +86,14 @@ class IsEqual(FieldValue):
|
|
|
86
86
|
)
|
|
87
87
|
|
|
88
88
|
|
|
89
|
+
class Operator(FieldValue):
|
|
90
|
+
def __init__(self, name: str, field: str, value: int, op: str):
|
|
91
|
+
super().__init__(
|
|
92
|
+
"match", f"is_{field}_{name}_{toCacheKey(value)}", f'{name}_{toCacheKey(value)}', field, value
|
|
93
|
+
)
|
|
94
|
+
self.op = op
|
|
95
|
+
|
|
96
|
+
|
|
89
97
|
class Load(Field):
|
|
90
98
|
"""Subclass of Field"""
|
|
91
99
|
|
|
@@ -60,6 +60,7 @@ class ICodeImplementation:
|
|
|
60
60
|
def __init__(self) -> None:
|
|
61
61
|
return
|
|
62
62
|
|
|
63
|
+
# TODO: (Vizonex) Transform these functions to lamdas instead to conserve space...
|
|
63
64
|
def And(self, c: code.And):
|
|
64
65
|
return IWrap(c)
|
|
65
66
|
|
|
@@ -92,6 +93,9 @@ class ICodeImplementation:
|
|
|
92
93
|
|
|
93
94
|
def Value(self, c: code.Value):
|
|
94
95
|
return IWrap(c)
|
|
96
|
+
|
|
97
|
+
def Operator(self, c: code.Operator):
|
|
98
|
+
return IWrap(c)
|
|
95
99
|
|
|
96
100
|
|
|
97
101
|
class IImplementation:
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
from llparse import LLParse
|
|
2
|
+
from llparse.pybuilder.main_code import Operator
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@pytest.fixture(params=[">", "<", ">=", "<="])
|
|
8
|
+
def op(request: pytest.FixtureRequest) -> str:
|
|
9
|
+
return request.param
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_build_tables():
|
|
13
|
+
# There was a bug with 1.2 of our version that doesn't affect the node-js one where
|
|
14
|
+
# it wouldn't building tables, this attempts to simulate the problem Currenlty this
|
|
15
|
+
# bug is patched now :)
|
|
16
|
+
p = LLParse("lltable")
|
|
17
|
+
start = p.node("start")
|
|
18
|
+
loop = p.node("loop")
|
|
19
|
+
loop.skipTo(start)
|
|
20
|
+
start.match(
|
|
21
|
+
[
|
|
22
|
+
48,
|
|
23
|
+
49,
|
|
24
|
+
50,
|
|
25
|
+
51,
|
|
26
|
+
52,
|
|
27
|
+
53,
|
|
28
|
+
54,
|
|
29
|
+
55,
|
|
30
|
+
56,
|
|
31
|
+
57,
|
|
32
|
+
97,
|
|
33
|
+
98,
|
|
34
|
+
99,
|
|
35
|
+
100,
|
|
36
|
+
101,
|
|
37
|
+
102,
|
|
38
|
+
103,
|
|
39
|
+
104,
|
|
40
|
+
105,
|
|
41
|
+
106,
|
|
42
|
+
107,
|
|
43
|
+
108,
|
|
44
|
+
109,
|
|
45
|
+
110,
|
|
46
|
+
111,
|
|
47
|
+
112,
|
|
48
|
+
113,
|
|
49
|
+
114,
|
|
50
|
+
115,
|
|
51
|
+
116,
|
|
52
|
+
117,
|
|
53
|
+
118,
|
|
54
|
+
119,
|
|
55
|
+
120,
|
|
56
|
+
121,
|
|
57
|
+
122,
|
|
58
|
+
65,
|
|
59
|
+
66,
|
|
60
|
+
67,
|
|
61
|
+
68,
|
|
62
|
+
69,
|
|
63
|
+
70,
|
|
64
|
+
71,
|
|
65
|
+
72,
|
|
66
|
+
73,
|
|
67
|
+
74,
|
|
68
|
+
75,
|
|
69
|
+
76,
|
|
70
|
+
77,
|
|
71
|
+
78,
|
|
72
|
+
79,
|
|
73
|
+
80,
|
|
74
|
+
81,
|
|
75
|
+
82,
|
|
76
|
+
83,
|
|
77
|
+
84,
|
|
78
|
+
85,
|
|
79
|
+
86,
|
|
80
|
+
87,
|
|
81
|
+
88,
|
|
82
|
+
89,
|
|
83
|
+
90,
|
|
84
|
+
33,
|
|
85
|
+
34,
|
|
86
|
+
35,
|
|
87
|
+
36,
|
|
88
|
+
37,
|
|
89
|
+
38,
|
|
90
|
+
39,
|
|
91
|
+
40,
|
|
92
|
+
41,
|
|
93
|
+
42,
|
|
94
|
+
43,
|
|
95
|
+
44,
|
|
96
|
+
45,
|
|
97
|
+
46,
|
|
98
|
+
47,
|
|
99
|
+
58,
|
|
100
|
+
59,
|
|
101
|
+
60,
|
|
102
|
+
61,
|
|
103
|
+
62,
|
|
104
|
+
63,
|
|
105
|
+
64,
|
|
106
|
+
91,
|
|
107
|
+
92,
|
|
108
|
+
93,
|
|
109
|
+
94,
|
|
110
|
+
95,
|
|
111
|
+
96,
|
|
112
|
+
123,
|
|
113
|
+
124,
|
|
114
|
+
125,
|
|
115
|
+
126,
|
|
116
|
+
32,
|
|
117
|
+
9,
|
|
118
|
+
10,
|
|
119
|
+
13,
|
|
120
|
+
11,
|
|
121
|
+
12,
|
|
122
|
+
],
|
|
123
|
+
loop,
|
|
124
|
+
).otherwise(p.error(0, "im a little teapot"))
|
|
125
|
+
|
|
126
|
+
# If there is not a lookup_table this then it has failed me ;-;
|
|
127
|
+
assert "lookup_table" in p.build(start).c
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def test_pausing():
|
|
131
|
+
# Ensure frotentend LoopChecker does not mark off against Pausing
|
|
132
|
+
p = LLParse("lltest")
|
|
133
|
+
s = p.node("start")
|
|
134
|
+
s2 = p.node("start2")
|
|
135
|
+
s.match("p", p.pause(1, "parser was asked to pause").otherwise(s2)).skipTo(s)
|
|
136
|
+
s2.match("p", p.pause(2, "parser was asked to pause again").otherwise(s)).skipTo(s2)
|
|
137
|
+
p.build(s)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def test_operators(op: str):
|
|
141
|
+
test = LLParse("test")
|
|
142
|
+
|
|
143
|
+
test.property("i16", "a")
|
|
144
|
+
|
|
145
|
+
node = test.node("node_1")
|
|
146
|
+
# WARNING: Don't try and do this normally need operator exposed for making fixtures a bit easier.
|
|
147
|
+
node.match(
|
|
148
|
+
"1",
|
|
149
|
+
test.invoke(
|
|
150
|
+
Operator(op, "a", 10), {1: node}, test.error(1, "a is not greater than 10")
|
|
151
|
+
),
|
|
152
|
+
).otherwise(test.error(2, "lol"))
|
|
153
|
+
|
|
154
|
+
t = test.build(node)
|
|
155
|
+
code = t.c.splitlines(keepends=False)
|
|
156
|
+
assert f" return state->a {op} 10;" in code
|
|
157
|
+
if op == ">":
|
|
158
|
+
assert "int test__c_gt_a_10 (" in code
|
|
159
|
+
elif op == "<":
|
|
160
|
+
assert "int test__c_lt_a_10 (" in code
|
|
161
|
+
elif op == ">=":
|
|
162
|
+
assert "int test__c_ge_a_10 (" in code
|
|
163
|
+
elif op == "<=":
|
|
164
|
+
assert "int test__c_le_a_10 (" in code
|
|
@@ -54,7 +54,7 @@ def test_allocate_overlapping_spans(span_alloc: tuple[SpanAllocator, Builder]) -
|
|
|
54
54
|
assert res.max == 1
|
|
55
55
|
|
|
56
56
|
assert len(res.concurrency) == 2
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
# python loves to shuffle things on me :/ but both exist nevertheless
|
|
59
59
|
assert span2 in res.concurrency[0] or span1 in res.concurrency[0]
|
|
60
60
|
assert span1 in res.concurrency[1] or span2 in res.concurrency[1]
|
|
@@ -106,16 +106,22 @@ def test_should_throw_on_loops(span_alloc: tuple[SpanAllocator, Builder]) -> Non
|
|
|
106
106
|
with pytest.raises(Error, match=r"unmatched.*on_data"):
|
|
107
107
|
sa.allocate(start)
|
|
108
108
|
|
|
109
|
-
|
|
109
|
+
|
|
110
|
+
def test_propagate_through_invoke_map(span_alloc: tuple[SpanAllocator, Builder]):
|
|
110
111
|
sa, b = span_alloc
|
|
111
|
-
start = b.node(
|
|
112
|
-
span = b.span(b.code.span(
|
|
112
|
+
start = b.node("start")
|
|
113
|
+
span = b.span(b.code.span("llparse__on_data"))
|
|
113
114
|
|
|
114
|
-
b.property(
|
|
115
|
+
b.property("i8", "custom")
|
|
115
116
|
|
|
116
|
-
start.otherwise(
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
start.otherwise(
|
|
118
|
+
b.invoke(
|
|
119
|
+
b.code.load("custom"),
|
|
120
|
+
{
|
|
121
|
+
0: span.end().skipTo(start),
|
|
122
|
+
},
|
|
123
|
+
span.end().skipTo(start),
|
|
124
|
+
)
|
|
125
|
+
)
|
|
119
126
|
|
|
120
127
|
sa.allocate(span.start(start))
|
|
121
|
-
|
|
@@ -1,35 +0,0 @@
|
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|