lambda-graphs 0.1.4__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.
Files changed (45) hide show
  1. lambda_graphs/__init__.py +18 -0
  2. lambda_graphs/__main__.py +7 -0
  3. lambda_graphs/cli.py +183 -0
  4. lambda_graphs/codeviews/AST/AST.py +120 -0
  5. lambda_graphs/codeviews/AST/AST_driver.py +37 -0
  6. lambda_graphs/codeviews/AST/__init__.py +0 -0
  7. lambda_graphs/codeviews/CFG/CFG.py +42 -0
  8. lambda_graphs/codeviews/CFG/CFG_c.py +1182 -0
  9. lambda_graphs/codeviews/CFG/CFG_cpp.py +5604 -0
  10. lambda_graphs/codeviews/CFG/CFG_driver.py +45 -0
  11. lambda_graphs/codeviews/CFG/CFG_java.py +1722 -0
  12. lambda_graphs/codeviews/CFG/__init__.py +0 -0
  13. lambda_graphs/codeviews/CST/CST_driver.py +70 -0
  14. lambda_graphs/codeviews/CST/__init__.py +0 -0
  15. lambda_graphs/codeviews/DFG/DFG_driver.py +42 -0
  16. lambda_graphs/codeviews/DFG/__init__.py +0 -0
  17. lambda_graphs/codeviews/SDFG/SDFG.py +135 -0
  18. lambda_graphs/codeviews/SDFG/SDFG_c.py +2041 -0
  19. lambda_graphs/codeviews/SDFG/SDFG_cpp.py +4030 -0
  20. lambda_graphs/codeviews/SDFG/SDFG_java.py +1618 -0
  21. lambda_graphs/codeviews/SDFG/__init__.py +0 -0
  22. lambda_graphs/codeviews/__init__.py +0 -0
  23. lambda_graphs/codeviews/combined_graph/__init__.py +0 -0
  24. lambda_graphs/codeviews/combined_graph/combined_driver.py +163 -0
  25. lambda_graphs/tree_parser/__init__.py +0 -0
  26. lambda_graphs/tree_parser/c_parser.py +565 -0
  27. lambda_graphs/tree_parser/cpp_parser.py +424 -0
  28. lambda_graphs/tree_parser/custom_parser.py +66 -0
  29. lambda_graphs/tree_parser/java_parser.py +254 -0
  30. lambda_graphs/tree_parser/parser_driver.py +54 -0
  31. lambda_graphs/utils/DFG_utils.py +44 -0
  32. lambda_graphs/utils/__init__.py +0 -0
  33. lambda_graphs/utils/c_nodes.py +431 -0
  34. lambda_graphs/utils/cpp_nodes.py +1331 -0
  35. lambda_graphs/utils/java_nodes.py +756 -0
  36. lambda_graphs/utils/multi_file_merger.py +444 -0
  37. lambda_graphs/utils/postprocessor.py +83 -0
  38. lambda_graphs/utils/preprocessor.py +68 -0
  39. lambda_graphs/utils/src_parser.py +23 -0
  40. lambda_graphs-0.1.4.dist-info/METADATA +380 -0
  41. lambda_graphs-0.1.4.dist-info/RECORD +45 -0
  42. lambda_graphs-0.1.4.dist-info/WHEEL +5 -0
  43. lambda_graphs-0.1.4.dist-info/entry_points.txt +2 -0
  44. lambda_graphs-0.1.4.dist-info/licenses/LICENSE +21 -0
  45. lambda_graphs-0.1.4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,424 @@
