Coverage for src/renaissance/integrations/clang/clang_adapter.py: 87%
31 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-10-02 13:12 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-10-02 13:12 +0000
1"""AI: Adapter that parses C/C++ source with libclang into the internal LST representation."""
3from clang import cindex
5from renaissance.integrations.tree_sitter.lst import LST, LSTNode
6from renaissance.utils.ast_utils import detect_placeholder
9class ClangAdapter:
10 """AI: Adapter that parses C/C++ source with libclang into the internal LST representation."""
12 def __init__(self, clang_path: str | None = None, args: list | None = None):
13 """AI: Configure the libclang native library location and default parse arguments."""
14 if clang_path and cindex.Config.library_path is None:
15 cindex.Config.set_library_path(clang_path)
16 self.args = args or ["-std=c++17"]
18 def parse(self, file_path: str) -> LST:
19 """AI: Parse the C/C++ source file at file_path into an LST."""
20 index = cindex.Index.create()
21 translation_unit = index.parse(file_path, args=self.args)
22 return LST(self._convert_node(translation_unit.cursor))
24 def load_from_text(self, text: str, file_name: str):
25 """AI: Parse in-memory C/C++ source text as the given file_name into an LST."""
26 index = cindex.Index.create()
27 translation_unit = index.parse(file_name, unsaved_files=[(file_name, text)], args=[])
28 return LST(self._convert_node(translation_unit.cursor))
30 def to_lst(self, source_code: str) -> LST:
31 """AI: Parse a C/C++ source code string into an LST."""
32 # source_code= replace_dollar(source_code)
33 return self.load_from_text(source_code, "no_src.cpp")
35 def _convert_node(self, cursor: cindex.Cursor, parent: LSTNode | None = None) -> LSTNode:
36 try:
37 kind = cursor.kind.name
38 except Exception as e:
39 print(e.__cause__)
40 kind = "invalid kind"
41 signature = cursor.spelling or cursor.displayname or kind
43 is_ph, coerced_type, ph_name = detect_placeholder(signature, kind)
45 node = LSTNode(
46 node_type=coerced_type if is_ph else kind,
47 properties={
48 "spelling": cursor.spelling,
49 "type": str(cursor.type.spelling),
50 "location": str(cursor.location),
51 "is_definition": cursor.is_definition(),
52 "name": ph_name,
53 **(
54 {
55 "placeholder": True,
56 "placeholder_name": ph_name,
57 "original_node_type": cursor.kind.name,
58 }
59 if is_ph
60 else {}
61 ),
62 },
63 signature=signature,
64 offset=cursor.extent.start.offset,
65 parent=parent,
66 )
68 for child in cursor.get_children():
69 child_node = self._convert_node(child, parent=node)
70 node.add_child(child_node)
71 return node