autosar-calltree 0.3.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.
- autosar_calltree/__init__.py +24 -0
- autosar_calltree/analyzers/__init__.py +5 -0
- autosar_calltree/analyzers/call_tree_builder.py +369 -0
- autosar_calltree/cli/__init__.py +5 -0
- autosar_calltree/cli/main.py +330 -0
- autosar_calltree/config/__init__.py +10 -0
- autosar_calltree/config/module_config.py +179 -0
- autosar_calltree/database/__init__.py +23 -0
- autosar_calltree/database/function_database.py +505 -0
- autosar_calltree/database/models.py +189 -0
- autosar_calltree/generators/__init__.py +5 -0
- autosar_calltree/generators/mermaid_generator.py +488 -0
- autosar_calltree/parsers/__init__.py +6 -0
- autosar_calltree/parsers/autosar_parser.py +314 -0
- autosar_calltree/parsers/c_parser.py +415 -0
- autosar_calltree/version.py +5 -0
- autosar_calltree-0.3.0.dist-info/METADATA +482 -0
- autosar_calltree-0.3.0.dist-info/RECORD +22 -0
- autosar_calltree-0.3.0.dist-info/WHEEL +5 -0
- autosar_calltree-0.3.0.dist-info/entry_points.txt +2 -0
- autosar_calltree-0.3.0.dist-info/licenses/LICENSE +21 -0
- autosar_calltree-0.3.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AUTOSAR-specific function parser.
|
|
3
|
+
|
|
4
|
+
Parses AUTOSAR macro-based function declarations like:
|
|
5
|
+
- FUNC(rettype, memclass) function_name(...)
|
|
6
|
+
- FUNC_P2VAR(rettype, ptrclass, memclass) function_name(...)
|
|
7
|
+
- STATIC FUNC(...) ...
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import re
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import List, Optional
|
|
13
|
+
|
|
14
|
+
from ..database.models import FunctionInfo, FunctionType, Parameter
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class AutosarParser:
|
|
18
|
+
"""Parse AUTOSAR-specific function declarations."""
|
|
19
|
+
|
|
20
|
+
# AUTOSAR function patterns
|
|
21
|
+
FUNC_PATTERN = re.compile(
|
|
22
|
+
r"(STATIC\s+)?FUNC\(\s*([^,]+?)\s*,\s*([^)]+?)\s*\)\s+(\w+)\s*\(", re.MULTILINE
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
FUNC_P2VAR_PATTERN = re.compile(
|
|
26
|
+
r"(STATIC\s+)?FUNC_P2VAR\(\s*([^,]+?)\s*,\s*([^,]+?)\s*,\s*([^)]+?)\s*\)\s+(\w+)\s*\(",
|
|
27
|
+
re.MULTILINE,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
FUNC_P2CONST_PATTERN = re.compile(
|
|
31
|
+
r"(STATIC\s+)?FUNC_P2CONST\(\s*([^,]+?)\s*,\s*([^,]+?)\s*,\s*([^)]+?)\s*\)\s+(\w+)\s*\(",
|
|
32
|
+
re.MULTILINE,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Parameter patterns
|
|
36
|
+
# Note: Order matters! More specific patterns (P2VAR, P2CONST) must be checked
|
|
37
|
+
# before less specific ones (VAR, CONST) to prevent false matches
|
|
38
|
+
P2VAR_PATTERN = re.compile(
|
|
39
|
+
r"P2VAR\(\s*([^,]+?)\s*,\s*([^,]+?)\s*,\s*([^)]+?)\s*\)\s+(\w+)"
|
|
40
|
+
)
|
|
41
|
+
P2CONST_PATTERN = re.compile(
|
|
42
|
+
r"P2CONST\(\s*([^,]+?)\s*,\s*([^,]+?)\s*,\s*([^)]+?)\s*\)\s+(\w+)"
|
|
43
|
+
)
|
|
44
|
+
# Use negative lookbehind to prevent matching P2VAR or P2CONST as VAR/CONST
|
|
45
|
+
VAR_PATTERN = re.compile(r"(?<!P2)VAR\(\s*([^,]+?)\s*,\s*([^)]+?)\s*\)\s+(\w+)")
|
|
46
|
+
CONST_PATTERN = re.compile(r"(?<!P2)CONST\(\s*([^,]+?)\s*,\s*([^)]+?)\s*\)\s+(\w+)")
|
|
47
|
+
|
|
48
|
+
def parse_function_declaration(
|
|
49
|
+
self, line: str, file_path: Path, line_number: int
|
|
50
|
+
) -> Optional[FunctionInfo]:
|
|
51
|
+
"""
|
|
52
|
+
Parse AUTOSAR function declaration.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
line: Line of code to parse
|
|
56
|
+
file_path: Path to source file
|
|
57
|
+
line_number: Line number in file
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
FunctionInfo object or None if not a function declaration
|
|
61
|
+
"""
|
|
62
|
+
# Try FUNC pattern
|
|
63
|
+
match = self.FUNC_PATTERN.search(line)
|
|
64
|
+
if match:
|
|
65
|
+
is_static_str, return_type, memory_class, func_name = match.groups()
|
|
66
|
+
# Extract parameters from the line
|
|
67
|
+
param_start = line.find(func_name) + len(func_name)
|
|
68
|
+
param_string = self._extract_param_string(line, param_start)
|
|
69
|
+
parameters = self.parse_parameters(param_string)
|
|
70
|
+
|
|
71
|
+
return FunctionInfo(
|
|
72
|
+
name=func_name,
|
|
73
|
+
return_type=return_type.strip(),
|
|
74
|
+
file_path=file_path,
|
|
75
|
+
line_number=line_number,
|
|
76
|
+
is_static=bool(is_static_str),
|
|
77
|
+
function_type=FunctionType.AUTOSAR_FUNC,
|
|
78
|
+
memory_class=memory_class.strip(),
|
|
79
|
+
macro_type="FUNC",
|
|
80
|
+
parameters=parameters,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Try FUNC_P2VAR pattern
|
|
84
|
+
match = self.FUNC_P2VAR_PATTERN.search(line)
|
|
85
|
+
if match:
|
|
86
|
+
is_static_str, return_type, ptr_class, memory_class, func_name = (
|
|
87
|
+
match.groups()
|
|
88
|
+
)
|
|
89
|
+
# Extract parameters from the line
|
|
90
|
+
param_start = line.find(func_name) + len(func_name)
|
|
91
|
+
param_string = self._extract_param_string(line, param_start)
|
|
92
|
+
parameters = self.parse_parameters(param_string)
|
|
93
|
+
|
|
94
|
+
return FunctionInfo(
|
|
95
|
+
name=func_name,
|
|
96
|
+
return_type=f"{return_type.strip()}*",
|
|
97
|
+
file_path=file_path,
|
|
98
|
+
line_number=line_number,
|
|
99
|
+
is_static=bool(is_static_str),
|
|
100
|
+
function_type=FunctionType.AUTOSAR_FUNC_P2VAR,
|
|
101
|
+
memory_class=memory_class.strip(),
|
|
102
|
+
macro_type="FUNC_P2VAR",
|
|
103
|
+
parameters=parameters,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# Try FUNC_P2CONST pattern
|
|
107
|
+
match = self.FUNC_P2CONST_PATTERN.search(line)
|
|
108
|
+
if match:
|
|
109
|
+
is_static_str, return_type, ptr_class, memory_class, func_name = (
|
|
110
|
+
match.groups()
|
|
111
|
+
)
|
|
112
|
+
# Extract parameters from the line
|
|
113
|
+
param_start = line.find(func_name) + len(func_name)
|
|
114
|
+
param_string = self._extract_param_string(line, param_start)
|
|
115
|
+
parameters = self.parse_parameters(param_string)
|
|
116
|
+
|
|
117
|
+
return FunctionInfo(
|
|
118
|
+
name=func_name,
|
|
119
|
+
return_type=f"const {return_type.strip()}*",
|
|
120
|
+
file_path=file_path,
|
|
121
|
+
line_number=line_number,
|
|
122
|
+
is_static=bool(is_static_str),
|
|
123
|
+
function_type=FunctionType.AUTOSAR_FUNC_P2CONST,
|
|
124
|
+
memory_class=memory_class.strip(),
|
|
125
|
+
macro_type="FUNC_P2CONST",
|
|
126
|
+
parameters=parameters,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
return None
|
|
130
|
+
|
|
131
|
+
def _extract_param_string(self, line: str, start_pos: int) -> str:
|
|
132
|
+
"""
|
|
133
|
+
Extract parameter string from function declaration line.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
line: Function declaration line
|
|
137
|
+
start_pos: Position to start searching for parameters
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
Parameter string (content inside parentheses)
|
|
141
|
+
"""
|
|
142
|
+
# Find opening parenthesis
|
|
143
|
+
paren_start = line.find("(", start_pos)
|
|
144
|
+
if paren_start == -1:
|
|
145
|
+
return ""
|
|
146
|
+
|
|
147
|
+
# Find matching closing parenthesis
|
|
148
|
+
paren_depth = 0
|
|
149
|
+
param_chars = []
|
|
150
|
+
|
|
151
|
+
for char in line[paren_start:]:
|
|
152
|
+
param_chars.append(char)
|
|
153
|
+
if char == "(":
|
|
154
|
+
paren_depth += 1
|
|
155
|
+
elif char == ")":
|
|
156
|
+
paren_depth -= 1
|
|
157
|
+
if paren_depth == 0:
|
|
158
|
+
break
|
|
159
|
+
|
|
160
|
+
# Remove outer parentheses
|
|
161
|
+
param_string = "".join(param_chars)
|
|
162
|
+
if param_string.startswith("(") and param_string.endswith(")"):
|
|
163
|
+
param_string = param_string[1:-1]
|
|
164
|
+
|
|
165
|
+
return param_string.strip()
|
|
166
|
+
|
|
167
|
+
def parse_parameters(self, param_string: str) -> List[Parameter]:
|
|
168
|
+
"""
|
|
169
|
+
Parse function parameters (both AUTOSAR and traditional).
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
param_string: Parameter list string (inside parentheses)
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
List of Parameter objects
|
|
176
|
+
"""
|
|
177
|
+
if not param_string or param_string.strip() in ("void", ""):
|
|
178
|
+
return []
|
|
179
|
+
|
|
180
|
+
parameters = []
|
|
181
|
+
|
|
182
|
+
# Split by comma, but respect nested parentheses
|
|
183
|
+
params = self._split_parameters(param_string)
|
|
184
|
+
|
|
185
|
+
for param in params:
|
|
186
|
+
param = param.strip()
|
|
187
|
+
if not param or param == "void":
|
|
188
|
+
continue
|
|
189
|
+
|
|
190
|
+
parsed_param = self._parse_single_parameter(param)
|
|
191
|
+
if parsed_param:
|
|
192
|
+
parameters.append(parsed_param)
|
|
193
|
+
|
|
194
|
+
return parameters
|
|
195
|
+
|
|
196
|
+
def _parse_single_parameter(self, param_str: str) -> Optional[Parameter]:
|
|
197
|
+
"""Parse a single parameter."""
|
|
198
|
+
# Try P2VAR pattern first (more specific)
|
|
199
|
+
match = self.P2VAR_PATTERN.search(param_str)
|
|
200
|
+
if match:
|
|
201
|
+
param_type, ptr_class, memory_class, name = match.groups()
|
|
202
|
+
return Parameter(
|
|
203
|
+
name=name,
|
|
204
|
+
param_type=param_type.strip(),
|
|
205
|
+
is_pointer=True,
|
|
206
|
+
is_const=False,
|
|
207
|
+
memory_class=memory_class.strip(),
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
# Try P2CONST pattern (more specific)
|
|
211
|
+
match = self.P2CONST_PATTERN.search(param_str)
|
|
212
|
+
if match:
|
|
213
|
+
param_type, ptr_class, memory_class, name = match.groups()
|
|
214
|
+
return Parameter(
|
|
215
|
+
name=name,
|
|
216
|
+
param_type=param_type.strip(),
|
|
217
|
+
is_pointer=True,
|
|
218
|
+
is_const=True,
|
|
219
|
+
memory_class=memory_class.strip(),
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
# Try VAR pattern (less specific)
|
|
223
|
+
match = self.VAR_PATTERN.search(param_str)
|
|
224
|
+
if match:
|
|
225
|
+
param_type, memory_class, name = match.groups()
|
|
226
|
+
return Parameter(
|
|
227
|
+
name=name,
|
|
228
|
+
param_type=param_type.strip(),
|
|
229
|
+
is_pointer=False,
|
|
230
|
+
is_const=False,
|
|
231
|
+
memory_class=memory_class.strip(),
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
# Try CONST pattern (less specific)
|
|
235
|
+
match = self.CONST_PATTERN.search(param_str)
|
|
236
|
+
if match:
|
|
237
|
+
param_type, memory_class, name = match.groups()
|
|
238
|
+
return Parameter(
|
|
239
|
+
name=name,
|
|
240
|
+
param_type=param_type.strip(),
|
|
241
|
+
is_pointer=False,
|
|
242
|
+
is_const=True,
|
|
243
|
+
memory_class=memory_class.strip(),
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
# Fallback: Traditional C parameter
|
|
247
|
+
return self._parse_traditional_parameter(param_str)
|
|
248
|
+
|
|
249
|
+
def _parse_traditional_parameter(self, param_str: str) -> Optional[Parameter]:
|
|
250
|
+
"""Parse traditional C parameter format."""
|
|
251
|
+
param_str = param_str.strip()
|
|
252
|
+
|
|
253
|
+
# Check for const
|
|
254
|
+
is_const = "const" in param_str
|
|
255
|
+
if is_const:
|
|
256
|
+
param_str = param_str.replace("const", "").strip()
|
|
257
|
+
|
|
258
|
+
# Check for pointer
|
|
259
|
+
is_pointer = "*" in param_str
|
|
260
|
+
if is_pointer:
|
|
261
|
+
param_str = param_str.replace("*", "").strip()
|
|
262
|
+
|
|
263
|
+
# Split type and name
|
|
264
|
+
parts = param_str.rsplit(None, 1)
|
|
265
|
+
if len(parts) == 2:
|
|
266
|
+
param_type, name = parts
|
|
267
|
+
return Parameter(
|
|
268
|
+
name=name,
|
|
269
|
+
param_type=param_type.strip(),
|
|
270
|
+
is_pointer=is_pointer,
|
|
271
|
+
is_const=is_const,
|
|
272
|
+
)
|
|
273
|
+
elif len(parts) == 1:
|
|
274
|
+
# Only type, no name (parameter name omitted)
|
|
275
|
+
return Parameter(
|
|
276
|
+
name="",
|
|
277
|
+
param_type=parts[0].strip(),
|
|
278
|
+
is_pointer=is_pointer,
|
|
279
|
+
is_const=is_const,
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
return None
|
|
283
|
+
|
|
284
|
+
def _split_parameters(self, param_string: str) -> List[str]:
|
|
285
|
+
"""Split parameters by comma, respecting nested parentheses."""
|
|
286
|
+
parameters = []
|
|
287
|
+
current_param = []
|
|
288
|
+
paren_depth = 0
|
|
289
|
+
|
|
290
|
+
for char in param_string:
|
|
291
|
+
if char == "(":
|
|
292
|
+
paren_depth += 1
|
|
293
|
+
current_param.append(char)
|
|
294
|
+
elif char == ")":
|
|
295
|
+
paren_depth -= 1
|
|
296
|
+
current_param.append(char)
|
|
297
|
+
elif char == "," and paren_depth == 0:
|
|
298
|
+
parameters.append("".join(current_param))
|
|
299
|
+
current_param = []
|
|
300
|
+
else:
|
|
301
|
+
current_param.append(char)
|
|
302
|
+
|
|
303
|
+
if current_param:
|
|
304
|
+
parameters.append("".join(current_param))
|
|
305
|
+
|
|
306
|
+
return parameters
|
|
307
|
+
|
|
308
|
+
def is_autosar_function(self, line: str) -> bool:
|
|
309
|
+
"""Check if line contains AUTOSAR function declaration."""
|
|
310
|
+
return bool(
|
|
311
|
+
self.FUNC_PATTERN.search(line)
|
|
312
|
+
or self.FUNC_P2VAR_PATTERN.search(line)
|
|
313
|
+
or self.FUNC_P2CONST_PATTERN.search(line)
|
|
314
|
+
)
|