1
+ from ..tree_parser.custom_parser import CustomParser
2
+
3
+
4
+ class CppParser(CustomParser):
5
+ def __init__(self, src_language, src_code):
6
+ super().__init__(src_language, src_code)
7
+
8
+ def check_declaration(self, current_node):
9
+ """
10
+ Check if the current node is a variable declaration in C++.
11
+ C++ declarations can be:
12
+ - init_declarator (e.g., int x = 5;)
13
+ - Direct child of declaration (e.g., int x;)
14
+ - parameter_declaration (function parameters)
15
+ - pointer_declarator (pointer variables)
16
+ - reference_declarator (reference variables)
17
+ - auto declarations
18
+ - structured bindings
19
+ """
20
+ parent_types = [
21
+ "init_declarator",
22
+ "parameter_declaration",
23
+ "optional_parameter_declaration",
24
+ "pointer_declarator",
25
+ "reference_declarator",
26
+ "array_declarator",
27
+ ]
28
+ current_types = ["identifier"]
29
+
30
+ if (
31
+ current_node.parent is not None
32
+ and current_node.parent.type in parent_types
33
+ and current_node.type in current_types
34
+ ):
35
+ if current_node.parent.type == "init_declarator":
36
+ declarator = (
37
+ current_node.parent.children[0]
38
+ if current_node.parent.children
39
+ else None
40
+ )
41
+ if declarator:
42
+ if declarator == current_node:
43
+ return True
44
+ if declarator.type in [
45
+ "pointer_declarator",
46
+ "reference_declarator",
47
+ "array_declarator",
48
+ ]:
49
+ if (
50
+ self.find_identifier_in_declarator(declarator)
51
+ == current_node
52
+ ):
53
+ return True
54
+ elif current_node.parent.type in [
55
+ "pointer_declarator",
56
+ "reference_declarator",
57
+ ]:
58
+ if current_node.type == "identifier":
59
+ return True
60
+ elif current_node.parent.type == "array_declarator":
61
+ if (
62
+ current_node.parent.children
63
+ and current_node.parent.children[0] == current_node
64
+ ):
65
+ return True
66
+ elif current_node.parent.type in [
67
+ "parameter_declaration",
68
+ "optional_parameter_declaration",
69
+ ]:
70
+ return True
71
+
72
+ if (
73
+ current_node.parent is not None
74
+ and current_node.parent.type == "declaration"
75
+ and current_node.type == "identifier"
76
+ ):
77
+ for i, child in enumerate(current_node.parent.children):
78
+ if child == current_node and i > 0:
79
+ prev_sibling = current_node.parent.children[i - 1]
80
+ if prev_sibling.type in [
81
+ "primitive_type",
82
+ "type_identifier",
83
+ "sized_type_specifier",
84
+ "struct_specifier",
85
+ "class_specifier",
86
+ "union_specifier",
87
+ "enum_specifier",
88
+ "storage_class_specifier",
89
+ "type_qualifier",
90
+ "auto",
91
+ "template_type",
92
+ "qualified_identifier",
93
+ ]:
94
+ return True
95
+ return False
96
+
97
+ if current_node.parent is not None and current_node.parent.type == "declarator":
98
+ if (
99
+ current_node.parent.parent is not None
100
+ and current_node.parent.parent.type
101
+ in ["declaration", "parameter_declaration"]
102
+ ):
103
+ return True
104
+
105
+ if (
106
+ current_node.parent is not None
107
+ and current_node.parent.type == "function_declarator"
108
+ ):
109
+ declarator = current_node.parent.child_by_field_name("declarator")
110
+ if declarator == current_node:
111
+ return True
112
+
113
+ if (
114
+ current_node.parent is not None
115
+ and current_node.parent.type == "field_declaration"
116
+ ):
117
+ for i, child in enumerate(current_node.parent.children):
118
+ if child == current_node and i > 0:
119
+ prev_sibling = current_node.parent.children[i - 1]
120
+ if prev_sibling.type in [
121
+ "primitive_type",
122
+ "type_identifier",
123
+ "sized_type_specifier",
124
+ "template_type",
125
+ "qualified_identifier",
126
+ "auto",
127
+ ]:
128
+ return True
129
+
130
+ return False
131
+
132
+ def find_identifier_in_declarator(self, node):
133
+ """Recursively find the identifier within a declarator node."""
134
+ if node.type == "identifier":
135
+ return node
136
+ for child in node.children:
137
+ result = self.find_identifier_in_declarator(child)
138
+ if result is not None:
139
+ return result
140
+ return None
141
+
142
+ def get_type(self, node):
143
+ """
144
+ Given a declarator node, return the variable type of the identifier.
145
+ C++ type specifiers include: primitive_type, type_identifier, template_type, etc.
146
+
147
+ This function traverses up the tree to find the declaration or parameter_declaration node,
148
+ then searches for the type specifier among its children.
149
+ """
150
+ datatypes = [
151
+ "primitive_type",
152
+ "type_identifier",
153
+ "sized_type_specifier",
154
+ "struct_specifier",
155
+ "class_specifier",
156
+ "union_specifier",
157
+ "enum_specifier",
158
+ "template_type",
159
+ "qualified_identifier",
160
+ "auto",
161
+ "decltype",
162
+ ]
163
+
164
+ current = node
165
+
166
+ while current is not None:
167
+ if current.type in [
168
+ "declaration",
169
+ "parameter_declaration",
170
+ "optional_parameter_declaration",
171
+ "field_declaration",
172
+ ]:
173
+ for child in current.children:
174
+ if child.type in datatypes:
175
+ return child.text.decode("utf-8")
176
+ return None
177
+
178
+ current = current.parent
179
+
180
+ return None
181
+
182
+ def scope_check(self, parent_scope, child_scope):
183
+ """Check if parent_scope is a subset of child_scope."""
184
+ for p in parent_scope:
185
+ if p not in child_scope:
186
+ return False
187
+ return True
188
+
189
+ def longest_scope_match(self, name_matches, symbol_table):
190
+ """Given a list of name matches, return the longest scope match."""
191
+ scope_array = list(map(lambda x: symbol_table["scope_map"][x[0]], name_matches))
192
+ max_val = max(scope_array, key=lambda x: len(x))
193
+ for i in range(len(scope_array)):
194
+ if scope_array[i] == max_val:
195
+ return name_matches[i][0]
196
+
197
+ def create_all_tokens(
198
+ self,
199
+ src_code,
200
+ root_node,
201
+ all_tokens,
202
+ label,
203
+ method_map,
204
+ method_calls,
205
+ start_line,
206
+ declaration,
207
+ declaration_map,
208
+ symbol_table,
209
+ ):
210
+ """
211
+ Create tokens for C++ language.
212
+ Handles C++-specific constructs like classes, namespaces, templates, references, etc.
213
+ """
214
+ remove_list = [
215
+ "function_definition",
216
+ "call_expression",
217
+ "class_specifier",
218
+ "struct_specifier",
219
+ ]
220
+
221
+ block_types = [
222
+ "compound_statement",
223
+ "if_statement",
224
+ "while_statement",
225
+ "for_statement",
226
+ "for_range_loop",
227
+ "do_statement",
228
+ "switch_statement",
229
+ "case_statement",
230
+ "function_definition",
231
+ "class_specifier",
232
+ "struct_specifier",
233
+ "namespace_definition",
234
+ "try_statement",
235
+ "catch_clause",
236
+ "lambda_expression",
237
+ ]
238
+
239
+ if root_node.is_named and root_node.type in block_types:
240
+ symbol_table["scope_id"] = symbol_table["scope_id"] + 1
241
+ symbol_table["scope_stack"].append(symbol_table["scope_id"])
242
+
243
+ if (
244
+ root_node.is_named
245
+ and (
246
+ len(root_node.children) == 0
247
+ or root_node.type in ["string_literal", "raw_string_literal"]
248
+ )
249
+ and root_node.type != "comment"
250
+ ):
251
+ index = self.index[
252
+ (root_node.start_point, root_node.end_point, root_node.type)
253
+ ]
254
+
255
+ label[index] = root_node.text.decode("UTF-8")
256
+
257
+ start_line[index] = root_node.start_point[0]
258
+
259
+ all_tokens.append(index)
260
+
261
+ symbol_table["scope_map"][index] = symbol_table["scope_stack"].copy()
262
+
263
+ current_node = root_node
264
+
265
+ if (
266
+ current_node.parent is not None
267
+ and current_node.parent.type in remove_list
268
+ ):
269
+ method_map.append(index)
270
+ if (
271
+ current_node.next_named_sibling is not None
272
+ and current_node.next_named_sibling.type == "argument_list"
273
+ ):
274
+ method_calls.append(index)
275
+
276
+ if (
277
+ current_node.parent is not None
278
+ and current_node.parent.type == "call_expression"
279
+ ):
280
+ function_node = current_node.parent.child_by_field_name("function")
281
+ if function_node == current_node or (
282
+ function_node
283
+ and self.find_identifier_in_declarator(function_node)
284
+ == current_node
285
+ ):
286
+ method_map.append(index)
287
+ method_calls.append(index)
288
+
289
+ if (
290
+ current_node.parent is not None
291
+ and current_node.parent.type == "field_expression"
292
+ ):
293
+ field_node = current_node.parent.child_by_field_name("field")
294
+ if field_node is not None:
295
+ field_index = self.index[
296
+ (field_node.start_point, field_node.end_point, field_node.type)
297
+ ]
298
+ current_index = self.index[
299
+ (
300
+ current_node.start_point,
301
+ current_node.end_point,
302
+ current_node.type,
303
+ )
304
+ ]
305
+ if field_index == current_index:
306
+ method_map.append(current_index)
307
+
308
+ while (
309
+ current_node.parent is not None
310
+ and current_node.parent.type == "field_expression"
311
+ ):
312
+ current_node = current_node.parent
313
+
314
+ if (
315
+ current_node.parent is not None
316
+ and current_node.parent.type == "call_expression"
317
+ ):
318
+ method_map.append(index)
319
+ method_calls.append(index)
320
+ label[index] = current_node.text.decode("UTF-8")
321
+
322
+ if (
323
+ current_node.parent is not None
324
+ and current_node.parent.type == "qualified_identifier"
325
+ ):
326
+ if (
327
+ current_node.parent.children
328
+ and current_node.parent.children[-1] == current_node
329
+ ):
330
+ label[index] = current_node.parent.text.decode("UTF-8")
331
+
332
+ if (
333
+ current_node.parent is not None
334
+ and current_node.parent.type == "template_function"
335
+ ):
336
+ method_map.append(index)
337
+ if (
338
+ current_node.next_named_sibling is not None
339
+ and current_node.next_named_sibling.type == "template_argument_list"
340
+ ):
341
+ method_calls.append(index)
342
+
343
+ if self.check_declaration(current_node):
344
+ variable_name = label[index]
345
+ declaration[index] = variable_name
346
+
347
+ variable_type = self.get_type(current_node.parent)
348
+ if variable_type is not None:
349
+ symbol_table["data_type"][index] = variable_type
350
+ else:
351
+ current_scope = symbol_table["scope_map"][index]
352
+
353
+ if (
354
+ current_node.parent is not None
355
+ and current_node.parent.type == "field_expression"
356
+ ):
357
+ field_variable = current_node.parent.children[-1]
358
+ field_variable_name = field_variable.text.decode("utf-8")
359
+
360
+ for ind, var in declaration.items():
361
+ if var == field_variable_name:
362
+ parent_scope = symbol_table["scope_map"][ind]
363
+ if self.scope_check(parent_scope, current_scope):
364
+ declaration_map[index] = ind
365
+ break
366
+ elif (
367
+ current_node.parent is not None
368
+ and current_node.parent.type == "qualified_identifier"
369
+ ):
370
+ qualified_name = current_node.parent.text.decode("utf-8")
371
+ name_matches = []
372
+ for ind, var in declaration.items():
373
+ if var == qualified_name or var == label[index]:
374
+ parent_scope = symbol_table["scope_map"][ind]
375
+ if self.scope_check(parent_scope, current_scope):
376
+ name_matches.append((ind, var))
377
+
378
+ if name_matches:
379
+ closest_index = self.longest_scope_match(
380
+ name_matches, symbol_table
381
+ )
382
+ declaration_map[index] = closest_index
383
+ else:
384
+ name_matches = []
385
+ for ind, var in declaration.items():
386
+ if var == label[index]:
387
+ parent_scope = symbol_table["scope_map"][ind]
388
+ if self.scope_check(parent_scope, current_scope):
389
+ name_matches.append((ind, var))
390
+
391
+ if name_matches:
392
+ closest_index = self.longest_scope_match(
393
+ name_matches, symbol_table
394
+ )
395
+ declaration_map[index] = closest_index
396
+
397
+ else:
398
+ for child in root_node.children:
399
+ self.create_all_tokens(
400
+ src_code,
401
+ child,
402
+ all_tokens,
403
+ label,
404
+ method_map,
405
+ method_calls,
406
+ start_line,
407
+ declaration,
408
+ declaration_map,
409
+ symbol_table,
410
+ )
411
+
412
+ if root_node.is_named and root_node.type in block_types:
413
+ symbol_table["scope_stack"].pop(-1)
414
+
415
+ return (
416
+ all_tokens,
417
+ label,
418
+ method_map,
419
+ method_calls,
420
+ start_line,
421
+ declaration,
422
+ declaration_map,
423
+ symbol_table,
424
+ )
@@ -0,0 +1,66 @@
1
+ import subprocess
2
+
3
+ from tree_sitter import Parser
4
+
5
+ from lambda_graphs import get_language_map
6
+
7
+
8
+ def get_commit_hash(directory):
9
+ try:
10
+ result = subprocess.run(
11
+ ["git", "rev-parse", "HEAD"], cwd=directory, capture_output=True, text=True
12
+ )
13
+ if result.returncode == 0:
14
+ commit_hash = result.stdout.strip()
15
+ return commit_hash
16
+ return None
17
+ except FileNotFoundError:
18
+ return None
19
+
20
+
21
+ class CustomParser:
22
+ """Custom parser for the src_language"""
23
+
24
+ def __init__(self, src_language, src_code):
25
+ """Initialize the parser with the language.
26
+ Language options are: c, cpp"""
27
+
28
+ self.src_language = src_language
29
+ self.src_code = src_code
30
+ self.index = {}
31
+ self.language_map = get_language_map()
32
+ self.root_node, self.tree = self.parse()
33
+ self.all_tokens = []
34
+ self.label = {}
35
+ self.method_map = []
36
+ self.method_calls = []
37
+ self.start_line = {}
38
+ self.declaration = {}
39
+ self.declaration_map = {}
40
+ self.symbol_table = {
41
+ "scope_stack": [0],
42
+ "scope_map": {},
43
+ "scope_id": 0,
44
+ "data_type": {},
45
+ }
46
+
47
+ def create_AST_id(self, root_node, AST_index, AST_id):
48
+ """Create an id for each node in the AST. This AST id is maintained and used across all code views so that
49
+ it is possible to easily combine graphs"""
50
+ if root_node.is_named:
51
+ current_node_id = AST_id[0]
52
+ AST_id[0] += 1
53
+ AST_index[(root_node.start_point, root_node.end_point, root_node.type)] = (
54
+ current_node_id
55
+ )
56
+ for child in root_node.children:
57
+ if child.is_named:
58
+ self.create_AST_id(child, AST_index, AST_id)
59
+ return
60
+
61
+ def parse(self):
62
+ parser = Parser(self.language_map[self.src_language])
63
+ tree = parser.parse(bytes(self.src_code, "utf8"))
64
+ self.root_node = tree.root_node
65
+ self.create_AST_id(self.root_node, self.index, [5])
66
+ return self.root_node, tree