Skip to content

Commit 418a16b

Browse files
authored
Merge pull request #2 from kunaltyagi/gen_func
2 parents 58b00ea + 89da38f commit 418a16b

4 files changed

Lines changed: 117 additions & 25 deletions

File tree

bindings/python/scripts/generate.py

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from context import scripts
22
import scripts.utils as utils
3+
from typing import Any, List, Dict
34

45

56
class bind:
@@ -44,7 +45,7 @@ def __init__(self, root: dict, module_name: str) -> None:
4445
"ANONYMOUS_UNION_DECL": handled_elsewhere, # in (handle_struct_decl) via get_fields_from_anonymous
4546
"ANONYMOUS_STRUCT_DECL": handled_elsewhere, # in (handle_struct_decl) via get_fields_from_anonymous
4647
"FRIEND_DECL": unsure,
47-
"FUNCTION_DECL": unsure,
48+
"FUNCTION_DECL": self.handle_function,
4849
# EXPRs: An expression that refers to a member of a struct, union, class, Objective-C class, etc.
4950
"CALL_EXPR": handled_by_pybind,
5051
"UNEXPOSED_EXPR": unsure,
@@ -281,6 +282,26 @@ def handle_struct_decl(self) -> None:
281282
f'.def("{sub_item["name"]}", py::overload_cast<>(&{self.name}::{sub_item["name"]}))'
282283
)
283284

285+
def handle_function(self) -> None:
286+
"""
287+
Handles `CursorKind.FUNCTION_DECL`
288+
289+
- Bind the function and its parameter list
290+
"""
291+
parameter_type_list = []
292+
details = self._state_stack[-1]
293+
for sub_item in self.members:
294+
if sub_item["kind"] == "PARM_DECL":
295+
parameter_type_list.append(f'"{sub_item["name"]}"_a')
296+
297+
parameter_type_list = ",".join(parameter_type_list)
298+
if parameter_type_list:
299+
parameter_type_list = "," + parameter_type_list
300+
301+
self._linelist.append(
302+
f'm.def("{self.name}", &{self.name} {parameter_type_list});'
303+
)
304+
284305
def handle_constructor(self) -> None:
285306
"""
286307
Handles `CursorKind.CONSTRUCTOR`
@@ -295,35 +316,35 @@ def handle_constructor(self) -> None:
295316
# generate parameter type list
296317
for sub_item in self.members:
297318
if sub_item["kind"] == "PARM_DECL":
298-
if sub_item["element_type"] == "LValueReference":
299-
for sub_sub_item in sub_item["members"]:
300-
if sub_sub_item["kind"] == "TYPE_REF":
301-
# @TODO: Make more robust
302-
type_ref = (
303-
sub_sub_item["name"]
304-
.replace("struct ", "")
305-
.replace("pcl::", "")
306-
)
307-
parameter_type_list.append(f"{type_ref} &")
308-
elif sub_item["element_type"] == "Elaborated":
309-
namespace_ref = ""
310-
for sub_sub_item in sub_item["members"]:
311-
if sub_sub_item["kind"] == "NAMESPACE_REF":
312-
namespace_ref += f'{sub_sub_item["name"]}::'
313-
if sub_sub_item["kind"] == "TYPE_REF":
314-
parameter_type_list.append(
315-
f'{namespace_ref}{sub_sub_item["name"]}'
316-
)
317-
elif sub_item["element_type"] in ("Float", "Int"):
318-
parameter_type_list.append(f'{sub_item["element_type"].lower()}')
319-
else:
320-
parameter_type_list.append(f'{sub_item["element_type"]}')
319+
parameter_type_list.append(self.get_parm_types(sub_item))
321320
parameter_type_list = ",".join(parameter_type_list)
322321

323322
# default ctor `.def(py::init<>())` already inserted while handling struct/class decl
324323
if parameter_type_list:
325324
self._linelist.append(f".def(py::init<{parameter_type_list}>())")
326325

326+
def get_parm_types(self, item: Dict[str, Any]) -> List[str]:
327+
if item["element_type"] == "LValueReference":
328+
for sub_item in item["members"]:
329+
if sub_item["kind"] == "TYPE_REF":
330+
# @TODO: Make more robust
331+
type_ref = (
332+
sub_item["name"].replace("struct ", "").replace("pcl::", "")
333+
)
334+
parameter_type_list = f"{type_ref} &"
335+
elif item["element_type"] == "Elaborated":
336+
namespace_ref = ""
337+
for sub_item in item["members"]:
338+
if sub_item["kind"] == "NAMESPACE_REF":
339+
namespace_ref += f'{sub_item["name"]}::'
340+
if sub_item["kind"] == "TYPE_REF":
341+
parameter_type_list = f'{namespace_ref}{sub_item["name"]}'
342+
elif item["element_type"] in ("Float", "Double", "Int"):
343+
parameter_type_list = f'{item["element_type"].lower()}'
344+
else:
345+
parameter_type_list = f'{item["element_type"]}'
346+
return parameter_type_list
347+
327348
def handle_inclusion_directive(self) -> None:
328349
"""
329350
Handle `CursorKind.INCLUSION_DIRECTIVE`

