Skip to content

Commit 55f15ef

Browse files
committed
Add naming transformer & remove source_path
Introduce NamingConfig and NameTransformer to support configurable naming conventions for files, types, enums, enum values, functions, methods, classes, fields, params, constants and aliases. Add a new codegen/naming.py with style helpers (snake/camel/pascal/kebab/screaming) and integrate the transformer into TypeMapper and generator (including output filename computation and Jinja context). Remove the obsolete source_path field from IR model, serializer, and mapped objects and switch normalizer to use a unified items list (and sort items by name) instead of maintaining separate per-kind buckets. Update example config and templates to demonstrate naming settings and minor template whitespace fixes.
1 parent df52c60 commit 55f15ef

File tree

9 files changed

+410
-90
lines changed

9 files changed

+410
-90
lines changed

tools/bindgen/codegen/context.py

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
IRStruct,
2020
IRType,
2121
)
22+
from .naming import NameTransformer
2223

2324

2425
@dataclass
@@ -56,7 +57,6 @@ class MappedField:
5657

5758
name: str
5859
type: MappedType
59-
source_path: Optional[str] = None
6060
raw: Optional[IRField] = None
6161

6262

@@ -79,20 +79,26 @@ class MappedStruct:
7979
name: str = ""
8080
fields: List[MappedField] = field(default_factory=list)
8181
qualified_name: Optional[str] = None
82-
source_path: Optional[str] = None
8382
raw: Optional[IRStruct] = None
8483

8584

85+
@dataclass
86+
class MappedEnumValue:
87+
"""An enum value."""
88+
89+
name: str = ""
90+
value: int = 0
91+
92+
8693
@dataclass
8794
class MappedEnum:
8895
"""An enum (no type mapping needed, but consistent interface)."""
8996

9097
kind: str = "enum"
9198
name: str = ""
92-
values: List[Dict[str, Any]] = field(default_factory=list)
99+
values: List[MappedEnumValue] = field(default_factory=list)
93100
scoped: bool = False
94101
qualified_name: Optional[str] = None
95-
source_path: Optional[str] = None
96102
raw: Optional[IREnum] = None
97103

98104

@@ -103,7 +109,6 @@ class MappedAlias:
103109
kind: str = "alias"
104110
name: str = ""
105111
target: Optional[MappedType] = None
106-
source_path: Optional[str] = None
107112
raw: Optional[IRAlias] = None
108113

109114

@@ -118,7 +123,6 @@ class MappedFunction:
118123
callconv: Optional[str] = None
119124
variadic: bool = False
120125
qualified_name: Optional[str] = None
121-
source_path: Optional[str] = None
122126
raw: Optional[IRFunction] = None
123127

124128

@@ -134,7 +138,6 @@ class MappedMethod:
134138
const: bool = False
135139
access: Optional[str] = None
136140
variadic: bool = False
137-
source_path: Optional[str] = None
138141
raw: Optional[IRMethod] = None
139142

140143

@@ -148,7 +151,6 @@ class MappedClass:
148151
methods: List[MappedMethod] = field(default_factory=list)
149152
bases: List[str] = field(default_factory=list)
150153
qualified_name: Optional[str] = None
151-
source_path: Optional[str] = None
152154
raw: Optional[IRClass] = None
153155

154156

@@ -160,7 +162,6 @@ class MappedConstant:
160162
name: str = ""
161163
type: Optional[MappedType] = None
162164
value: Any = 0
163-
source_path: Optional[str] = None
164165
raw: Optional[IRConstant] = None
165166

166167

@@ -213,6 +214,7 @@ class TypeMapper:
213214

214215
def __init__(self, config: MappingConfig):
215216
self.config = config
217+
self.namer = NameTransformer(config.naming)
216218

217219
def map_type(self, ir_type: Optional[IRType]) -> MappedType:
218220
"""Map an IRType to a MappedType."""
@@ -402,16 +404,15 @@ def _lookup_type(self, type_name: str, is_const: bool = False) -> str:
402404
def map_field(self, ir_field: IRField) -> MappedField:
403405
"""Map an IRField to a MappedField."""
404406
return MappedField(
405-
name=ir_field.name,
407+
name=self.namer.field_name(ir_field.name),
406408
type=self.map_type(ir_field.type),
407-
source_path=ir_field.source_path,
408409
raw=ir_field,
409410
)
410411

