schemagen: reserve generated Root/Skip/RootScalar and avoid Kind/ArrN collisions
CI / build (-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++, clang-arm64, ubuntu-latest-arm64, true) (pull_request) Successful in 53s
CI / pre-commit (pull_request) Successful in 51s
CI / build (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc-arm64, ubuntu-latest-arm64, false) (pull_request) Successful in 49s
CI / build (-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++, clang-amd64, ubuntu-latest-amd64, true) (pull_request) Successful in 1m29s
CI / build (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc-amd64, ubuntu-latest-amd64, false) (pull_request) Successful in 1m25s
CI / build (-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++, clang-arm64, ubuntu-latest-arm64, true) (pull_request) Successful in 53s
CI / pre-commit (pull_request) Successful in 51s
CI / build (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc-arm64, ubuntu-latest-arm64, false) (pull_request) Successful in 49s
CI / build (-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++, clang-amd64, ubuntu-latest-amd64, true) (pull_request) Successful in 1m29s
CI / build (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc-amd64, ubuntu-latest-amd64, false) (pull_request) Successful in 1m25s
Make the type-name allocator aware of the identifiers the generator emits itself (`Root` alias, `Skip`/`RootScalar` Kind enumerators) so user `$defs` names can no longer collide with them. Array-kind names (`Arr0`, `Arr1`, ...) are now allocated only after checking for object/enum names, preventing duplicate `Kind` enumerators when a schema defines e.g. `Arr0`. Add Python regression tests that also syntax-check the generated headers with a C++ compiler. Closes #21
This commit is contained in:
@@ -216,12 +216,26 @@ class Builder:
|
||||
base = camel(hint)
|
||||
name = base
|
||||
i = 1
|
||||
while name in self._used_names:
|
||||
while self._name_taken(name):
|
||||
candidate = f"{base}{i}"
|
||||
# A numeric suffix can itself land on a reserved generated name
|
||||
# (e.g. hint "Arr0" -> "Arr01"). Use an underscore separator so
|
||||
# we never loop through the reserved block.
|
||||
if self._is_reserved(candidate):
|
||||
candidate = f"{base}_{i}"
|
||||
name = candidate
|
||||
i += 1
|
||||
name = f"{base}{i}"
|
||||
self._used_names.add(name)
|
||||
return name
|
||||
|
||||
def _name_taken(self, name):
|
||||
return name in self._used_names or self._is_reserved(name)
|
||||
|
||||
@staticmethod
|
||||
def _is_reserved(name):
|
||||
"""Names generated internally that must not collide with user types."""
|
||||
return name in ("Root", "Skip", "RootScalar")
|
||||
|
||||
def ref_name(self, ref):
|
||||
if not ref.startswith("#/"):
|
||||
raise GenError(f"only local $ref supported, got: {ref}")
|
||||
@@ -435,6 +449,7 @@ class Emitter:
|
||||
self.kind_order = [] # all Kind enumerators in declaration order
|
||||
self.root_ty = None
|
||||
self.root_nullable = False
|
||||
self._arr_counter = 0
|
||||
|
||||
# -- type strings -------------------------------------------------------
|
||||
def base_cpp(self, ty):
|
||||
@@ -465,11 +480,19 @@ class Emitter:
|
||||
def arr_kind(self, tarr):
|
||||
sig = self.base_cpp(tarr)
|
||||
if sig not in self.arr_kinds:
|
||||
name = f"Arr{len(self.arr_kinds)}"
|
||||
name = self._fresh_arr_kind_name()
|
||||
self.arr_kinds[sig] = name
|
||||
self.arr_types.append((name, tarr))
|
||||
self.b._used_names.add(name)
|
||||
return self.arr_kinds[sig]
|
||||
|
||||
def _fresh_arr_kind_name(self):
|
||||
while True:
|
||||
name = f"Arr{self._arr_counter}"
|
||||
self._arr_counter += 1
|
||||
if not self.b._name_taken(name):
|
||||
return name
|
||||
|
||||
def cat(self, ty):
|
||||
if isinstance(ty, TScalar):
|
||||
return {"str": "Str", "int": "Int", "dbl": "Dbl", "bool": "Bool"}[ty.kind]
|
||||
|
||||
Reference in New Issue
Block a user