-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuilder.py
More file actions
412 lines (299 loc) · 11.6 KB
/
builder.py
File metadata and controls
412 lines (299 loc) · 11.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
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
91
92
93
94
95
96
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
from typing import Literal
from ..pybuilder import main_code as code
import warnings
# typehinting node and code (TODO: Vizonex) Lets seperate the modules soon...
node = code
i8 = "i8"
i16 = "i16"
i32 = "i32"
i64 = "i64"
ptr = "ptr"
PropertyTypes = [i8, i16, i32, i64, ptr]
class Property:
def __init__(self, ty: str, name: str) -> None:
if ty not in PropertyTypes:
raise Exception(
f"Can't use property : {ty} Because it is not a valid property type in ['i8' , 'i16' , 'i32' , 'i64' , 'ptr']"
)
if any([(True if n in name else False) for n in ["\\\\", "/", " "]]):
raise Exception(
f"Flag/Pointer Name:{name} cannot have spaces or other strange characters in C..."
)
self.name = name
self.ty = ty
class Creator:
"""API for creating external callbacks and intrinsic operations."""
def __init__(self) -> None:
return
# TODO (Vizonex): I think we could add a node for property copying from a to b
# and addition and subtraction nodes like a counter unlike consume and I think the typescript
# Library could have the same features as us. I seem to come up with new nodes often
# which is a rare thing to see. :)
# Thank goodness I can do C example documentation in here :) - Vizonex
def match(self, name: str) -> code.Match:
"""Create an external callback that **has no** `value` argument.
This callback can be used in all `Invoke` nodes except those that are
targets of `.select()` method.
C signature of callback must be:
```c
int name(llparse_t* state, char* p, char* endp)
```
Where `llparse_t` is parser state's type name.
:param name: External function name.
"""
return code._Match(name)
def value(self, name: str) -> code.Value:
"""
Create an external callback that **has** `value` argument.
This callback can be used only in `Invoke` nodes that are targets of
`.select()` method.
C signature of callback must be:
```c
int name(llparse_t* state, char* p, char* endp, int value)
```
Where `llparse_t` is parser state's type name.
:param name: External function name.
"""
return code.Value(name)
def span(self, name: str) -> code._Span:
"""Create an external span callback.
This callback can be used only in `Span` constructor.
The difference is that in typescript it's an Arbitrary Span
in python it's called SpanCallback to try not to be as confusing...
C signature of callback must be:
```c
int name(llparse_t* state, char* p, char* endp)
```
NOTE: non-zero return value is treated as resumable error.
:param name: External function name.
"""
return code._Span(name)
def store(self, field: str) -> code.Store:
"""
Intrinsic operation. Stores `value` from `.select()` node into the state's
property with the name specified by `field`, returns zero.
```c
state[field] = value;
return 0;
```
:param field: Property name
"""
return code.Store(field)
def load(self, field: str) -> code.Load:
"""Intrinsic operation. Loads and returns state's property with the name
specified by `field`.
The value of the property is either truncated or zero-extended to fit into
32-bit unsigned integer.
```c
return state[field];
```
:param field: Property name.
"""
return code.Load(field)
def mulAdd(
self, field: str, base: int, max: int | None = None, signed: bool = False
) -> code.MulAdd:
"""Intrinsic operation. Takes `value` from `.select()`, state's property
with the name `field` and does:
```c
field = state[field];
field *= options.base;
field += value;
state[field] = field;
return 0; // or 1 on overflow
```
Return values are:
- 0 - success
- 1 - overflow
Unlike in Typescript, The values of `IMulAddOptions` have been added here
since it's Python , not Typescript
:param field: Property name
:param base: Value to multiply the property with in the first step
:param max: Maximum value of the property. If at any point of computation the
intermediate result exceeds it - `mulAdd` returns 1 (overflow).
:param signed: If `true` - all arithmetics perfomed by `mulAdd` will be signed.
Default value: `false`"""
return code.MulAdd(field, base, max, signed)
def update(self, field: str, value: int) -> code.Update:
"""
Intrinsic operation. Puts `value` integer into the state's property with
the name specified by `field`.
state[field] = value;
return 0;
:param field: Property name
:param value: Integer value to be stored into the property.
"""
return code.Update(field, value)
def isEqual(self, field: str, value: str) -> code.IsEqual:
"""Intrinsic operation.
```c
state[field] &= value
return 0;
```
:param field: Property name
:param value: Integer value
"""
return code.IsEqual(field, value)
# NOTE : Unlike in typescript lowercase "and" & "or"
# cannot be used this might have to be
# throughly addressed - Vizonex
def And(self, field: str, value: int) -> code.And:
"""Intrinsic operation.
```c
state[field] &= value
return 0;
```
:param field: Property name
:param value: Integer value
"""
return code.And(field, value)
def Or(self, field: str, value: int) -> code.Or:
"""
Intrinsic operation.
state[field] |= value
return 0;
:param field: Property name
:param value: Integer value
This will allow us to set our own flags at will
"""
return code.Or(field, value)
def test(self, field: str, value: int) -> code.Test:
"""Intrinsic operation.
```c
return (state[field] & value) == value ? 1 : 0;
```
:param field: Property name
:param value: Integer value
"""
return code.Test(field, value)
def is_gt(self, field: str, value: int) -> code.Operator:
"""Intrinsic operation.
```c
return (state[field] > value);
```
:param field: Property name
:param value: Integer value
"""
return code.Operator(">", field, value)
def is_lt(self, field: str, value: int) -> code.Operator:
"""Intrinsic operation.
```c
return (state[field] < value);
```
:param field: Property name
:param value: Integer value
"""
return code.Operator("<", field, value)
def is_le(self, field: str, value: int) -> code.Operator:
"""Intrinsic operation.
```c
return (state[field] <= value);
```
:param field: Property name
:param value: Integer value
"""
return code.Operator("<=", field, value)
def is_ge(self, field: str, value: int) -> code.Operator:
"""Intrinsic operation.
```c
return (state[field] >= value);
```
:param field: Property name
:param value: Integer value
"""
return code.Operator(">=", field, value)
# NOTE: I have Nodes and Codes in the same file called `main_code`
# as a tiny convienience for the sake a protability of not wanting
# to Cause Hell for those wishing to move files to other folder
# quickly this was done during development
# as a caution of not needing to
# open too many ides and numerous windows - Vizonex
# so node.Match in typescript is code.Match in python...
# TODO (Vizonex) Add more Documentation later , I got tired of it...
class Builder:
def __init__(self) -> None:
self.code = Creator()
" API for creating external callbacks and intrinsic operations."
self.transform = code.TransfromCreator()
self.privProperties: dict[str, Property] = {}
def node(self, name: str):
return code.Match(name)
def error(self, errorCode: int, reason: str):
return code.Error(errorCode, reason)
def invoke(
self,
fn: code.Code,
Map: dict[int, code.Node] | code.Node | None = None,
otherwise: code.Node | None = None,
):
if not Map:
res = code.Invoke(fn, {})
elif isinstance(Map, code.Node):
res = code.Invoke(fn, {})
otherwise = Map
else:
res = code.Invoke(fn, Map)
if otherwise:
res.otherwise(otherwise)
return res
def consume(self, field: str):
return code.Comsume(field)
def pause(self, errorCode: int, reason: str):
return code.Pause(errorCode, reason)
# NOTE SpanCallback Can Really be any node, just needed to Calrify that - Vizonex
def span(self, callback: code._Span):
return code.Span(callback)
def property(self, ty: Literal["i8", "i16", "i32", "i64", "ptr"], name: str):
if ty not in PropertyTypes:
raise TypeError(f"ty:{ty} is not an existing Parser Property")
if self.privProperties.get(name):
raise RuntimeError(f"Duplicate property with name:{name}")
self.privProperties[name] = Property(ty, name)
def properties(self) -> list[Property]:
"""Return list of all allocated properties in parser's state."""
return list(self.privProperties.values())
def intBE(self, field: str, bits: int):
"""
:param field: State's property name
:param bits: Number of bits to use
"""
return code.Int(field, bits, True, False)
def intLE(self, field: str, bits: int):
"""
return a node for unpacking arrays to integers
:param field: State's property name
:param bits: Number of bits to use
"""
return code.Int(field, bits, True, True)
def uintBE(self, field: str, bits: int):
"""
return a node for unpacking arrays to integers
:param field: State's property name
:param bits: Number of bits to use
"""
return code.Int(field, bits, False, False)
def uintLE(self, field: str, bits: int):
"""
return a node for unpacking arrays to integers
:param field: State's property name
:param bits: Number of bits to use
"""
return code.Int(field, bits, False, True)
def skip_multiple(self, value: int):
"""
A Numerous number of nodes to have being skipped over
this value must be greater than 1 but know that passing
anything higher than 256 may lead to performance regressions
with your parser.
:param value: a value greater than 2
"""
if value <= 1:
raise ValueError(f"A value that is <= 1 for skip_multiple defeats it's purpose. Got {value}")
# TODO: Remove this warning when an alternative method such as
# Creating a u64 dummy field with consume can be readily supplied (Code generation side of things).
if value >= 256:
warnings.warn(
f"skipping nodes greater than 256 in this case: {value}"
" may cause significant performance regressions with the parser " \
" being generated", UserWarning)
return code.LengthConsume(value)