411412
def map_param(self, ir_param: IRParam) -> MappedParam:
412413
"""Map an IRParam to a MappedParam."""
413414
return MappedParam(
414-
name=ir_param.name,
415+
name=self.namer.param_name(ir_param.name),
415416
type=self.map_type(ir_param.type),
416417
nullable=ir_param.nullable,
417418
direction=ir_param.direction,
@@ -422,85 +423,84 @@ def map_struct(self, ir_struct: IRStruct) -> MappedStruct:
422423
"""Map an IRStruct to a MappedStruct."""
423424
return MappedStruct(
424425
kind="struct",
425-
name=ir_struct.name,
426+
name=self.namer.type_name(ir_struct.name),
426427
fields=[self.map_field(f) for f in ir_struct.fields],
427428
qualified_name=ir_struct.qualified_name,
428-
source_path=ir_struct.source_path,
429429
raw=ir_struct,
430430
)
431431

432432
def map_enum(self, ir_enum: IREnum) -> MappedEnum:
433433
"""Map an IREnum to a MappedEnum."""
434434
return MappedEnum(
435435
kind="enum",
436-
name=ir_enum.name,
437-
values=[{"name": v.name, "value": v.value} for v in ir_enum.values],
436+
name=self.namer.enum_name(ir_enum.name),
437+
values=[
438+
MappedEnumValue(
439+
name=self.namer.enum_value_name(v.name),
440+
value=v.value,
441+
)
442+
for v in ir_enum.values
443+
],
438444
scoped=ir_enum.scoped,
439445
qualified_name=ir_enum.qualified_name,
440-
source_path=ir_enum.source_path,
441446
raw=ir_enum,
442447
)
443448

444449
def map_alias(self, ir_alias: IRAlias) -> MappedAlias:
445450
"""Map an IRAlias to a MappedAlias."""
446451
return MappedAlias(
447452
kind="alias",
448-
name=ir_alias.name,
453+
name=self.namer.alias_name(ir_alias.name),
449454
target=self.map_type(ir_alias.target),
450-
source_path=ir_alias.source_path,
451455
raw=ir_alias,
452456
)
453457

454458
def map_function(self, ir_func: IRFunction) -> MappedFunction:
455459
"""Map an IRFunction to a MappedFunction."""
456460
return MappedFunction(
457461
kind="function",
458-
name=ir_func.name,
462+
name=self.namer.function_name(ir_func.name),
459463
return_type=self.map_type(ir_func.return_type),
460464
params=[self.map_param(p) for p in ir_func.params],
461465
callconv=ir_func.callconv,
462466
variadic=ir_func.variadic,
463467
qualified_name=ir_func.qualified_name,
464-
source_path=ir_func.source_path,
465468
raw=ir_func,
466469
)
467470

468471
def map_method(self, ir_method: IRMethod) -> MappedMethod:
469472
"""Map an IRMethod to a MappedMethod."""
470473
return MappedMethod(
471-
name=ir_method.name,
474+
name=self.namer.method_name(ir_method.name),
472475
return_type=self.map_type(ir_method.return_type),
473476
params=[self.map_param(p) for p in ir_method.params],
474477
method_kind=ir_method.kind,
475478
static=ir_method.static,
476479
const=ir_method.const,
477480
access=ir_method.access,
478481
variadic=ir_method.variadic,
479-
source_path=ir_method.source_path,
480482
raw=ir_method,
481483
)
482484

483485
def map_class(self, ir_class: IRClass) -> MappedClass:
484486
"""Map an IRClass to a MappedClass."""
485487
return MappedClass(
486488
kind="class",
487-
name=ir_class.name,
489+
name=self.namer.class_name(ir_class.name),
488490
fields=[self.map_field(f) for f in ir_class.fields],
489491
methods=[self.map_method(m) for m in ir_class.methods],
490492
bases=ir_class.bases,
491493
qualified_name=ir_class.qualified_name,
492-
source_path=ir_class.source_path,
493494
raw=ir_class,
494495
)
495496

496497
def map_constant(self, ir_const: IRConstant) -> MappedConstant:
497498
"""Map an IRConstant to a MappedConstant."""
498499
return MappedConstant(
499500
kind="constant",
500-
name=ir_const.name,
501+
name=self.namer.constant_name(ir_const.name),
501502
type=self.map_type(ir_const.type),
502503
value=ir_const.value,
503-
source_path=ir_const.source_path,
504504
raw=ir_const,
505505
)
506506

@@ -535,12 +535,14 @@ def build_context(module: IRModule, mapping: MappingConfig) -> Dict[str, Any]:
535535
536536
The context provides:
537537
- Mapped data with types already converted to target language
538+
- Names transformed according to naming config
538539
- Items in declaration order (via 'items' list)
539540
- Filtered accessors (types, enums, functions, etc.)
540541
- Raw IR data accessible via .raw attribute on each item
541542
- Global raw module accessible via 'raw' key
542543
"""
543544
mapper = TypeMapper(mapping)
545+
namer = NameTransformer(mapping.naming)
544546

545547
files = module.files
546548
sorted_paths = sorted(files.keys())
@@ -583,6 +585,8 @@ def build_context(module: IRModule, mapping: MappingConfig) -> Dict[str, Any]:
583585
"constants": constants,
584586
"aliases": aliases,
585587
"mapping": mapping,
588+
# Name transformer for custom transformations in templates
589+
"namer": namer,
586590
# Raw IR data for direct access
587591
"raw": module,
588592
}

tools/bindgen/codegen/generator.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
from typing import Any, Dict
55

66
from ..config import BindgenConfig
7-
from ..ir.model import IRFile, IRModule
7+
from ..ir.model import IRModule
88
from .context import MappedFile, build_context
9+
from .naming import NameTransformer
910

1011

1112
def _load_jinja():
@@ -34,8 +35,10 @@ def _build_file_context(
3435
"files": global_context["files"],
3536
"file_paths": global_context["file_paths"],
3637
"mapping": global_context["mapping"],
38+
"namer": global_context["namer"],
3739
"raw": global_context["raw"],
3840
# All items (for cross-referencing)
41+
"all_items": global_context["items"],
3942
"all_types": global_context["types"],
4043
"all_enums": global_context["enums"],
4144
"all_functions": global_context["functions"],
@@ -49,21 +52,15 @@ def _build_file_context(
4952
"file_name": p.name,
5053
"file_stem": p.stem,
5154
"file_ext": p.suffix,
55+
"items": mapped_file.items,
5256
"types": mapped_file.types,
5357
"enums": mapped_file.enums,
5458
"functions": mapped_file.functions,
5559
"classes": mapped_file.classes,
5660
"constants": mapped_file.constants,
5761
"aliases": mapped_file.aliases,
5862
# Convenience flags
59-
"is_empty": (
60-
not mapped_file.types
61-
and not mapped_file.enums
62-
and not mapped_file.functions
63-
and not mapped_file.classes
64-
and not mapped_file.constants
65-
and not mapped_file.aliases
66-
),
63+
"is_empty": not mapped_file.items,
6764
"has_types": bool(mapped_file.types),
6865
"has_enums": bool(mapped_file.enums),
6966
"has_functions": bool(mapped_file.functions),
@@ -77,6 +74,7 @@ def _compute_output_path(
7774
ir_file_path: str,
7875
template_name: str,
7976
out_dir: Path,
77+
namer: NameTransformer,
8078
) -> Path:
8179
"""
8280
Compute output path from IR file path and template name.
@@ -85,15 +83,20 @@ def _compute_output_path(
8583
- IR path: src/foundation/geometry.h
8684
- Template: file/dart.j2 -> out/src/foundation/geometry.dart
8785
- Template: file/rs.j2 -> out/src/foundation/geometry.rs
86+
87+
File name is transformed according to naming config.
8888
"""
8989
from pathlib import PurePosixPath
9090

9191
ir_path = PurePosixPath(ir_file_path)
9292
# Template stem becomes the new extension
9393
template_stem = Path(template_name).stem # e.g., "dart" from "dart.j2"
9494

95-
# Build output path: out_dir / ir_dir / ir_stem.template_stem
96-
output_name = f"{ir_path.stem}.{template_stem}"
95+
# Apply naming transformation to the file stem
96+
transformed_stem = namer.file_name(ir_path.stem)
97+
98+
# Build output path: out_dir / ir_dir / transformed_stem.template_stem
99+
output_name = f"{transformed_stem}.{template_stem}"
97100
output_path = out_dir / ir_path.parent / output_name
98101

99102
return output_path
@@ -129,6 +132,7 @@ def generate_bindings(
129132

130133
# Build context with preprocessed (mapped) data
131134
global_context = build_context(module, cfg.mapping)
135+
namer = global_context["namer"]
132136

133137
# 1. Render global templates (template/*.j2)
134138
for template_path in config_template_root.glob("*.j2"):
@@ -151,7 +155,7 @@ def generate_bindings(
151155
for template_path in file_templates:
152156
template = env.get_template(f"file/{template_path.name}")
153157
output_path = _compute_output_path(
154-
ir_file_path, template_path.name, out_dir
158+
ir_file_path, template_path.name, out_dir, namer
155159
)
156160

157161
# Create output directory if needed

0 commit comments

Comments
 (0)