diff --git a/wasm/codegen/generators/common.py b/wasm/codegen/generators/common.py index a056c325..c809d265 100644 --- a/wasm/codegen/generators/common.py +++ b/wasm/codegen/generators/common.py @@ -31,12 +31,12 @@ def write_to_file(filepath: str, content: str) -> None: print(f"Error writing to output file: {filepath} - {e}") -def lowercase_first_letter(input_string: str) -> str: +def decapitalize(input_string: str) -> str: """Lowercases the first letter of a string.""" return input_string[:1].lower() + input_string[1:] -def uppercase_first_letter(input_string: str) -> str: +def capitalize(input_string: str) -> str: """Uppercases the first letter of a string.""" return input_string[:1].upper() + input_string[1:] diff --git a/wasm/codegen/generators/functions.py b/wasm/codegen/generators/functions.py index fc4d49af..76f757f7 100644 --- a/wasm/codegen/generators/functions.py +++ b/wasm/codegen/generators/functions.py @@ -60,9 +60,8 @@ def get_const_qualifier(func: ast_nodes.FunctionDecl) -> str: def should_be_wrapped(func: ast_nodes.FunctionDecl) -> bool: """Checks if a MuJoCo function needs a wrapper function.""" - return ( - get_pointer_return_inner_value_type(func) is not None - or any(get_inner_value_type(param) for param in func.parameters) + return get_pointer_return_inner_value_type(func) is not None or any( + get_inner_value_type(param) for param in func.parameters ) @@ -89,9 +88,7 @@ def generate_function_wrapper(func: ast_nodes.FunctionDecl) -> str: if bound_check_code: builder.line(bound_check_code) - c_params_list = get_params_string_maybe_with_conversion( - func.parameters - ) + c_params_list = get_params_string_maybe_with_conversion(func.parameters) c_params_str = ", ".join(c_params_list) c_call = f"{func.name}({c_params_str})" c_statement = get_compatible_return_call(func, c_call) @@ -146,9 +143,7 @@ def get_param_unpack_statement( return f"UNPACK_VALUE({inner_type.name}, {p.name});" -def get_param_string( - p: ast_nodes.FunctionParameterDecl -) -> str: +def get_param_string(p: ast_nodes.FunctionParameterDecl) -> str: """Generates a list of C++ parameter declarations as strings.""" if ( @@ -159,7 +154,7 @@ def get_param_string( # Pointer to struct parameters const_qualifier = "const " if p.type.inner_type.is_const else "" return ( - f"{const_qualifier}{common.uppercase_first_letter(p.type.inner_type.name)}&" + f"{const_qualifier}{common.capitalize(p.type.inner_type.name)}&" f" {p.name}" ) elif ( @@ -258,7 +253,7 @@ def get_compatible_return_type(func: ast_nodes.FunctionDecl) -> str: return "std::string" if inner_type.name not in constants.PRIMITIVE_TYPES: const_qualifier = get_const_qualifier(func) - return f"""{const_qualifier}std::optional<{common.uppercase_first_letter(inner_type.name)}>""" + return f"""{const_qualifier}std::optional<{common.capitalize(inner_type.name)}>""" if ( isinstance(func.return_type, ast_nodes.ValueType) and func.return_type.name in constants.PRIMITIVE_TYPES @@ -275,7 +270,7 @@ def get_converted_struct_to_class( const_qualifier = get_const_qualifier(func) return_type = cast(ast_nodes.PointerType, func.return_type) struct_name = cast(ast_nodes.ValueType, return_type.inner_type).name - class_constructor = common.uppercase_first_letter(struct_name) + class_constructor = common.capitalize(struct_name) return_str = f"{class_constructor}(result)" return f"""{const_qualifier}{struct_name}* result = {invoker}; if (result == nullptr) {{ diff --git a/wasm/codegen/generators/structs.py b/wasm/codegen/generators/structs.py index 8d31303a..1a784b39 100644 --- a/wasm/codegen/generators/structs.py +++ b/wasm/codegen/generators/structs.py @@ -93,7 +93,7 @@ def _generate_field_data( """Generates the C++ definition and binding code for the struct field.""" f = field w = struct_wrapper_name - s = common.lowercase_first_letter(w) + s = common.decapitalize(w) if f.name in constants.MANUAL_FIELDS.get(w, []): # Note: Manually handled MjModel fields are special cased so that a @@ -130,7 +130,7 @@ def _generate_field_data( elif isinstance(f.type, ast_nodes.ValueType) and f.type.name.startswith("mj"): return WrappedFieldData( - definition=f"{common.uppercase_first_letter(f.type.name)} {f.name};", + definition=f"{common.capitalize(f.type.name)} {f.name};", typename=_get_field_struct_type(f.type), binding=_simple_property_binding(f, w, setter=False, reference=True), ptr_initialization=f"{f.name}(&ptr_->{f.name})", @@ -156,9 +156,7 @@ def _generate_field_data( return WrappedFieldData( binding=_simple_property_binding(f, w, setter=False, reference=True), typename=anonymous_struct_name, - definition=( - f"{common.uppercase_first_letter(anonymous_struct_name)} {f.name};" - ), + definition=f"{common.capitalize(anonymous_struct_name)} {f.name};", ptr_initialization=f"{f.name}(&ptr_->{f.name})", ptr_copy_reset=f"{f.name}.set(&ptr_->{f.name});", is_primitive_or_fixed_size=True, @@ -201,8 +199,7 @@ def _generate_field_data( elif inner_type_name.startswith("mj"): return WrappedFieldData( definition=( - f"std::vector<{common.uppercase_first_letter(inner_type_name)}>" - f" {f.name};" + f"std::vector<{common.capitalize(inner_type_name)}> {f.name};" ), ptr_initialization=f"{f.name}(&ptr_->{f.name})", typename=_get_field_struct_type(f.type), @@ -270,7 +267,7 @@ def _generate_field_data( and w not in constants.MANUAL_FIELDS.keys() ): ptr_field = cast(ast_nodes.PointerType, f.type) - wrapper_field_name = common.uppercase_first_letter( + wrapper_field_name = common.capitalize( cast(ast_nodes.ValueType, ptr_field.inner_type).name ) return WrappedFieldData( @@ -351,14 +348,14 @@ def build_struct_header( ): """Builds the C++ header file code for a struct.""" s = struct_name - w = common.uppercase_first_letter(s) + w = common.capitalize(s) if w in constants.MANUAL_STRUCTS: return "" if ( - s not in constants.ANONYMOUS_STRUCTS and - s not in introspect_structs.STRUCTS + s not in constants.ANONYMOUS_STRUCTS + and s not in introspect_structs.STRUCTS ): raise RuntimeError(f"Struct {s} not found in introspect structs") @@ -442,7 +439,7 @@ def build_struct_source( return "" s = struct_name - w = common.uppercase_first_letter(s) + w = common.capitalize(s) is_mjs = w.startswith("Mjs") member_inits = _find_member_inits(wrapped_fields) @@ -511,7 +508,7 @@ def _build_struct_bindings( wrapped_fields: List[WrappedFieldData], ): """Builds the C++ bindings for a struct.""" - w = common.uppercase_first_letter(struct_name) + w = common.capitalize(struct_name) is_mjs = w.startswith("Mjs") builder = code_builder.CodeBuilder() @@ -595,7 +592,7 @@ def generate_wasm_bindings( wrapped_structs: Dict[str, WrappedStructData] = {} for struct_name in structs_to_bind: s = struct_name - w = common.uppercase_first_letter(s) + w = common.capitalize(s) if s in introspect_structs.STRUCTS: struct_fields = introspect_structs.STRUCTS[s].fields diff --git a/wasm/codegen/tests/coverage_test.py b/wasm/codegen/tests/coverage_test.py index fbf5c15e..671cc12e 100644 --- a/wasm/codegen/tests/coverage_test.py +++ b/wasm/codegen/tests/coverage_test.py @@ -117,11 +117,11 @@ class BindingCoverageTest(absltest.TestCase): """Asserts that each struct is either not bound or bound in structs.cc.""" bound_structs = _get_bound_structs_from_cc() all_structs = { - common.uppercase_first_letter(struct_name) + common.capitalize(struct_name) for struct_name in introspect_structs.STRUCTS.keys() } skipped_structs = { - common.uppercase_first_letter(struct_name) + common.capitalize(struct_name) for struct_name in constants.SKIPPED_STRUCTS } missing_structs = [] diff --git a/wasm/codegen/tests/generators_test.py b/wasm/codegen/tests/generators_test.py index 55a42132..b7e32683 100644 --- a/wasm/codegen/tests/generators_test.py +++ b/wasm/codegen/tests/generators_test.py @@ -56,14 +56,12 @@ class CodeBuilderTest(absltest.TestCase): class CommonUtilsTest(absltest.TestCase): - def test_uppercase_first_letter(self): - self.assertEqual(common.uppercase_first_letter(""), "") - self.assertEqual(common.uppercase_first_letter("hello"), "Hello") - self.assertEqual(common.uppercase_first_letter("1st place"), "1st place") - self.assertEqual(common.uppercase_first_letter("!wow"), "!wow") - self.assertEqual( - common.uppercase_first_letter(" leading space"), " leading space" - ) + def test_capitalize(self): + self.assertEqual(common.capitalize(""), "") + self.assertEqual(common.capitalize("hello"), "Hello") + self.assertEqual(common.capitalize("1st place"), "1st place") + self.assertEqual(common.capitalize("!wow"), "!wow") + self.assertEqual(common.capitalize(" leading space"), " leading space") class FunctionUtilsTest(absltest.TestCase): @@ -197,12 +195,8 @@ class FunctionUtilsTest(absltest.TestCase): self.assertTrue(functions.is_excluded_function_name("mjui_function")) self.assertTrue(functions.is_excluded_function_name("mju_malloc")) self.assertTrue(functions.is_excluded_function_name("mj_makeData")) - self.assertFalse( - functions.is_excluded_function_name("mjv_updateScene") - ) - self.assertFalse( - functions.is_excluded_function_name("mj_normalFunction") - ) + self.assertFalse(functions.is_excluded_function_name("mjv_updateScene")) + self.assertFalse(functions.is_excluded_function_name("mj_normalFunction")) self.assertFalse( functions.is_excluded_function_name("mju_someOtherFunction") ) @@ -493,7 +487,8 @@ class StructFieldCodeBuilderTest(absltest.TestCase): type=ast_nodes.ValueType(name="int"), doc="number of geoms", ) - self.assertEqual(structs._generate_field_data(field, "ngeom").definition, + self.assertEqual( + structs._generate_field_data(field, "ngeom").definition, """ int ngeom() const { return ptr_->ngeom;