bindings/python/scripts/parse.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ def generate_parsed_info(node):
9797
parsed_info["line"] = cursor.location.line
9898
parsed_info["column"] = cursor.location.column
9999
parsed_info["kind"] = cursor.kind.name
100+
parsed_info["tokens"] = [x.spelling for x in cursor.get_tokens()]
101+
100102
if cursor.is_anonymous():
101103
parsed_info["kind"] = "ANONYMOUS_" + parsed_info["kind"]
102104
parsed_info["name"] = cursor.spelling

bindings/python/tests/test_generate.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,40 @@ def get_expected_string(file_include, expected_module_code):
8282
return expected_output
8383

8484

85+
def test_function_without_parameters(tmp_path):
86+
cpp_code_block = "void AFunction();"
87+
file_include, output = generate_bindings(
88+
tmp_path=tmp_path, cpp_code_block=cpp_code_block, module_name="pcl"
89+
)
90+
91+
expected_module_code = """
92+
PYBIND11_MODULE(pcl, m){
93+
m.def("AFunction", &AFunction);
94+
}
95+
"""
96+
97+
assert output == get_expected_string(
98+
file_include=file_include, expected_module_code=expected_module_code
99+
)
100+
101+
102+
def test_function_with_parameters(tmp_path):
103+
cpp_code_block = "void AFunction(int firstParam, double secondParam);"
104+
file_include, output = generate_bindings(
105+
tmp_path=tmp_path, cpp_code_block=cpp_code_block, module_name="pcl"
106+
)
107+
108+
expected_module_code = """
109+
PYBIND11_MODULE(pcl, m){
110+
m.def("AFunction", &AFunction, "firstParam"_a, "secondParam"_a);
111+
}
112+
"""
113+
114+
assert output == get_expected_string(
115+
file_include=file_include, expected_module_code=expected_module_code
116+
)
117+
118+
85119
def test_struct_without_members(tmp_path):
86120
cpp_code_block = "struct AStruct {};"
87121
file_include, output = generate_bindings(

bindings/python/tests/test_parse.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,42 @@ def test_parsed_info_structure(tmp_path):
134134
assert len(parsed_info["members"]) == 0
135135

136136

137-
def test_call_expr(tmp_path):
137+
def test_function_decl_without_parameters(tmp_path):
138+
file_contents = """
139+
int aFunction();
140+
"""
141+
parsed_info = get_parsed_info(tmp_path=tmp_path, file_contents=file_contents)
142+
143+
func_decl = parsed_info["members"][0]
144+
145+
assert func_decl["kind"] == "FUNCTION_DECL"
146+
assert func_decl["name"] == "aFunction"
147+
assert func_decl["result_type"] == "int"
148+
149+
150+
def test_function_decl_with_parameters(tmp_path):
151+
file_contents = """
152+
int aFunction(int firstParam, double secondParam);
153+
"""
154+
parsed_info = get_parsed_info(tmp_path=tmp_path, file_contents=file_contents)
155+
156+
func_decl = parsed_info["members"][0]
157+
158+
assert func_decl["kind"] == "FUNCTION_DECL"
159+
assert func_decl["name"] == "aFunction"
160+
assert func_decl["result_type"] == "int"
161+
162+
first_param = func_decl["members"][0]
163+
second_param = func_decl["members"][1]
164+
165+
assert first_param["name"] == "firstParam"
166+
assert first_param["element_type"] == "Int"
167+
168+
assert second_param["name"] == "secondParam"
169+
assert second_param["element_type"] == "Double"
170+
171+
172+
def test_simple_call_expr(tmp_path):
138173
file_contents = """
139174
int aFunction() {
140175
return 1;

0 commit comments

Comments
 (0)