dissect.cstruct 4.8.dev9__tar.gz → 5.0.dev0__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.
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/PKG-INFO +1 -1
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/dissect/cstruct/cstruct.py +4 -10
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/dissect/cstruct/exceptions.py +4 -4
- dissect_cstruct-5.0.dev0/dissect/cstruct/expression.py +222 -0
- dissect_cstruct-5.0.dev0/dissect/cstruct/lexer.py +574 -0
- dissect_cstruct-5.0.dev0/dissect/cstruct/parser.py +546 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/dissect/cstruct/utils.py +24 -10
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/dissect.cstruct.egg-info/PKG-INFO +1 -1
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/dissect.cstruct.egg-info/SOURCES.txt +2 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/dissect.cstruct.egg-info/scm_file_list.json +52 -50
- dissect_cstruct-5.0.dev0/dissect.cstruct.egg-info/scm_version.json +8 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/pyproject.toml +0 -1
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/_docs/conf.py +0 -1
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/test_basic.py +9 -19
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/test_benchmark.py +26 -0
- dissect_cstruct-5.0.dev0/tests/test_expression.py +271 -0
- dissect_cstruct-5.0.dev0/tests/test_lexer.py +202 -0
- dissect_cstruct-5.0.dev0/tests/test_parser.py +546 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/test_tools_stubgen.py +10 -10
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/test_types_pointer.py +1 -1
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/test_types_structure.py +13 -9
- dissect_cstruct-4.8.dev9/dissect/cstruct/expression.py +0 -304
- dissect_cstruct-4.8.dev9/dissect/cstruct/parser.py +0 -713
- dissect_cstruct-4.8.dev9/dissect.cstruct.egg-info/scm_version.json +0 -8
- dissect_cstruct-4.8.dev9/tests/test_expression.py +0 -129
- dissect_cstruct-4.8.dev9/tests/test_parser.py +0 -296
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/.git-blame-ignore-revs +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/.gitattributes +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/CHANGELOG.md +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/COPYRIGHT +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/LICENSE +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/MANIFEST.in +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/README.md +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/dissect/cstruct/__init__.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/dissect/cstruct/bitbuffer.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/dissect/cstruct/compiler.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/dissect/cstruct/tools/__init__.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/dissect/cstruct/tools/stubgen.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/dissect/cstruct/types/__init__.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/dissect/cstruct/types/base.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/dissect/cstruct/types/char.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/dissect/cstruct/types/enum.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/dissect/cstruct/types/flag.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/dissect/cstruct/types/int.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/dissect/cstruct/types/leb128.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/dissect/cstruct/types/packed.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/dissect/cstruct/types/pointer.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/dissect/cstruct/types/structure.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/dissect/cstruct/types/void.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/dissect/cstruct/types/wchar.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/dissect.cstruct.egg-info/dependency_links.txt +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/dissect.cstruct.egg-info/entry_points.txt +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/dissect.cstruct.egg-info/requires.txt +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/dissect.cstruct.egg-info/top_level.txt +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/examples/disk.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/examples/mirai.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/examples/pe.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/examples/protobuf.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/examples/secdesc.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/setup.cfg +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/__init__.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/_data/testdef.txt +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/_docs/Makefile +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/_docs/__init__.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/_docs/index.rst +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/conftest.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/test_align.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/test_annotations.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/test_bitbuffer.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/test_bitfield.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/test_compiler.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/test_ctypes.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/test_types_base.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/test_types_char.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/test_types_custom.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/test_types_enum.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/test_types_flag.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/test_types_int.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/test_types_leb128.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/test_types_packed.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/test_types_union.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/test_types_void.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/test_types_wchar.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/test_utils.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tests/utils.py +0 -0
- {dissect_cstruct-4.8.dev9 → dissect_cstruct-5.0.dev0}/tox.ini +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dissect.cstruct
|
|
3
|
-
Version:
|
|
3
|
+
Version: 5.0.dev0
|
|
4
4
|
Summary: A Dissect module implementing a parser for C-like structures: structure parsing in Python made easy
|
|
5
5
|
Author-email: Dissect Team <dissect@fox-it.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -10,7 +10,7 @@ from typing import TYPE_CHECKING, Any, BinaryIO, TypeVar, cast
|
|
|
10
10
|
|
|
11
11
|
from dissect.cstruct.exceptions import ResolveError
|
|
12
12
|
from dissect.cstruct.expression import Expression
|
|
13
|
-
from dissect.cstruct.parser import CStyleParser
|
|
13
|
+
from dissect.cstruct.parser import CStyleParser
|
|
14
14
|
from dissect.cstruct.types import (
|
|
15
15
|
LEB128,
|
|
16
16
|
BaseArray,
|
|
@@ -56,7 +56,6 @@ class cstruct:
|
|
|
56
56
|
self.endian = endian
|
|
57
57
|
|
|
58
58
|
self.consts = {}
|
|
59
|
-
self.lookups = {}
|
|
60
59
|
self.includes = []
|
|
61
60
|
# fmt: off
|
|
62
61
|
self.typedefs = {
|
|
@@ -212,7 +211,6 @@ class cstruct:
|
|
|
212
211
|
cs = cstruct(endian=self.endian, pointer=self.pointer.__name__)
|
|
213
212
|
cs._anonymous_count = self._anonymous_count
|
|
214
213
|
cs.consts = self.consts.copy()
|
|
215
|
-
cs.lookups = self.lookups.copy()
|
|
216
214
|
cs.includes = self.includes.copy()
|
|
217
215
|
|
|
218
216
|
# Update typedefs to point to the new cstruct instance
|
|
@@ -274,11 +272,7 @@ class cstruct:
|
|
|
274
272
|
Definitions can be parsed using different parsers. Currently, there's
|
|
275
273
|
only one supported parser - DEF_CSTYLE. Parsers can add types and
|
|
276
274
|
modify this cstruct instance. Arguments can be passed to parsers
|
|
277
|
-
using kwargs
|
|
278
|
-
|
|
279
|
-
The CSTYLE parser was recently replaced with token based parser,
|
|
280
|
-
instead of a strictly regex based one. The old parser is still available
|
|
281
|
-
by using DEF_LEGACY.
|
|
275
|
+
using ``kwargs``.
|
|
282
276
|
|
|
283
277
|
Args:
|
|
284
278
|
definition: The definition to parse.
|
|
@@ -288,9 +282,9 @@ class cstruct:
|
|
|
288
282
|
deftype = deftype or cstruct.DEF_CSTYLE
|
|
289
283
|
|
|
290
284
|
if deftype == cstruct.DEF_CSTYLE:
|
|
291
|
-
TokenParser(self, **kwargs).parse(definition)
|
|
292
|
-
elif deftype == cstruct.DEF_LEGACY:
|
|
293
285
|
CStyleParser(self, **kwargs).parse(definition)
|
|
286
|
+
else:
|
|
287
|
+
raise ValueError(f"Unknown definition type: {deftype}")
|
|
294
288
|
|
|
295
289
|
return self
|
|
296
290
|
|
|
@@ -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
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
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
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from collections.abc import Callable
|
|
11
|
+
|
|
12
|
+
from dissect.cstruct import cstruct
|
|
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):
|
|
67
|
+
"""Expression parser for calculations in definitions."""
|
|
68
|
+
|
|
69
|
+
def __init__(self, expression: str):
|
|
70
|
+
self.expression = expression
|
|
71
|
+
|
|
72
|
+
tokens = Lexer(expression).tokenize()
|
|
73
|
+
super().__init__(tokens)
|
|
74
|
+
self._stack: list[TokenType] = []
|
|
75
|
+
self._queue: list[int | str] = []
|
|
76
|
+
|
|
77
|
+
def __repr__(self) -> str:
|
|
78
|
+
return self.expression
|
|
79
|
+
|
|
80
|
+
def _reset(self) -> None:
|
|
81
|
+
"""Reset the expression state for a new input."""
|
|
82
|
+
self._reset_cursor()
|
|
83
|
+
self._stack = []
|
|
84
|
+
self._queue = []
|
|
85
|
+
|
|
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}")
|
|
88
|
+
|
|
89
|
+
def _evaluate_expression(self, cs: cstruct) -> None:
|
|
90
|
+
operator = self._stack.pop(-1)
|
|
91
|
+
result = 0
|
|
92
|
+
|
|
93
|
+
if operator in UNARY_OPERATORS:
|
|
94
|
+
if len(self._queue) < 1:
|
|
95
|
+
raise ExpressionParserError("Invalid expression: not enough operands")
|
|
96
|
+
|
|
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")
|
|
109
|
+
|
|
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])
|
|
117
|
+
|
|
118
|
+
self._queue.append(result)
|
|
119
|
+
|
|
120
|
+
def evaluate(self, cs: cstruct, context: dict[str, int] | None = None) -> int:
|
|
121
|
+
"""Evaluates an expression using a Shunting-Yard implementation."""
|
|
122
|
+
self._reset()
|
|
123
|
+
context = context or {}
|
|
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:
|
|
130
|
+
while (
|
|
131
|
+
len(self._stack) != 0
|
|
132
|
+
and self._stack[-1] != TokenType.LPAREN
|
|
133
|
+
and precedence(self._stack[-1], token.type)
|
|
134
|
+
):
|
|
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()
|
|
210
|
+
|
|
211
|
+
else:
|
|
212
|
+
raise self._error(f"Unmatched token: '{token.value}'", token=token)
|
|
213
|
+
|
|
214
|
+
while len(self._stack) != 0:
|
|
215
|
+
if TokenType.LPAREN in self._stack:
|
|
216
|
+
raise self._error("Mismatched parentheses")
|
|
217
|
+
self._evaluate_expression(cs)
|
|
218
|
+
|
|
219
|
+
if len(self._queue) != 1:
|
|
220
|
+
raise self._error("Invalid expression: too many operands")
|
|
221
|
+
|
|
222
|
+
return self._queue[0]
|