dissect.cstruct 4.8.dev8__py3-none-any.whl → 5.0.dev0__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.
- dissect/cstruct/cstruct.py +32 -9
- dissect/cstruct/exceptions.py +4 -4
- dissect/cstruct/expression.py +189 -271
- dissect/cstruct/lexer.py +574 -0
- dissect/cstruct/parser.py +425 -592
- dissect/cstruct/utils.py +24 -10
- {dissect_cstruct-4.8.dev8.dist-info → dissect_cstruct-5.0.dev0.dist-info}/METADATA +1 -1
- {dissect_cstruct-4.8.dev8.dist-info → dissect_cstruct-5.0.dev0.dist-info}/RECORD +15 -12
- {dissect_cstruct-4.8.dev8.dist-info → dissect_cstruct-5.0.dev0.dist-info}/WHEEL +1 -1
- dissect_cstruct-5.0.dev0.dist-info/scm_file_list.json +78 -0
- dissect_cstruct-5.0.dev0.dist-info/scm_version.json +8 -0
- {dissect_cstruct-4.8.dev8.dist-info → dissect_cstruct-5.0.dev0.dist-info}/entry_points.txt +0 -0
- {dissect_cstruct-4.8.dev8.dist-info → dissect_cstruct-5.0.dev0.dist-info}/licenses/COPYRIGHT +0 -0
- {dissect_cstruct-4.8.dev8.dist-info → dissect_cstruct-5.0.dev0.dist-info}/licenses/LICENSE +0 -0
- {dissect_cstruct-4.8.dev8.dist-info → dissect_cstruct-5.0.dev0.dist-info}/top_level.txt +0 -0
dissect/cstruct/cstruct.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import copy
|
|
3
4
|
import ctypes as _ctypes
|
|
4
5
|
import struct
|
|
5
6
|
import sys
|
|
@@ -9,7 +10,7 @@ from typing import TYPE_CHECKING, Any, BinaryIO, TypeVar, cast
|
|
|
9
10
|
|
|
10
11
|
from dissect.cstruct.exceptions import ResolveError
|
|
11
12
|
from dissect.cstruct.expression import Expression
|
|
12
|
-
from dissect.cstruct.parser import CStyleParser
|
|
13
|
+
from dissect.cstruct.parser import CStyleParser
|
|
13
14
|
from dissect.cstruct.types import (
|
|
14
15
|
LEB128,
|
|
15
16
|
BaseArray,
|
|
@@ -25,6 +26,7 @@ from dissect.cstruct.types import (
|
|
|
25
26
|
Void,
|
|
26
27
|
Wchar,
|
|
27
28
|
)
|
|
29
|
+
from dissect.cstruct.types.base import MetaType
|
|
28
30
|
|
|
29
31
|
if TYPE_CHECKING:
|
|
30
32
|
from collections.abc import Iterable
|
|
@@ -54,7 +56,6 @@ class cstruct:
|
|
|
54
56
|
self.endian = endian
|
|
55
57
|
|
|
56
58
|
self.consts = {}
|
|
57
|
-
self.lookups = {}
|
|
58
59
|
self.includes = []
|
|
59
60
|
# fmt: off
|
|
60
61
|
self.typedefs = {
|
|
@@ -206,6 +207,24 @@ class cstruct:
|
|
|
206
207
|
|
|
207
208
|
raise AttributeError(f"Invalid attribute: {attr}")
|
|
208
209
|
|
|
210
|
+
def __copy__(self) -> cstruct:
|
|
211
|
+
cs = cstruct(endian=self.endian, pointer=self.pointer.__name__)
|
|
212
|
+
cs._anonymous_count = self._anonymous_count
|
|
213
|
+
cs.consts = self.consts.copy()
|
|
214
|
+
cs.includes = self.includes.copy()
|
|
215
|
+
|
|
216
|
+
# Update typedefs to point to the new cstruct instance
|
|
217
|
+
for name, type in self.typedefs.items():
|
|
218
|
+
if name in cs.typedefs:
|
|
219
|
+
continue
|
|
220
|
+
|
|
221
|
+
if isinstance(type, MetaType):
|
|
222
|
+
new_type = copy.copy(type)
|
|
223
|
+
new_type.cs = cs
|
|
224
|
+
cs.typedefs[name] = new_type
|
|
225
|
+
|
|
226
|
+
return cs
|
|
227
|
+
|
|
209
228
|
def _next_anonymous(self) -> str:
|
|
210
229
|
name = f"__anonymous_{self._anonymous_count}__"
|
|
211
230
|
self._anonymous_count += 1
|
|
@@ -253,11 +272,7 @@ class cstruct:
|
|
|
253
272
|
Definitions can be parsed using different parsers. Currently, there's
|
|
254
273
|
only one supported parser - DEF_CSTYLE. Parsers can add types and
|
|
255
274
|
modify this cstruct instance. Arguments can be passed to parsers
|
|
256
|
-
using kwargs
|
|
257
|
-
|
|
258
|
-
The CSTYLE parser was recently replaced with token based parser,
|
|
259
|
-
instead of a strictly regex based one. The old parser is still available
|
|
260
|
-
by using DEF_LEGACY.
|
|
275
|
+
using ``kwargs``.
|
|
261
276
|
|
|
262
277
|
Args:
|
|
263
278
|
definition: The definition to parse.
|
|
@@ -267,9 +282,9 @@ class cstruct:
|
|
|
267
282
|
deftype = deftype or cstruct.DEF_CSTYLE
|
|
268
283
|
|
|
269
284
|
if deftype == cstruct.DEF_CSTYLE:
|
|
270
|
-
TokenParser(self, **kwargs).parse(definition)
|
|
271
|
-
elif deftype == cstruct.DEF_LEGACY:
|
|
272
285
|
CStyleParser(self, **kwargs).parse(definition)
|
|
286
|
+
else:
|
|
287
|
+
raise ValueError(f"Unknown definition type: {deftype}")
|
|
273
288
|
|
|
274
289
|
return self
|
|
275
290
|
|
|
@@ -328,6 +343,14 @@ class cstruct:
|
|
|
328
343
|
|
|
329
344
|
raise ResolveError(f"Recursion limit exceeded while resolving type {name}")
|
|
330
345
|
|
|
346
|
+
def copy(self) -> cstruct:
|
|
347
|
+
"""Create a copy of this cstruct instance.
|
|
348
|
+
|
|
349
|
+
Returns:
|
|
350
|
+
A new cstruct instance with the same types and settings as this one.
|
|
351
|
+
"""
|
|
352
|
+
return copy.copy(self)
|
|
353
|
+
|
|
331
354
|
def _make_type(
|
|
332
355
|
self,
|
|
333
356
|
name: str,
|
dissect/cstruct/exceptions.py
CHANGED
|
@@ -5,6 +5,10 @@ class Error(Exception):
|
|
|
5
5
|
pass
|
|
6
6
|
|
|
7
7
|
|
|
8
|
+
class LexerError(Error):
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
8
12
|
class ParserError(Error):
|
|
9
13
|
pass
|
|
10
14
|
|
|
@@ -23,7 +27,3 @@ class ArraySizeError(Error):
|
|
|
23
27
|
|
|
24
28
|
class ExpressionParserError(Error):
|
|
25
29
|
pass
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class ExpressionTokenizerError(Error):
|
|
29
|
-
pass
|
dissect/cstruct/expression.py
CHANGED
|
@@ -1,304 +1,222 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
from typing import TYPE_CHECKING, ClassVar
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
5
4
|
|
|
6
|
-
from dissect.cstruct.exceptions import ExpressionParserError
|
|
5
|
+
from dissect.cstruct.exceptions import ExpressionParserError
|
|
6
|
+
from dissect.cstruct.lexer import IDENTIFIER_TYPES, Lexer, TokenCursor, TokenType
|
|
7
|
+
from dissect.cstruct.utils import offsetof, sizeof
|
|
7
8
|
|
|
8
9
|
if TYPE_CHECKING:
|
|
9
10
|
from collections.abc import Callable
|
|
10
11
|
|
|
11
12
|
from dissect.cstruct import cstruct
|
|
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
|
-
self.consume()
|
|
67
|
-
return True
|
|
68
|
-
|
|
69
|
-
return False
|
|
70
|
-
|
|
71
|
-
def consume(self) -> None:
|
|
72
|
-
self.pos += 1
|
|
73
|
-
|
|
74
|
-
def eol(self) -> bool:
|
|
75
|
-
return self.pos >= len(self.expression)
|
|
76
|
-
|
|
77
|
-
def get_token(self) -> str:
|
|
78
|
-
if self.eol():
|
|
79
|
-
raise ExpressionTokenizerError(f"Out of bounds index: {self.pos}, length: {len(self.expression)}")
|
|
80
|
-
return self.expression[self.pos]
|
|
81
|
-
|
|
82
|
-
def tokenize(self) -> list[str]:
|
|
83
|
-
token = ""
|
|
84
|
-
|
|
85
|
-
# Loop over expression runs in linear time
|
|
86
|
-
while not self.eol():
|
|
87
|
-
# If token is a single character operand add it to tokens
|
|
88
|
-
if self.match(self.operator):
|
|
89
|
-
continue
|
|
90
|
-
|
|
91
|
-
# If token is a single digit, keep looping over expression and build the number
|
|
92
|
-
if self.match(self.digit, consume=False, append=False):
|
|
93
|
-
token += self.get_token()
|
|
94
|
-
self.consume()
|
|
95
|
-
|
|
96
|
-
# Support for binary and hexadecimal notation
|
|
97
|
-
if self.match(expected=HEXBIN_SUFFIX, consume=False, append=False):
|
|
98
|
-
token += self.get_token()
|
|
99
|
-
self.consume()
|
|
100
|
-
|
|
101
|
-
while self.match(self.hexdigit, consume=False, append=False):
|
|
102
|
-
token += self.get_token()
|
|
103
|
-
self.consume()
|
|
104
|
-
if self.eol():
|
|
105
|
-
break
|
|
106
|
-
|
|
107
|
-
# Checks for suffixes in numbers
|
|
108
|
-
if self.match(expected={"u", "U"}, consume=False, append=False):
|
|
109
|
-
self.consume()
|
|
110
|
-
self.match(expected={"l", "L"}, append=False)
|
|
111
|
-
self.match(expected={"l", "L"}, append=False)
|
|
112
|
-
|
|
113
|
-
elif self.match(expected={"l", "L"}, append=False):
|
|
114
|
-
self.match(expected={"l", "L"}, append=False)
|
|
115
|
-
self.match(expected={"u", "U"}, append=False)
|
|
116
|
-
else:
|
|
117
|
-
pass
|
|
118
|
-
|
|
119
|
-
# Number cannot end on x or b in the case of binary or hexadecimal notation
|
|
120
|
-
if len(token) == 2 and token[-1] in HEXBIN_SUFFIX:
|
|
121
|
-
raise ExpressionTokenizerError("Invalid binary or hex notation")
|
|
122
|
-
|
|
123
|
-
if len(token) > 1 and token[0] == "0" and token[1] not in HEXBIN_SUFFIX:
|
|
124
|
-
token = token[:1] + "o" + token[1:]
|
|
125
|
-
self.tokens.append(token)
|
|
126
|
-
token = ""
|
|
127
|
-
|
|
128
|
-
# If token is alpha or underscore we need to build the identifier
|
|
129
|
-
elif self.match(self.alpha, consume=False, append=False) or self.match(
|
|
130
|
-
expected="_", consume=False, append=False
|
|
131
|
-
):
|
|
132
|
-
while self.match(self.alnum, consume=False, append=False) or self.match(
|
|
133
|
-
expected="_", consume=False, append=False
|
|
134
|
-
):
|
|
135
|
-
token += self.get_token()
|
|
136
|
-
self.consume()
|
|
137
|
-
if self.eol():
|
|
138
|
-
break
|
|
139
|
-
self.tokens.append(token)
|
|
140
|
-
token = ""
|
|
141
|
-
# If token is length 2 operand make sure next character is part of length 2 operand append to tokens
|
|
142
|
-
elif self.match(expected=">", append=False) and self.match(expected=">", append=False):
|
|
143
|
-
self.tokens.append(">>")
|
|
144
|
-
elif self.match(expected="<", append=False) and self.match(expected="<", append=False):
|
|
145
|
-
self.tokens.append("<<")
|
|
146
|
-
elif self.match(expected={" ", "\n", "\t"}, append=False):
|
|
147
|
-
continue
|
|
148
|
-
else:
|
|
149
|
-
raise ExpressionTokenizerError(
|
|
150
|
-
f"Tokenizer does not recognize following token '{self.expression[self.pos]}'"
|
|
151
|
-
)
|
|
152
|
-
return self.tokens
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
class Expression:
|
|
13
|
+
from dissect.cstruct.lexer import Token
|
|
14
|
+
|
|
15
|
+
BINARY_OPERATORS: dict[TokenType, Callable[[int, int], int]] = {
|
|
16
|
+
TokenType.PIPE: lambda a, b: a | b,
|
|
17
|
+
TokenType.CARET: lambda a, b: a ^ b,
|
|
18
|
+
TokenType.AMPERSAND: lambda a, b: a & b,
|
|
19
|
+
TokenType.LSHIFT: lambda a, b: a << b,
|
|
20
|
+
TokenType.RSHIFT: lambda a, b: a >> b,
|
|
21
|
+
TokenType.PLUS: lambda a, b: a + b,
|
|
22
|
+
TokenType.MINUS: lambda a, b: a - b,
|
|
23
|
+
TokenType.STAR: lambda a, b: a * b,
|
|
24
|
+
TokenType.SLASH: lambda a, b: a // b,
|
|
25
|
+
TokenType.PERCENT: lambda a, b: a % b,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
UNARY_OPERATORS: dict[TokenType, Callable[[int], int]] = {
|
|
29
|
+
TokenType.UNARY_MINUS: lambda a: -a,
|
|
30
|
+
TokenType.TILDE: lambda a: ~a,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
OPERATORS = set(BINARY_OPERATORS.keys()) | set(UNARY_OPERATORS.keys())
|
|
34
|
+
|
|
35
|
+
FUNCTION_TOKENS = {
|
|
36
|
+
TokenType.SIZEOF: 1,
|
|
37
|
+
TokenType.OFFSETOF: 2,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
PRECEDENCE_LEVELS = {
|
|
41
|
+
TokenType.PIPE: 0,
|
|
42
|
+
TokenType.CARET: 1,
|
|
43
|
+
TokenType.AMPERSAND: 2,
|
|
44
|
+
TokenType.LSHIFT: 3,
|
|
45
|
+
TokenType.RSHIFT: 3,
|
|
46
|
+
TokenType.PLUS: 4,
|
|
47
|
+
TokenType.MINUS: 4,
|
|
48
|
+
TokenType.STAR: 5,
|
|
49
|
+
TokenType.SLASH: 5,
|
|
50
|
+
TokenType.PERCENT: 5,
|
|
51
|
+
TokenType.UNARY_MINUS: 6,
|
|
52
|
+
TokenType.TILDE: 6,
|
|
53
|
+
# Functions
|
|
54
|
+
TokenType.SIZEOF: 7,
|
|
55
|
+
TokenType.OFFSETOF: 7,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def precedence(o1: TokenType, o2: TokenType) -> bool:
|
|
60
|
+
# Unary (prefix) operators are right-associative, so use strict greater-than
|
|
61
|
+
if o2 in UNARY_OPERATORS:
|
|
62
|
+
return PRECEDENCE_LEVELS[o1] > PRECEDENCE_LEVELS[o2]
|
|
63
|
+
return PRECEDENCE_LEVELS[o1] >= PRECEDENCE_LEVELS[o2]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class Expression(TokenCursor):
|
|
156
67
|
"""Expression parser for calculations in definitions."""
|
|
157
68
|
|
|
158
|
-
binary_operators: ClassVar[dict[str, Callable[[int, int], int]]] = {
|
|
159
|
-
"|": lambda a, b: a | b,
|
|
160
|
-
"^": lambda a, b: a ^ b,
|
|
161
|
-
"&": lambda a, b: a & b,
|
|
162
|
-
"<<": lambda a, b: a << b,
|
|
163
|
-
">>": lambda a, b: a >> b,
|
|
164
|
-
"+": lambda a, b: a + b,
|
|
165
|
-
"-": lambda a, b: a - b,
|
|
166
|
-
"*": lambda a, b: a * b,
|
|
167
|
-
"/": lambda a, b: a // b,
|
|
168
|
-
"%": lambda a, b: a % b,
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
unary_operators: ClassVar[dict[str, Callable[[int], int]]] = {
|
|
172
|
-
"u": lambda a: -a,
|
|
173
|
-
"~": lambda a: ~a,
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
precedence_levels: ClassVar[dict[str, int]] = {
|
|
177
|
-
"|": 0,
|
|
178
|
-
"^": 1,
|
|
179
|
-
"&": 2,
|
|
180
|
-
"<<": 3,
|
|
181
|
-
">>": 3,
|
|
182
|
-
"+": 4,
|
|
183
|
-
"-": 4,
|
|
184
|
-
"*": 5,
|
|
185
|
-
"/": 5,
|
|
186
|
-
"%": 5,
|
|
187
|
-
"u": 6,
|
|
188
|
-
"~": 6,
|
|
189
|
-
"sizeof": 6,
|
|
190
|
-
}
|
|
191
|
-
|
|
192
69
|
def __init__(self, expression: str):
|
|
193
70
|
self.expression = expression
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
71
|
+
|
|
72
|
+
tokens = Lexer(expression).tokenize()
|
|
73
|
+
super().__init__(tokens)
|
|
74
|
+
self._stack: list[TokenType] = []
|
|
75
|
+
self._queue: list[int | str] = []
|
|
197
76
|
|
|
198
77
|
def __repr__(self) -> str:
|
|
199
78
|
return self.expression
|
|
200
79
|
|
|
201
|
-
def
|
|
202
|
-
|
|
80
|
+
def _reset(self) -> None:
|
|
81
|
+
"""Reset the expression state for a new input."""
|
|
82
|
+
self._reset_cursor()
|
|
83
|
+
self._stack = []
|
|
84
|
+
self._queue = []
|
|
203
85
|
|
|
204
|
-
def
|
|
205
|
-
|
|
206
|
-
res = 0
|
|
86
|
+
def _error(self, msg: str, *, token: Token | None = None) -> ExpressionParserError:
|
|
87
|
+
return ExpressionParserError(f"line {(token if token is not None else self._current()).line}: {msg}")
|
|
207
88
|
|
|
208
|
-
|
|
209
|
-
|
|
89
|
+
def _evaluate_expression(self, cs: cstruct) -> None:
|
|
90
|
+
operator = self._stack.pop(-1)
|
|
91
|
+
result = 0
|
|
210
92
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
res = self.unary_operators[operator](right)
|
|
214
|
-
else:
|
|
215
|
-
if len(self.queue) < 1:
|
|
93
|
+
if operator in UNARY_OPERATORS:
|
|
94
|
+
if len(self._queue) < 1:
|
|
216
95
|
raise ExpressionParserError("Invalid expression: not enough operands")
|
|
217
96
|
|
|
218
|
-
|
|
219
|
-
|
|
97
|
+
result = UNARY_OPERATORS[operator](self._queue.pop(-1))
|
|
98
|
+
elif operator in BINARY_OPERATORS:
|
|
99
|
+
if len(self._queue) < 2:
|
|
100
|
+
raise ExpressionParserError("Invalid expression: not enough operands")
|
|
101
|
+
|
|
102
|
+
right = self._queue.pop(-1)
|
|
103
|
+
left = self._queue.pop(-1)
|
|
104
|
+
result = BINARY_OPERATORS[operator](left, right)
|
|
105
|
+
elif operator in FUNCTION_TOKENS:
|
|
106
|
+
num_args = FUNCTION_TOKENS[operator]
|
|
107
|
+
if len(self._queue) < num_args:
|
|
108
|
+
raise ExpressionParserError("Invalid expression: not enough operands")
|
|
220
109
|
|
|
221
|
-
|
|
110
|
+
args = [self._queue.pop(-1) for _ in range(num_args)][::-1]
|
|
111
|
+
if operator == TokenType.SIZEOF:
|
|
112
|
+
type_ = cs.resolve(args[0])
|
|
113
|
+
result = sizeof(type_)
|
|
114
|
+
elif operator == TokenType.OFFSETOF:
|
|
115
|
+
type_ = cs.resolve(args[0])
|
|
116
|
+
result = offsetof(type_, args[1])
|
|
222
117
|
|
|
223
|
-
|
|
224
|
-
return token.isnumeric() or (len(token) > 2 and token[0] == "0" and token[1] in ("x", "X", "b", "B", "o", "O"))
|
|
118
|
+
self._queue.append(result)
|
|
225
119
|
|
|
226
120
|
def evaluate(self, cs: cstruct, context: dict[str, int] | None = None) -> int:
|
|
227
121
|
"""Evaluates an expression using a Shunting-Yard implementation."""
|
|
228
|
-
self.
|
|
229
|
-
self.queue = []
|
|
230
|
-
operators = set(self.binary_operators.keys()) | set(self.unary_operators.keys())
|
|
231
|
-
|
|
122
|
+
self._reset()
|
|
232
123
|
context = context or {}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
self.tokens[i] = "u"
|
|
240
|
-
continue
|
|
241
|
-
if self.tokens[i - 1] in operators or self.tokens[i - 1] == "u" or self.tokens[i - 1] == "(":
|
|
242
|
-
self.tokens[i] = "u"
|
|
243
|
-
continue
|
|
244
|
-
|
|
245
|
-
i = 0
|
|
246
|
-
while i < len(tmp_expression):
|
|
247
|
-
current_token = tmp_expression[i]
|
|
248
|
-
if self.is_number(current_token):
|
|
249
|
-
self.queue.append(int(current_token, 0))
|
|
250
|
-
elif current_token in context:
|
|
251
|
-
self.queue.append(int(context[current_token]))
|
|
252
|
-
elif current_token in cs.consts:
|
|
253
|
-
self.queue.append(int(cs.consts[current_token]))
|
|
254
|
-
elif current_token in self.unary_operators:
|
|
255
|
-
self.stack.append(current_token)
|
|
256
|
-
elif current_token == "sizeof":
|
|
257
|
-
if len(tmp_expression) < i + 3 or (tmp_expression[i + 1] != "(" or tmp_expression[i + 3] != ")"):
|
|
258
|
-
raise ExpressionParserError("Invalid sizeof operation")
|
|
259
|
-
self.queue.append(len(cs.resolve(tmp_expression[i + 2])))
|
|
260
|
-
i += 3
|
|
261
|
-
elif current_token in operators:
|
|
124
|
+
|
|
125
|
+
while (token := self._current()).type != TokenType.EOF:
|
|
126
|
+
if token.type == TokenType.NUMBER:
|
|
127
|
+
self._queue.append(int(self._take().value, 0))
|
|
128
|
+
|
|
129
|
+
elif token.type in OPERATORS:
|
|
262
130
|
while (
|
|
263
|
-
len(self.
|
|
131
|
+
len(self._stack) != 0
|
|
132
|
+
and self._stack[-1] != TokenType.LPAREN
|
|
133
|
+
and precedence(self._stack[-1], token.type)
|
|
264
134
|
):
|
|
265
|
-
self.
|
|
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
|
-
|
|
135
|
+
self._evaluate_expression(cs)
|
|
136
|
+
|
|
137
|
+
self._stack.append(self._take().type)
|
|
138
|
+
|
|
139
|
+
elif token.type in FUNCTION_TOKENS:
|
|
140
|
+
func = self._take().type
|
|
141
|
+
self._stack.append(func)
|
|
142
|
+
|
|
143
|
+
self._expect(TokenType.LPAREN)
|
|
144
|
+
|
|
145
|
+
num_args = FUNCTION_TOKENS[func]
|
|
146
|
+
while num_args > 1:
|
|
147
|
+
self._queue.append(self._collect_until(TokenType.COMMA))
|
|
148
|
+
self._expect(TokenType.COMMA)
|
|
149
|
+
num_args -= 1
|
|
150
|
+
|
|
151
|
+
self._queue.append(self._collect_until(TokenType.RPAREN))
|
|
152
|
+
self._expect(TokenType.RPAREN)
|
|
153
|
+
|
|
154
|
+
# Evaluate immediately
|
|
155
|
+
self._evaluate_expression(cs)
|
|
156
|
+
|
|
157
|
+
elif token.type in IDENTIFIER_TYPES:
|
|
158
|
+
ident = self._take().value
|
|
159
|
+
|
|
160
|
+
obj = None
|
|
161
|
+
for i, part in enumerate(ident.split(".")):
|
|
162
|
+
if i == 0:
|
|
163
|
+
if part in context:
|
|
164
|
+
obj = context[part]
|
|
165
|
+
elif part in cs.consts:
|
|
166
|
+
obj = cs.consts[part]
|
|
167
|
+
elif part in cs.typedefs:
|
|
168
|
+
obj = cs.resolve(part)
|
|
169
|
+
else:
|
|
170
|
+
raise self._error(f"Unknown identifier: '{ident}'", token=token)
|
|
171
|
+
else:
|
|
172
|
+
if isinstance(obj, dict) and part in obj:
|
|
173
|
+
obj = obj[part]
|
|
174
|
+
elif hasattr(obj, part):
|
|
175
|
+
obj = getattr(obj, part)
|
|
176
|
+
else:
|
|
177
|
+
raise self._error(f"Unknown identifier: '{ident}'", token=token)
|
|
178
|
+
|
|
179
|
+
try:
|
|
180
|
+
self._queue.append(int(obj))
|
|
181
|
+
except (ValueError, TypeError):
|
|
182
|
+
raise self._error(f"Identifier '{ident}' does not resolve to an integer", token=token)
|
|
183
|
+
|
|
184
|
+
elif token.type == TokenType.LPAREN:
|
|
185
|
+
if self._previous().type == TokenType.NUMBER:
|
|
186
|
+
raise self._error(
|
|
187
|
+
f"Parser expected sizeof or an arethmethic operator instead got: '{self._previous().value}'",
|
|
188
|
+
token=self._previous(),
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
self._stack.append(self._take().type)
|
|
192
|
+
|
|
193
|
+
elif token.type == TokenType.RPAREN:
|
|
194
|
+
if self._previous().type == TokenType.LPAREN:
|
|
195
|
+
raise self._error(
|
|
196
|
+
"Parser expected an expression, instead received empty parenthesis.",
|
|
197
|
+
token=self._previous(),
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
if len(self._stack) == 0:
|
|
201
|
+
raise self._error("Mismatched parentheses")
|
|
202
|
+
|
|
203
|
+
while self._stack[-1] != TokenType.LPAREN:
|
|
204
|
+
self._evaluate_expression(cs)
|
|
205
|
+
if len(self._stack) == 0:
|
|
206
|
+
raise self._error("Mismatched parentheses")
|
|
207
|
+
|
|
208
|
+
self._stack.pop(-1) # Pop the '('
|
|
209
|
+
self._take()
|
|
294
210
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
raise ExpressionParserError("Invalid expression")
|
|
211
|
+
else:
|
|
212
|
+
raise self._error(f"Unmatched token: '{token.value}'", token=token)
|
|
298
213
|
|
|
299
|
-
|
|
214
|
+
while len(self._stack) != 0:
|
|
215
|
+
if TokenType.LPAREN in self._stack:
|
|
216
|
+
raise self._error("Mismatched parentheses")
|
|
217
|
+
self._evaluate_expression(cs)
|
|
300
218
|
|
|
301
|
-
if len(self.
|
|
302
|
-
raise
|
|
219
|
+
if len(self._queue) != 1:
|
|
220
|
+
raise self._error("Invalid expression: too many operands")
|
|
303
221
|
|
|
304
|
-
return self.
|
|
222
|
+
return self._queue[0]
|