Per the README contract, an object schema without an explicit
`additionalProperties` was documented as rejected at generation time,
but the code defaulted it to `false` and silently generated a strict
parser that rejects valid documents with extra properties.
Make the code match the documented contract by raising GenError when
`additionalProperties` is absent, with a clear message instructing the
author to set it explicitly to false. Update the README prose section
to list absent additionalProperties alongside true, and fix existing
schemas and tests to declare additionalProperties explicitly.
Closes#55
Move the zero-detection check in the generated parseJsonInt64 before the
finalExp < 0 guard. Previously, valid JSON numbers whose mathematical
value is 0 but written with a large negative exponent (e.g. 0e-2,
0.0e-2, -0e-2, 0e-20) were rejected because the negative-finalExp early
return ran before the all-zero-digits branch could set out = 0. Non-zero
values with negative exponents are still correctly rejected.
Closes#56
The generator already preserves nullability for self-referential object
$defs thanks to prior fixes, but issue #14 had no regression coverage.
Add a test using the exact reproduction schema from the issue and verify
that both the outer and recursive `self` fields accept `null`.
Cyclic array definitions (directly or through a chain of array $defs)
created a self-referential TArr, which then caused infinite recursion in
base_cpp, storage_cpp, and _walk_arrays.
Object-only cycles are already broken with std::unique_ptr, but
array-only cycles have no object edge for break_cycles to cut.
Detect them after building an array's items by following TArr.elem links
and raise a clear GenError so generation fails gracefully rather than
overflowing the Python stack.
Closes#33
Fixes#35.
Add a helper to escape C++ string literals so that JSON control characters
(\n, \r, \t, and other bytes below 0x20) are emitted as escape sequences
instead of raw bytes. Use it for:
- field comments that include the JSON property key
- object key comparison literals in matchKey()
- enum name arrays
Also add regression tests that generate and syntax-check headers for
schemas containing newlines and other control characters in property keys
and enum values.
Replace the O(k^2) loop that erased leading zeros one byte at a time
from the front of a std::string with a single linear scan and one
erase(0, n) call.
Also adds regression tests for issue #34:
- correctness cases for numbers with leading fractional zeros
- a static check that the generated code no longer contains the
quadratic pattern
- a large-input case (100k leading zeros) that reproduces the
vulnerable shape
Closes#34
Extend the existing per-definition cache (`self._building`) to enum,
array, and scalar $defs, not just object definitions. This ensures
that multiple $refs to the same non-object definition reuse the same
C++ type instead of generating Role, Role2, Role3, etc.
- Cache the built (type, nullable) tuple under defname for enum,
scalar, and array definitions.
- Pre-register array definitions before recursing into items so $ref
cycles resolve to the same TArr instance.
- Store object definitions as (TObj, nullable) tuples so nullable object
$defs also preserve their nullability when referenced.
Add a regression test for issue #17 covering reused enum and array-of-enum
$defs.
Replace the double-based fallback in generated integer slots with a
string-to-int64 parser that handles decimal points and exponents
without losing precision near the int64 boundaries.
The old path used std::from_chars<double> and compared against
±9223372036854775808.0, which rounds the int64 max and min so that
valid values are rejected and out-of-range negatives are accepted.
The new helper:
- Parses sign, integer part, optional fraction, and optional exponent.
- Strips trailing zeros to cancel fractional places.
- Rejects non-integral values and overflow using exact uint64_t
arithmetic.
Adds regression tests covering root integer and object-field integer
boundary values, including the cases from issue #19.
Following review feedback, the generator no longer supports permissive
objects. Changes:
- Reject `additionalProperties: true` at generation time.
- Treat an absent `additionalProperties` as `false`, so every object is
strict by default and unknown keys are rejected during parsing.
- Remove the now-dead permissive-object infrastructure: `Kind::Skip`,
`Cat::Skip`, `kSkip`, the per-frame `unknown` key set, and `isStrict()`.
- Update the README feature/rejection tables accordingly.
- Remove the permissive "loose" object from example.schema.json and the
associated tests from test_gen.cpp.
- Add Python unit tests verifying the new `additionalProperties` behavior.
All tests pass (`ctest --output-on-failure`).
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
Add concept, consteval, constinit, co_await, co_return, co_yield,
requires, module, and import to _CPP_KEYWORDS so property names that
happen to be C++20 keywords get sanitized with a trailing underscore.
Closes#18
Distinct JSON enum values can sanitize to the same C++ identifier
(e.g. "foo-bar" and "foo_bar" both become `foo_bar`), producing an
invalid `enum class` with duplicate constants.
Add `unique_enum_identifiers()` which appends a numeric suffix to
later collisions while preserving enum declaration order, so the
index-to-JSON-value mapping used by the generated parser stays intact.
Also add contrib/schemagen/test_schemagen.py and wire the schemagen
Python tests plus the existing example.schema.json/test_gen.cpp example
into ctest via CMakeLists.txt.
Closes#4