forked from weaselab/weaseljson
schemagen: accept integral exponent/decimal numbers for integer slots
JSON Schema's 'integer' type matches any number with a zero fractional part, but the generated builder rejected forms like 1e3 or 2.0 because it only ran std::from_chars<int64_t>. Keep that exact path for plain integer literals (so large values near INT64_MAX stay exact), and fall back to parsing as double for the rest, requiring an integral value within int64 range. Add <cmath> for std::trunc and cover the new cases in test_gen.
This commit is contained in:
@@ -134,6 +134,32 @@ int main() {
|
||||
printf("ok minimal object\n");
|
||||
}
|
||||
|
||||
// ---- integer accepts any number with no fractional part (JSON Schema) ----
|
||||
{
|
||||
struct {
|
||||
const char *json;
|
||||
int64_t age;
|
||||
} cases[] = {
|
||||
{R"({"name":"x","age":4e1})", 40},
|
||||
{R"({"name":"x","age":2.0})", 2},
|
||||
{R"({"name":"x","age":-1.5e1})", -15},
|
||||
{R"({"name":"x","age":0e0})", 0},
|
||||
};
|
||||
for (auto &c : cases) {
|
||||
RootBuilder b;
|
||||
WeaselJsonStatus s = parseStrided(b, c.json);
|
||||
CHECK(s == WeaselJson_OK);
|
||||
if (s == WeaselJson_OK) {
|
||||
Root r = b.take();
|
||||
CHECK(r.age == c.age);
|
||||
}
|
||||
}
|
||||
printf("ok integer in exponent/decimal form\n");
|
||||
}
|
||||
// a fractional or out-of-range value is still rejected for an integer slot
|
||||
expectReject(R"({"name":"x","age":2.5e0})", "non-integral exponent form");
|
||||
expectReject(R"({"name":"x","age":1e30})", "integer out of int64 range");
|
||||
|
||||
// ---- schema-violation rejections ----
|
||||
expectReject(R"({"name":"x"})", "missing required field 'age'");
|
||||
expectReject(R"({"name":"x","age":"notnum"})", "wrong type (string for int)");
|
||||
|
||||
@@ -487,6 +487,7 @@ class Emitter:
|
||||
#pragma once
|
||||
|
||||
#include <charconv>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
@@ -928,8 +929,21 @@ private:
|
||||
if (si.cat == Cat::Int) {{
|
||||
int64_t v = 0;
|
||||
auto r = std::from_chars(b, e, v);
|
||||
if (r.ec != std::errc() || r.ptr != e) {{ reject(); return; }}
|
||||
*(int64_t *)p = v;
|
||||
if (r.ec == std::errc() && r.ptr == e) {{
|
||||
*(int64_t *)p = v; // plain integer literal: parsed exactly
|
||||
}} else {{
|
||||
// JSON Schema "integer" accepts any number with no fractional part,
|
||||
// including exponent/decimal forms like 1e3 or 2.0. Parse those as a
|
||||
// double and require an integral value within int64 range.
|
||||
double d = 0;
|
||||
auto rd = std::from_chars(b, e, d);
|
||||
if (rd.ec != std::errc() || rd.ptr != e || d != std::trunc(d) ||
|
||||
d < -9223372036854775808.0 || d >= 9223372036854775808.0) {{
|
||||
reject();
|
||||
return;
|
||||
}}
|
||||
*(int64_t *)p = (int64_t)d;
|
||||
}}
|
||||
}} else {{
|
||||
double v = 0;
|
||||
auto r = std::from_chars(b, e, v);
|
||||
|
||||
Reference in New Issue
Block a user