mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 09:48:32 +00:00
138 lines
4.2 KiB
Python
138 lines
4.2 KiB
Python
from __future__ import annotations
|
|
|
|
import keyword
|
|
import re
|
|
|
|
|
|
INTEGER_CPP_TYPES = {"int", "size_t", "std::size_t", "unsigned int", "uint32_t"}
|
|
|
|
|
|
def normalize_identifier(name: str) -> str:
|
|
result: list[str] = []
|
|
previous_was_lower = False
|
|
for character in name:
|
|
if character.isupper() and previous_was_lower:
|
|
result.append("_")
|
|
result.append(character.lower() if character.isalnum() else "_")
|
|
previous_was_lower = character.islower() or character.isdigit()
|
|
normalized = "".join(result).strip("_")
|
|
while "__" in normalized:
|
|
normalized = normalized.replace("__", "_")
|
|
return normalized
|
|
|
|
|
|
def cpp_leaf_name(cpp_name: str) -> str:
|
|
return cpp_name.rsplit("::", 1)[-1]
|
|
|
|
|
|
def pascal_case(name: str) -> str:
|
|
parts = [part for part in re.split(r"[_\W]+", name) if part]
|
|
if parts:
|
|
return "".join(part[:1].upper() + part[1:] for part in parts)
|
|
return name[:1].upper() + name[1:] if name else name
|
|
|
|
|
|
def safe_python_identifier(name: str) -> str:
|
|
return f"{name}_" if keyword.iskeyword(name) else name
|
|
|
|
|
|
def normalize_cpp_type(cpp_type: str) -> str:
|
|
normalized = cpp_type.replace("class ", "").replace("struct ", "").replace("enum ", "")
|
|
normalized = normalized.replace("std::basic_string<char>", "std::string")
|
|
normalized = normalized.replace("std::__cxx11::basic_string<char>", "std::string")
|
|
normalized = re.sub(r"\b(const|volatile)\b", " ", normalized)
|
|
normalized = normalized.replace("&", " ")
|
|
normalized = re.sub(r"\s+", "", normalized)
|
|
return normalized
|
|
|
|
|
|
def strip_pointer(cpp_type: str) -> str:
|
|
normalized = normalize_cpp_type(cpp_type)
|
|
while normalized.endswith("*"):
|
|
normalized = normalized[:-1]
|
|
return normalized
|
|
|
|
|
|
def c_identifier_from_cpp_name(cpp_name: str, c_prefix: str | None = None) -> str:
|
|
parts = [part for part in cpp_name.split("::") if part]
|
|
if c_prefix and parts and normalize_identifier(parts[0]) == normalize_identifier(c_prefix):
|
|
parts = parts[1:]
|
|
return normalize_identifier("_".join(parts))
|
|
|
|
|
|
def default_enum_c_name(c_prefix: str, py_name: str) -> str:
|
|
return f"{c_prefix}_{normalize_identifier(py_name)}_t"
|
|
|
|
|
|
def default_enum_value_c_name(enum_c_name: str, value_name: str) -> str:
|
|
return f"{enum_c_name.upper()}_{value_name}"
|
|
|
|
|
|
def handle_adapter_name(cpp_name: str) -> str:
|
|
return f"handle:{normalize_cpp_type(cpp_name)}"
|
|
|
|
|
|
def is_handle_adapter(adapter: str) -> bool:
|
|
return adapter.startswith("handle:")
|
|
|
|
|
|
def handle_adapter_target(adapter: str) -> str:
|
|
return adapter.split(":", 1)[1]
|
|
|
|
|
|
def enum_adapter_name(cpp_name: str) -> str:
|
|
return f"enum:{normalize_cpp_type(cpp_name)}"
|
|
|
|
|
|
def is_enum_adapter(adapter: str) -> bool:
|
|
return adapter.startswith("enum:")
|
|
|
|
|
|
def enum_adapter_target(adapter: str) -> str:
|
|
return adapter.split(":", 1)[1]
|
|
|
|
|
|
def sequence_adapter_name(cpp_name: str) -> str:
|
|
return f"sequence:{normalize_cpp_type(cpp_name)}"
|
|
|
|
|
|
def is_sequence_adapter(adapter: str) -> bool:
|
|
return adapter.startswith("sequence:")
|
|
|
|
|
|
def sequence_adapter_target(adapter: str) -> str:
|
|
return adapter.split(":", 1)[1]
|
|
|
|
|
|
def cpp_type_lists_match(actual_types: list[str], expected_types: list[str]) -> bool:
|
|
if len(actual_types) != len(expected_types):
|
|
return False
|
|
return all(cpp_types_equivalent(actual, expected) for actual, expected in zip(actual_types, expected_types))
|
|
|
|
|
|
def cpp_types_equivalent(actual_type: str, expected_type: str) -> bool:
|
|
actual = normalize_cpp_type(actual_type)
|
|
expected = normalize_cpp_type(expected_type)
|
|
if actual == expected:
|
|
return True
|
|
if "<" in actual or "<" in expected:
|
|
return False
|
|
actual_leaf = strip_pointer(actual).rsplit("::", 1)[-1]
|
|
expected_leaf = strip_pointer(expected).rsplit("::", 1)[-1]
|
|
return actual_leaf == expected_leaf
|
|
|
|
|
|
def resolve_cpp_type_key(cpp_type: str, candidates: set[str]) -> str | None:
|
|
canonical = normalize_cpp_type(cpp_type)
|
|
if canonical in candidates:
|
|
return canonical
|
|
leaf = strip_pointer(canonical).rsplit("::", 1)[-1]
|
|
matches = [
|
|
candidate
|
|
for candidate in candidates
|
|
if strip_pointer(candidate).rsplit("::", 1)[-1] == leaf
|
|
]
|
|
if len(matches) == 1:
|
|
return matches[0]
|
|
return None
|