> As a consolation prize, the TranslateC build step can be enhanced with advanced settings, such as namespace stripping and validation of existing bindings. Since changes to this don't require changing the language, it's OK if the scope & complexity increase to some extent.
Unfortunately, we must not forget forget ABOMINATIONCase and NAMESPACED_PascalCase (we could also generalize this to any switch from one convention to another after the first word). I've found they usually are, in fact, identifiable and roundtrippable. I've found the following usually works (it fails for multi-word prefixes or non-leading exceptions, and of course single-word identifiers are ambiguous):
count and strip all leading, then trailing, symbols (in some languages this is not limited to underscore but said languages usually need special handling anyway)
for every chunk separated by underscore, space, or hyphen (this may be nothing):
if the chunk either has no uppercase or has no lowercase, simply use it as a word. Otherwise:
for every sliding-window pair of letters (c, d) in the chunk:
if c is uppercase:
if d is lowercase, or d is a digit and there is lowercase elsewhere:
start a new word before c
Then for the words-to-convention direction: space and kebab case don't have any good answer for affixes AFAIK. Otherwise, restore at least the leading underscores (trailing underscores are usually keyword-avoiding)
for snake and screaming case, be sure to prepend an underscore if the *result* would start with a digit
for camel variants, prepend an underscore to each *word* that starts with a digit. But if there were originally more than 1 leading underscores, use those instead for the first word. translateSpecialCaseNames(.{
.{ .src = "ABOMINATIONCase", .dst = "case" },
.{ .src = "NAMESPACED_PascalCase", .dst = "pascalCase" },
});
...in my own language bindings generator, being able to define such special case mappings is actually quite important, mainly for handling that situation where an automatic mapping would result in a reserved keyword.I do think that namespaces need to be semi-manually managed though (likely only at the project level), since often there are things that look like a namespace but shouldn't be treated like one, and it's not always obvious from a single identifier how many words should be treated as the namespace in some styles.
One special case is that sometimes C-ish libraries have class-likes like {Foo, FooBar, FooBaz} where the desired mapping is {foo.Foo, foo.Bar, foo.Baz}.
For instance Odin allows to define a 'link_prefix' for C APIs which is then stripped from the imported symbols:
This causes name transformations like: ...maybe even convert from common case-conventions (like snake-, camel-, pascal- case etc...) to Zig's naming convention so that imported C interfaces don't look so alien relative to native Zig interfaces.