From 5b11156e05c9c2181319f1671f7f8a9230c3e6b1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 01:10:34 +0000 Subject: [PATCH 1/4] Initial plan From 1e8641d744b6341b0b5ab5259e36529e686786c5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 01:13:32 +0000 Subject: [PATCH 2/4] Initial analysis: TS and JS generators share significant duplicated code Co-authored-by: rijesha <7819200+rijesha@users.noreply.github.com> --- package-lock.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..12e3643b --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "struct-frame", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} From 769e4647ab90272953cde7ee28a57db5d28ca0ae Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 01:18:03 +0000 Subject: [PATCH 3/4] Refactor TS and JS generators to share common field generation logic via ts_js_base.py Co-authored-by: rijesha <7819200+rijesha@users.noreply.github.com> --- src/struct_frame/js_gen.py | 134 +++-------------------- src/struct_frame/ts_gen.py | 153 ++++---------------------- src/struct_frame/ts_js_base.py | 194 +++++++++++++++++++++++++++++++++ 3 files changed, 233 insertions(+), 248 deletions(-) create mode 100644 src/struct_frame/ts_js_base.py diff --git a/src/struct_frame/js_gen.py b/src/struct_frame/js_gen.py index 2151fd21..a21439e2 100644 --- a/src/struct_frame/js_gen.py +++ b/src/struct_frame/js_gen.py @@ -4,46 +4,24 @@ JavaScript code generator for struct-frame. This module generates human-readable JavaScript code for struct serialization. -It reuses the TypeScript generator's type mappings and logic but outputs -JavaScript syntax (CommonJS) instead of TypeScript. +It reuses the shared TypeScript/JavaScript base module for common logic +but outputs JavaScript syntax (CommonJS) instead of TypeScript. """ from struct_frame import version, NamingStyleC +from struct_frame.ts_js_base import ( + common_types, + common_typed_array_methods, + BaseFieldGen, + BaseEnumGen, +) import time StyleC = NamingStyleC() -# Reuse type mappings from TypeScript generator -js_types = { - "int8": 'Int8', - "uint8": 'UInt8', - "int16": 'Int16LE', - "uint16": 'UInt16LE', - "bool": 'Boolean8', - "double": 'Float64LE', - "float": 'Float32LE', - "int32": 'Int32LE', - "uint32": 'UInt32LE', - "int64": 'BigInt64LE', - "uint64": 'BigUInt64LE', - "string": 'String', -} - -# JavaScript typed array methods for array fields -js_typed_array_methods = { - "int8": 'Int8Array', - "uint8": 'UInt8Array', - "int16": 'Int16Array', - "uint16": 'UInt16Array', - "bool": 'UInt8Array', # Boolean arrays stored as UInt8Array - "double": 'Float64Array', - "float": 'Float32Array', - "int32": 'Int32Array', - "uint32": 'UInt32Array', - "int64": 'BigInt64Array', - "uint64": 'BigUInt64Array', - "string": 'StructArray', # String arrays use StructArray -} +# Use shared type mappings +js_types = common_types +js_typed_array_methods = common_typed_array_methods class EnumJsGen(): @@ -87,92 +65,14 @@ def generate(field, packageName): class FieldJsGen(): + """JavaScript field generator using shared base logic.""" + @staticmethod def generate(field, packageName): - result = '' - # Check if field is an enum type - isEnum = field.isEnum if hasattr(field, 'isEnum') else False - var_name = StyleC.var_name(field.name) - type_name = field.fieldType - - # Handle arrays - if field.is_array: - if field.fieldType == "string": - if field.size_option is not None: # Fixed size array [size=X] - # Fixed string array: string[size] -> StructArray with fixed length - result += " // Fixed string array: %d strings, each exactly %d chars\n" % (field.size_option, field.element_size) - # For string arrays, we need to use StructArray with String elements - result += " .StructArray('%s', %d, new Struct().String('value', %d).compile())" % (var_name, field.size_option, field.element_size) - else: # Variable size array [max_size=X] - # Variable string array: string[max_size=X, element_size=Y] -> count + StructArray - result += " // Variable string array: up to %d strings, each max %d chars\n" % (field.max_size, field.element_size) - result += " .UInt8('%s_count')\n" % var_name - result += " .StructArray('%s_data', %d, new Struct().String('value', %d).compile())" % (var_name, field.max_size, field.element_size) - else: - # Regular type arrays - if type_name in js_types: - base_type = js_types[type_name] - array_method = js_typed_array_methods.get(type_name, 'StructArray') - elif isEnum: - # Enum arrays are stored as UInt8Array - base_type = 'UInt8' - array_method = 'UInt8Array' - else: - # Struct arrays - use the original type name (e.g., 'Sensor' not 'sensor') - base_type = '%s_%s' % (packageName, type_name) - array_method = 'StructArray' - - if field.size_option is not None: # Fixed size array [size=X] - # Fixed array: type[size] -> TypedArray with fixed length - array_size = field.size_option - result += ' // Fixed array: always %d elements\n' % array_size - if array_method == 'StructArray': - result += " .%s('%s', %d, %s)" % (array_method, var_name, array_size, base_type) - else: - result += " .%s('%s', %d)" % (array_method, var_name, array_size) - else: # Variable size array [max_size=X] - # Variable array: type[max_size=X] -> count + TypedArray - max_count = field.max_size - result += ' // Variable array: up to %d elements\n' % max_count - result += " .UInt8('%s_count')\n" % var_name - if array_method == 'StructArray': - result += " .%s('%s_data', %d, %s)" % (array_method, var_name, max_count, base_type) - else: - result += " .%s('%s_data', %d)" % (array_method, var_name, max_count) - else: - # Non-array fields (existing logic) - if field.fieldType == "string": - if hasattr(field, 'size_option') and field.size_option is not None: - # Fixed string: string[size] -> fixed length string - result += ' // Fixed string: exactly %d chars\n' % field.size_option - result += " .String('%s', %d)" % (var_name, field.size_option) - elif hasattr(field, 'max_size') and field.max_size is not None: - # Variable string: string[max_size=X] -> length + data - result += ' // Variable string: up to %d chars\n' % field.max_size - result += " .UInt8('%s_length')\n" % var_name - result += " .String('%s_data', %d)" % (var_name, field.max_size) - else: - # Default string handling (should not occur with new parser) - result += " .String('%s')" % var_name - else: - # Regular types - if type_name in js_types: - type_name = js_types[type_name] - else: - type_name = '%s_%s' % (packageName, StyleC.struct_name(type_name)) - - if isEnum: - # Enums are stored as UInt8 in JavaScript - result += " .UInt8('%s')" % var_name - else: - result += " .%s('%s')" % (type_name, var_name) - - leading_comment = field.comments - if leading_comment: - for c in leading_comment: - result = c + "\n" + result - - return result + """Generate JavaScript field definition using shared base.""" + return BaseFieldGen.generate( + field, packageName, js_types, js_typed_array_methods + ) # --------------------------------------------------------------------------- diff --git a/src/struct_frame/ts_gen.py b/src/struct_frame/ts_gen.py index f251ad1b..4773f238 100644 --- a/src/struct_frame/ts_gen.py +++ b/src/struct_frame/ts_gen.py @@ -1,57 +1,27 @@ #!/usr/bin/env python3 # kate: replace-tabs on; indent-width 4; +""" +TypeScript code generator for struct-frame. + +This module generates TypeScript code for struct serialization using +ES6 module syntax (import/export). +""" from struct_frame import version, NamingStyleC +from struct_frame.ts_js_base import ( + common_types, + common_typed_array_methods, + ts_array_types, + BaseFieldGen, + BaseEnumGen, +) import time StyleC = NamingStyleC() -ts_types = { - "int8": 'Int8', - "uint8": 'UInt8', - "int16": 'Int16LE', - "uint16": 'UInt16LE', - "bool": 'Boolean8', - "double": 'Float64LE', - "float": 'Float32LE', - "int32": 'Int32LE', - "uint32": 'UInt32LE', - "int64": 'BigInt64LE', - "uint64": 'BigUInt64LE', - "string": 'String', -} - -# TypeScript type mappings for array declarations -ts_array_types = { - "int8": 'number', - "uint8": 'number', - "int16": 'number', - "uint16": 'number', - "bool": 'boolean', - "double": 'number', - "float": 'number', - "int32": 'number', - "uint32": 'number', - "uint64": 'bigint', - "int64": 'bigint', - "string": 'string', -} - -# TypeScript typed array methods for array fields -ts_typed_array_methods = { - "int8": 'Int8Array', - "uint8": 'UInt8Array', - "int16": 'Int16Array', - "uint16": 'UInt16Array', - "bool": 'UInt8Array', # Boolean arrays stored as UInt8Array - "double": 'Float64Array', - "float": 'Float32Array', - "int32": 'Int32Array', - "uint32": 'UInt32Array', - "int64": 'BigInt64Array', - "uint64": 'BigUInt64Array', - "string": 'StructArray', # String arrays use StructArray -} +# Use shared type mappings +ts_types = common_types +ts_typed_array_methods = common_typed_array_methods class EnumTsGen(): @@ -94,93 +64,14 @@ def generate(field, packageName): class FieldTsGen(): + """TypeScript field generator using shared base logic.""" + @staticmethod def generate(field, packageName): - result = '' - # Check if field is an enum type - isEnum = field.isEnum if hasattr(field, 'isEnum') else False - var_name = StyleC.var_name(field.name) - type_name = field.fieldType - - # Handle arrays - if field.is_array: - if field.fieldType == "string": - if field.size_option is not None: # Fixed size array [size=X] - # Fixed string array: string[size] -> StructArray with fixed length - result += f' // Fixed string array: {field.size_option} strings, each exactly {field.element_size} chars\n' - # For string arrays, we need to use StructArray with String elements - result += f' .StructArray(\'{var_name}\', {field.size_option}, new Struct().String(\'value\', {field.element_size}).compile())' - else: # Variable size array [max_size=X] - # Variable string array: string[max_size=X, element_size=Y] -> count + StructArray - result += f' // Variable string array: up to {field.max_size} strings, each max {field.element_size} chars\n' - result += f' .UInt8(\'{var_name}_count\')\n' - result += f' .StructArray(\'{var_name}_data\', {field.max_size}, new Struct().String(\'value\', {field.element_size}).compile())' - else: - # Regular type arrays - if type_name in ts_types: - base_type = ts_types[type_name] - array_method = ts_typed_array_methods.get(type_name, 'StructArray') - elif isEnum: - # Enum arrays are stored as UInt8Array - base_type = 'UInt8' - array_method = 'UInt8Array' - else: - # Struct arrays - use the original type name (e.g., 'Sensor' not 'sensor') - base_type = f'{packageName}_{type_name}' - array_method = 'StructArray' - - if field.size_option is not None: # Fixed size array [size=X] - # Fixed array: type[size] -> TypedArray with fixed length - # For fixed arrays, size_option contains the exact size - array_size = field.size_option - result += f' // Fixed array: always {array_size} elements\n' - if array_method == 'StructArray': - result += f' .{array_method}(\'{var_name}\', {array_size}, {base_type})' - else: - result += f' .{array_method}(\'{var_name}\', {array_size})' - else: # Variable size array [max_size=X] - # Variable array: type[max_size=X] -> count + TypedArray - max_count = field.max_size # For variable arrays, max_size is the maximum count - result += f' // Variable array: up to {max_count} elements\n' - result += f' .UInt8(\'{var_name}_count\')\n' - if array_method == 'StructArray': - result += f' .{array_method}(\'{var_name}_data\', {max_count}, {base_type})' - else: - result += f' .{array_method}(\'{var_name}_data\', {max_count})' - else: - # Non-array fields (existing logic) - if field.fieldType == "string": - if hasattr(field, 'size_option') and field.size_option is not None: - # Fixed string: string[size] -> fixed length string - result += f' // Fixed string: exactly {field.size_option} chars\n' - result += f' .String(\'{var_name}\', {field.size_option})' - elif hasattr(field, 'max_size') and field.max_size is not None: - # Variable string: string[max_size=X] -> length + data - result += f' // Variable string: up to {field.max_size} chars\n' - result += f' .UInt8(\'{var_name}_length\')\n' - result += f' .String(\'{var_name}_data\', {field.max_size})' - else: - # Default string handling (should not occur with new parser) - result += f' .String(\'{var_name}\')' - else: - # Regular types - if type_name in ts_types: - type_name = ts_types[type_name] - else: - type_name = f'{packageName}_{StyleC.struct_name(type_name)}' - - if isEnum: - # Enums are stored as UInt8 in TypeScript - result += f' .UInt8(\'{var_name}\')' - else: - result += f' .{type_name}(\'{var_name}\')' - - leading_comment = field.comments - if leading_comment: - for c in leading_comment: - result = c + "\n" + result - - return result + """Generate TypeScript field definition using shared base.""" + return BaseFieldGen.generate( + field, packageName, ts_types, ts_typed_array_methods + ) # --------------------------------------------------------------------------- diff --git a/src/struct_frame/ts_js_base.py b/src/struct_frame/ts_js_base.py new file mode 100644 index 00000000..8e80d701 --- /dev/null +++ b/src/struct_frame/ts_js_base.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python3 +# kate: replace-tabs on; indent-width 4; +""" +Shared base module for TypeScript and JavaScript code generators. + +This module provides common functionality used by both ts_gen.py and js_gen.py +to reduce code duplication and ensure consistent behavior. +""" + +from struct_frame import NamingStyleC + +StyleC = NamingStyleC() + +# Common type mappings shared by TypeScript and JavaScript generators +# Maps proto types to struct method names +common_types = { + "int8": 'Int8', + "uint8": 'UInt8', + "int16": 'Int16LE', + "uint16": 'UInt16LE', + "bool": 'Boolean8', + "double": 'Float64LE', + "float": 'Float32LE', + "int32": 'Int32LE', + "uint32": 'UInt32LE', + "int64": 'BigInt64LE', + "uint64": 'BigUInt64LE', + "string": 'String', +} + +# TypeScript type mappings for array declarations (TypeScript only) +ts_array_types = { + "int8": 'number', + "uint8": 'number', + "int16": 'number', + "uint16": 'number', + "bool": 'boolean', + "double": 'number', + "float": 'number', + "int32": 'number', + "uint32": 'number', + "uint64": 'bigint', + "int64": 'bigint', + "string": 'string', +} + +# Common typed array methods for array fields +# Maps proto types to typed array method names +common_typed_array_methods = { + "int8": 'Int8Array', + "uint8": 'UInt8Array', + "int16": 'Int16Array', + "uint16": 'UInt16Array', + "bool": 'UInt8Array', # Boolean arrays stored as UInt8Array + "double": 'Float64Array', + "float": 'Float32Array', + "int32": 'Int32Array', + "uint32": 'UInt32Array', + "int64": 'BigInt64Array', + "uint64": 'BigUInt64Array', + "string": 'StructArray', # String arrays use StructArray +} + + +class BaseFieldGen: + """Base field generator with shared logic for TypeScript and JavaScript.""" + + @staticmethod + def generate(field, packageName, types_dict, typed_array_methods_dict): + """ + Generate field definition code. + + Args: + field: Field object containing field metadata + packageName: Package name prefix + types_dict: Dictionary mapping proto types to struct method names + typed_array_methods_dict: Dictionary mapping proto types to typed array method names + + Returns: + String containing the field definition code + """ + result = '' + isEnum = field.isEnum if hasattr(field, 'isEnum') else False + var_name = StyleC.var_name(field.name) + type_name = field.fieldType + + # Handle arrays + if field.is_array: + if field.fieldType == "string": + if field.size_option is not None: # Fixed size array [size=X] + result += " // Fixed string array: %d strings, each exactly %d chars\n" % ( + field.size_option, field.element_size) + result += " .StructArray('%s', %d, new Struct().String('value', %d).compile())" % ( + var_name, field.size_option, field.element_size) + else: # Variable size array [max_size=X] + result += " // Variable string array: up to %d strings, each max %d chars\n" % ( + field.max_size, field.element_size) + result += " .UInt8('%s_count')\n" % var_name + result += " .StructArray('%s_data', %d, new Struct().String('value', %d).compile())" % ( + var_name, field.max_size, field.element_size) + else: + # Regular type arrays + if type_name in types_dict: + base_type = types_dict[type_name] + array_method = typed_array_methods_dict.get( + type_name, 'StructArray') + elif isEnum: + base_type = 'UInt8' + array_method = 'UInt8Array' + else: + base_type = '%s_%s' % (packageName, type_name) + array_method = 'StructArray' + + if field.size_option is not None: # Fixed size array [size=X] + array_size = field.size_option + result += ' // Fixed array: always %d elements\n' % array_size + if array_method == 'StructArray': + result += " .%s('%s', %d, %s)" % ( + array_method, var_name, array_size, base_type) + else: + result += " .%s('%s', %d)" % ( + array_method, var_name, array_size) + else: # Variable size array [max_size=X] + max_count = field.max_size + result += ' // Variable array: up to %d elements\n' % max_count + result += " .UInt8('%s_count')\n" % var_name + if array_method == 'StructArray': + result += " .%s('%s_data', %d, %s)" % ( + array_method, var_name, max_count, base_type) + else: + result += " .%s('%s_data', %d)" % ( + array_method, var_name, max_count) + else: + # Non-array fields + if field.fieldType == "string": + if hasattr(field, 'size_option') and field.size_option is not None: + result += ' // Fixed string: exactly %d chars\n' % field.size_option + result += " .String('%s', %d)" % (var_name, field.size_option) + elif hasattr(field, 'max_size') and field.max_size is not None: + result += ' // Variable string: up to %d chars\n' % field.max_size + result += " .UInt8('%s_length')\n" % var_name + result += " .String('%s_data', %d)" % (var_name, field.max_size) + else: + result += " .String('%s')" % var_name + else: + # Regular types + if type_name in types_dict: + type_name = types_dict[type_name] + else: + type_name = '%s_%s' % (packageName, + StyleC.struct_name(type_name)) + + if isEnum: + result += " .UInt8('%s')" % var_name + else: + result += " .%s('%s')" % (type_name, var_name) + + # Prepend leading comments + leading_comment = field.comments + if leading_comment: + for c in leading_comment: + result = c + "\n" + result + + return result + + +class BaseEnumGen: + """Base enum generator with shared logic for TypeScript and JavaScript.""" + + @staticmethod + def get_enum_values(field): + """ + Get enum values with proper formatting. + + Args: + field: Enum field object + + Returns: + Tuple of (enum_length, enum_values_data) + where enum_values_data is a list of (name, value, comments) tuples + """ + enum_length = len(field.data) + enum_values_data = [] + for index, d in enumerate(field.data): + leading_comment = field.data[d][1] + value = field.data[d][0] + is_last = (index == enum_length - 1) + enum_values_data.append({ + 'name': StyleC.enum_entry(d), + 'value': value, + 'comments': leading_comment, + 'is_last': is_last + }) + return enum_length, enum_values_data From f6c1cfae9e17a710e77c5a2c6e465a2cd3748e3a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 01:19:15 +0000 Subject: [PATCH 4/4] Remove accidental package-lock.json from root directory Co-authored-by: rijesha <7819200+rijesha@users.noreply.github.com> --- package-lock.json | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 12e3643b..00000000 --- a/package-lock.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "struct-frame", - "lockfileVersion": 3, - "requires": true, - "packages": {} -}