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,18 @@
1
+ from tree_sitter import Language
2
+
3
+ import tree_sitter_c
4
+ import tree_sitter_cpp
5
+ import tree_sitter_java
6
+
7
+
8
+ def get_language_map():
9
+ """Return a mapping from language name to tree-sitter Language object.
10
+
11
+ Uses pre-compiled grammar libraries from pip packages instead of
12
+ cloning and building grammars at runtime.
13
+ """
14
+ return {
15
+ "c": Language(tree_sitter_c.language()),
16
+ "cpp": Language(tree_sitter_cpp.language()),
17
+ "java": Language(tree_sitter_java.language()),
18
+ }
@@ -0,0 +1,7 @@
1
+ """Support executing module as script"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .cli import app
6
+
7
+ app()
lambda_graphs/cli.py ADDED
@@ -0,0 +1,183 @@
1
+ """CLI for lambda-graphs"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ from pathlib import Path
7
+ from typing import Optional
8
+
9
+ import typer
10
+ from loguru import logger
11
+
12
+ from .codeviews.combined_graph.combined_driver import CombinedDriver
13
+ from .utils.multi_file_merger import merge_files
14
+ from . import get_language_map
15
+
16
+ get_language_map()
17
+ app = typer.Typer()
18
+
19
+
20
+ @app.callback(invoke_without_command=True)
21
+ def main(
22
+ lang: str = typer.Option(..., help="c, cpp, java"),
23
+ code: Optional[str] = typer.Option(
24
+ None,
25
+ help="""
26
+ int main() {
27
+ int x = 3;
28
+ x = x + 3;
29
+ int y = 4;
30
+ y += 1;
31
+ return 0;
32
+ }
33
+ """,
34
+ ),
35
+ code_file: Optional[Path] = typer.Option(None, help="./test_file.c"),
36
+ code_folder: Optional[Path] = typer.Option(
37
+ None, help="./project_folder/ - combines all source files"
38
+ ),
39
+ combined_name: Optional[str] = typer.Option(
40
+ None,
41
+ help="Custom name for combined file (without extension), e.g., 'myproject'",
42
+ ),
43
+ graphs: str = typer.Option("ast,dfg", help="ast, cfg, dfg"),
44
+ output: str = typer.Option("dot", help="all/json/dot (dot generates png as well)"),
45
+ blacklisted: str = typer.Option("", help="Nodes to be removed from the AST"),
46
+ collapsed: bool = typer.Option(
47
+ False, help="Collapses all occurrences of a variable into one node"
48
+ ),
49
+ last_def: bool = typer.Option(
50
+ False, help="Adds last definition information to the DFG"
51
+ ),
52
+ last_use: bool = typer.Option(False, help="Adds last use information to the DFG"),
53
+ throw_parse_error: bool = typer.Option(
54
+ False, help="Throws an error if the code cannot be parsed"
55
+ ),
56
+ debug: bool = typer.Option(False, help="Enables debug logs"),
57
+ ):
58
+ """
59
+ lambda-graphs
60
+
61
+ Generates, customizes and combines multiple source code representations (AST, CFG, DFG)
62
+
63
+
64
+ """
65
+ if debug:
66
+ level = "DEBUG"
67
+ else:
68
+ level = "WARNING"
69
+
70
+ config = {
71
+ "handlers": [{"sink": sys.stderr, "level": level}],
72
+ }
73
+ logger.configure(**config)
74
+
75
+ codeviews = {
76
+ "AST": {
77
+ "exists": False,
78
+ "collapsed": collapsed,
79
+ "minimized": bool(blacklisted),
80
+ "blacklisted": blacklisted.split(","),
81
+ },
82
+ "DFG": {
83
+ "exists": False,
84
+ "collapsed": collapsed,
85
+ "minimized": False,
86
+ "statements": True,
87
+ "last_def": last_def,
88
+ "last_use": last_use,
89
+ },
90
+ "CFG": {
91
+ "exists": False,
92
+ },
93
+ }
94
+
95
+ if "ast" in graphs.lower():
96
+ codeviews["AST"] = {
97
+ "exists": True,
98
+ "collapsed": collapsed,
99
+ "minimized": bool(blacklisted),
100
+ "blacklisted": blacklisted.split(","),
101
+ }
102
+ if "dfg" in graphs.lower():
103
+ codeviews["DFG"] = {
104
+ "exists": True,
105
+ "collapsed": collapsed,
106
+ "minimized": False,
107
+ "statements": True,
108
+ "last_def": last_def,
109
+ "last_use": last_use,
110
+ }
111
+ if "cfg" in graphs.lower():
112
+ codeviews["CFG"] = {
113
+ "exists": True,
114
+ }
115
+
116
+ try:
117
+ output_dir = Path("output")
118
+ output_dir.mkdir(exist_ok=True)
119
+
120
+ if code_folder:
121
+ logger.info(f"Combining source files from folder: {code_folder}")
122
+
123
+ temp_dir = Path("temp")
124
+ temp_dir.mkdir(exist_ok=True)
125
+
126
+ if combined_name:
127
+ output_base_name = combined_name
128
+ else:
129
+ output_base_name = code_folder.name
130
+
131
+ if lang == "c":
132
+ combined_source_file = str(temp_dir / f"{output_base_name}.c")
133
+ elif lang == "java":
134
+ combined_source_file = str(temp_dir / f"{output_base_name}.java")
135
+ else:
136
+ combined_source_file = str(temp_dir / f"{output_base_name}.cpp")
137
+
138
+ combined_file_path = merge_files(
139
+ str(code_folder), lang, combined_source_file
140
+ )
141
+ logger.info(f"Combined source written to: {combined_file_path}")
142
+
143
+ with open(combined_file_path, "r") as file_handle:
144
+ src_code = file_handle.read()
145
+
146
+ output_file_name = str(output_dir / f"{output_base_name}.json")
147
+ CombinedDriver(
148
+ src_language=lang,
149
+ src_code=src_code,
150
+ output_file=output_file_name,
151
+ graph_format=output,
152
+ codeviews=codeviews,
153
+ )
154
+ elif code_file:
155
+ file_handle = open(code_file, "r")
156
+ src_code = file_handle.read()
157
+ file_handle.close()
158
+ output_base_name = code_file.stem
159
+ output_file_name = str(output_dir / f"{output_base_name}.json")
160
+ CombinedDriver(
161
+ src_language=lang,
162
+ src_code=src_code,
163
+ output_file=output_file_name,
164
+ graph_format=output,
165
+ codeviews=codeviews,
166
+ )
167
+ else:
168
+ if not code:
169
+ raise Exception("No code provided")
170
+ output_file_name = str(output_dir / "output.json")
171
+ CombinedDriver(
172
+ src_language=lang,
173
+ src_code=code,
174
+ output_file=output_file_name,
175
+ graph_format=output,
176
+ codeviews=codeviews,
177
+ )
178
+ except Exception as e:
179
+ try:
180
+ logger.error(e.msg)
181
+ except AttributeError:
182
+ logger.error(e)
183
+ sys.exit(-1)
@@ -0,0 +1,120 @@
1
+ import networkx as nx
2
+
3
+
4
+ class ASTGraph:
5
+ def __init__(self, src_language, src_code, properties, root_node, parser):
6
+ self.src_language = src_language
7
+ self.src_code = src_code
8
+ self.properties = properties
9
+ self.root_node = root_node
10
+ self.parser = parser
11
+ self.index = self.parser.index
12
+ self.all_tokens = self.parser.all_tokens
13
+ self.label = self.parser.label
14
+ self.method_map = self.parser.method_map
15
+ self.method_calls = self.parser.method_calls
16
+ self.start_line = self.parser.start_line
17
+ self.graph = self.to_networkx()
18
+
19
+ def get_AST_nodes(self, root_node, AST, AST_index):
20
+
21
+ if root_node.is_named:
22
+ if root_node.type in ["string_content", "escape_sequence"]:
23
+ return None
24
+
25
+ current_node_id = AST_index[
26
+ (root_node.start_point, root_node.end_point, root_node.type)
27
+ ]
28
+ if current_node_id in self.all_tokens:
29
+
30
+ label = self.label[current_node_id]
31
+ else:
32
+ label = root_node.type
33
+ AST.add_node(
34
+ current_node_id,
35
+ node_type=root_node.type,
36
+ label=label,
37
+ shape="box",
38
+ style="rounded, filled",
39
+ fillcolor="#BFE6D3",
40
+ color="white",
41
+ )
42
+ for child in root_node.children:
43
+ if child.is_named:
44
+ child_id = self.get_AST_nodes(child, AST, AST_index)
45
+ if child_id != None:
46
+ AST.add_edge(current_node_id, child_id)
47
+ return current_node_id
48
+
49
+ def merge_nodes(self, G, nodes, new_node):
50
+ """
51
+ Merges the selected `nodes` of the graph G into one `new_node`,
52
+ meaning that all the edges that pointed to or from one of these
53
+ `nodes` will point to or from the `new_node`.
54
+ """
55
+
56
+ nodes.remove(new_node)
57
+
58
+ incoming_edges = G.in_edges(nodes, data=True)
59
+ outgoing_edges = G.out_edges(nodes, data=True)
60
+ for i in incoming_edges:
61
+ G.add_edge(i[0], new_node, **i[2])
62
+ for i in outgoing_edges:
63
+ G.add_edge(new_node, i[1], **i[2])
64
+
65
+ for n in nodes:
66
+ G.remove_node(n)
67
+
68
+ def collapse(self, G):
69
+ name_to_index_map = {}
70
+ node_attributes = {}
71
+
72
+ for variable in self.all_tokens:
73
+ if variable not in self.method_map:
74
+ name_to_index_map[self.label[variable]] = set()
75
+
76
+ for node, properties in G.nodes(data=True):
77
+ name = properties["label"]
78
+ if name in name_to_index_map.keys():
79
+ name_to_index_map[name].add(node)
80
+
81
+ for name, indexes in name_to_index_map.items():
82
+ node_list = list(indexes)
83
+ chosen_node = min(node_list)
84
+ node_attributes[chosen_node] = G.nodes(data=True)[chosen_node]
85
+ self.merge_nodes(G, node_list, chosen_node)
86
+ nx.set_node_attributes(G, node_attributes)
87
+
88
+ def minimize(self, root_node, blacklisted_nodes):
89
+ if root_node.type in self.properties.get("blacklisted", []):
90
+ blacklisted_nodes.append(
91
+ self.index[(root_node.start_point, root_node.end_point, root_node.type)]
92
+ )
93
+
94
+ for child in root_node.children:
95
+ self.minimize(child, blacklisted_nodes)
96
+
97
+ return blacklisted_nodes
98
+
99
+ def remove_blacklisted_nodes(self, G):
100
+ blacklisted_nodes = self.minimize(self.root_node, [])
101
+
102
+ for node in blacklisted_nodes:
103
+ for predecessor in G.predecessors(node):
104
+ for successor in G.successors(node):
105
+ G.add_edge(predecessor, successor)
106
+
107
+ G.remove_node(node)
108
+
109
+ def to_networkx(self):
110
+ G = nx.MultiDiGraph()
111
+ self.get_AST_nodes(self.root_node, G, self.index)
112
+ if self.properties.get("collapsed", False) == True:
113
+ self.collapse(G)
114
+ if self.properties.get("minimized", False) == True:
115
+ self.remove_blacklisted_nodes(G)
116
+
117
+ nx.set_edge_attributes(G, "AST_edge", "edge_type")
118
+ nx.set_edge_attributes(G, "indigo", "color")
119
+ nx.set_edge_attributes(G, "vee", "shape")
120
+ return G
@@ -0,0 +1,37 @@
1
+ from .AST import ASTGraph
2
+ from ...tree_parser.parser_driver import ParserDriver
3
+ from ...utils import postprocessor
4
+
5
+
6
+ class ASTDriver:
7
+ def __init__(
8
+ self,
9
+ src_language="c",
10
+ src_code="",
11
+ output_file="AST_output.json",
12
+ properties={},
13
+ ):
14
+ self.src_language = src_language
15
+ self.src_code = src_code
16
+ self.properties = properties
17
+ self.parser = ParserDriver(src_language, src_code).parser
18
+ self.root_node = self.parser.root_node
19
+ self.AST = ASTGraph(
20
+ self.src_language,
21
+ self.src_code,
22
+ self.properties,
23
+ self.root_node,
24
+ self.parser,
25
+ )
26
+ self.graph = self.AST.graph
27
+ if output_file:
28
+ self.json = postprocessor.write_networkx_to_json(self.graph, output_file)
29
+ try:
30
+ postprocessor.write_to_dot(
31
+ self.graph,
32
+ output_file.split(".")[0] + ".dot",
33
+ output_png=True,
34
+ src_language=self.src_language,
35
+ )
36
+ except Exception:
37
+ pass
File without changes
@@ -0,0 +1,42 @@
1
+ import networkx as nx
2
+
3
+
4
+ class CFGGraph:
5
+ def __init__(self, src_language, src_code, properties, root_node, parser):
6
+ self.src_language = src_language
7
+ self.src_code = src_code
8
+ self.properties = properties
9
+ self.root_node = root_node
10
+ self.parser = parser
11
+ self.index = self.parser.index
12
+ self.all_tokens = self.parser.all_tokens
13
+
14
+ def to_networkx(self, CFG_node_list, CFG_edge_list):
15
+ G = nx.MultiDiGraph()
16
+ for node in CFG_node_list:
17
+ label = str(node[1] + 1) + "_ " + node[2]
18
+ G.add_node(node[0], label=label, type_label=node[3])
19
+ for edge in CFG_edge_list:
20
+ additional_data = None
21
+ if len(edge) == 4:
22
+ additional_data = edge[3]
23
+ if edge[2].startswith("constructor_call"):
24
+ normal_label = "constructor_call"
25
+ elif edge[2].startswith("method_call"):
26
+ normal_label = "method_call"
27
+ elif edge[2].startswith("virtual_call"):
28
+ normal_label = "virtual_call"
29
+ else:
30
+ normal_label = edge[2]
31
+ G.add_edge(
32
+ edge[0],
33
+ edge[1],
34
+ controlflow_type=edge[2],
35
+ edge_type="CFG_edge",
36
+ label=normal_label,
37
+ color="red",
38
+ )
39
+ if additional_data:
40
+ for key, value in additional_data.items():
41
+ G.edges[edge[0], edge[1], 0][key] = value
42
+ return G