Skip to content

Commit

Permalink
improve cpp_to_python.type_to_python
Browse files Browse the repository at this point in the history
  • Loading branch information
pthom committed Nov 14, 2024
1 parent b735d1b commit 61be10f
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 17 deletions.
14 changes: 8 additions & 6 deletions src/codemanip/code_replacements.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ def __init__(self, replace_what: str, by_what: str) -> None:
self.by_what = by_what

def apply(self, s: str) -> str:
# regex = self.replace_what_re
# subst = self.by_what
# r, nb = re.subn(regex, subst, s)

r, nb = self.replace_what_re.subn(self.by_what, s)
return r

Expand Down Expand Up @@ -53,9 +49,15 @@ def from_string(lines: str) -> RegexReplacementList:
return r

def apply(self, s: str) -> str:
previous_s = None
r = s
for replacement in self.replacements:
r = replacement.apply(r)
max_iterations = 3
iteration = 0
while previous_s != r and iteration < max_iterations:
previous_s = r
for replacement in self.replacements:
r = replacement.apply(r)
iteration += 1
return r

def add_last_regex_replacement(self, replacement: RegexReplacement) -> None:
Expand Down
25 changes: 14 additions & 11 deletions src/litgen/internal/cpp_to_python.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ def type_to_python(options: LitgenOptions, cpp_type_str: str) -> str:
r = r.replace("static ", "")
r = r.replace("constexpr ", "")
r = options.type_replacements.apply(r).strip()
r = r.replace("::", ".")

def normalize_whitespace(s: str) -> str:
import re
return re.sub(r'\s+', ' ', s).strip()
r = normalize_whitespace(r)
return r


Expand Down Expand Up @@ -550,7 +556,6 @@ def standard_type_replacements() -> RegexReplacementList:
Consists mostly of
* types translations
* NULL, nullptr, void translation
* number translation (e.g. `1.5f` -> `1.5`)
"""
replacements_str = r"""
\bunsigned \s*int\b -> int
Expand Down Expand Up @@ -583,29 +588,27 @@ def standard_type_replacements() -> RegexReplacementList:
\bstd::string\b -> str
\bstd::unique_ptr<(.*?)> -> \1
\bstd::shared_ptr<(.*?)> -> \1
\bstd::vector\s*<\s*([\w:]*)\s*> -> List[\1]
\bstd::array\s*<\s*([\w:]*)\s*,\s*([\w:]*)\s*> -> List[\1]
\bstd::tuple<(.*)> -> Tuple[\1]
\bstd::pair<(.*)> -> Tuple[\1]
\bstd::vector\s*<\s*(.*?)\s*> -> List[\1]
\bstd::array\s*<\s*(.*?)\s*,\s*(.*?)\s*> -> List[\1]
\bstd::tuple<(.*?)> -> Tuple[\1]
\bstd::pair<(.*?)> -> Tuple[\1]
\bstd::variant<(.*?)> -> Union[\1]
\bstd::optional<(.*?)> -> Optional[\1]
\bstd::map<\s*([\w:]*)\s*,\s*([\w:]*)\s*> -> Dict[\1, \2]
\bstd::map<\s*(.*?)\s*,\s*(.*?)\s*> -> Dict[\1, \2]
\bvoid\s*\* -> Any
\bvoid\b -> None
\bpy::array\b -> np.ndarray
\bpy::ndarray<(.*)> -> np.ndarray
\bpy::ndarray<(.*?)> -> np.ndarray
\bnb::array\b -> np.ndarray
\bnb::ndarray<(.*)> -> np.ndarray
\bnb::ndarray<(.*?)> -> np.ndarray
\bconst\b -> REMOVE
\bmutable\b -> REMOVE
& -> REMOVE
\* -> REMOVE
:: -> .
"""
# Note: the two last regexes replace C numbers like 1.5f or 1.5d by 1.5

replaces = RegexReplacementList.from_string(replacements_str)
return replaces
Expand Down
32 changes: 32 additions & 0 deletions src/litgen/tests/internal/cpp_to_python_test.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations
import litgen
from litgen.internal import cpp_to_python


def test_standard_replacements():
Expand All @@ -22,3 +23,34 @@ def test_std_array():
r = litgen.standard_type_replacements().apply(s)
assert r == "List[ImVec4] Colors;"
print(r)


def test_type_to_python() -> None:
options = litgen.LitgenOptions()
def my_type_to_python(s: str) -> str:
return cpp_to_python.type_to_python(options, s)

assert my_type_to_python("unsigned int") == "int"
assert my_type_to_python("std::vector<std::optional<int>>") == "List[Optional[int]]"
assert my_type_to_python("std::optional<std::vector<int>>") == "Optional[List[int]]"
assert my_type_to_python("std::map<int, std::string>") == "Dict[int, str]"
assert my_type_to_python("std::vector<std::map<int, std::string>>") == "List[Dict[int, str]]"
assert my_type_to_python("std::map<std::string, std::vector<int>>") == "Dict[str, List[int]]"
assert my_type_to_python("std::tuple<int, float, std::string>") == "Tuple[int, float, str]"
assert my_type_to_python("std::function<void(int)>") == "Callable[[int], None]"
assert my_type_to_python("std::function<int(std::string, double)>") == "Callable[[str, float], int]"
assert my_type_to_python("std::vector<int*>") == "List[int]"
assert my_type_to_python("std::vector<int&>") == "List[int]"
assert my_type_to_python("std::tuple<int, float, std::string>") == "Tuple[int, float, str]"
assert my_type_to_python("std::variant<int, float, std::string>") == "Union[int, float, str]"
assert my_type_to_python("std::vector<std::map<int, std::vector<std::string>>>") == "List[Dict[int, List[str]]]"
assert my_type_to_python("std::function<void(std::vector<int>&, const std::string&)>") == "Callable[[List[int], str], None]"

# Known limitations
# =================
# volatile is not handled
assert my_type_to_python("volatile int") == "volatile int"
# unsigned char is not handled (up the user to decide if it is a char or an int)
assert my_type_to_python("unsigned char") == "unsigned char"
# Missed allocator in optional (this is a corner case, which we do not handle)
assert my_type_to_python("std::optional<int, std::allocator<int>>") == "Optional[int, std.allocator<int]>"

0 comments on commit 61be10f

Please sign in to comment.