yeptris 0.6.59.1-arm-linux-eabihf → 0.6.60.1-arm-linux-eabihf
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/yeptris_native/version.h +2 -2
- data/lib/yeptris/3.4/native.so +0 -0
- data/lib/yeptris/ffi.rb +19 -0
- data/lib/yeptris/yaml.rb +61 -0
- data/lib/yeptris.rb +1 -1
- data/libyeptris.so +0 -0
- data/vendor/libyeptris/CMakeLists.txt +1 -1
- data/vendor/libyeptris/src/CMakeLists.txt +7 -0
- data/vendor/libyeptris/src/include/yeptris/emit.h +30 -2
- data/vendor/libyeptris/src/include/yeptris/parse.h +52 -0
- data/vendor/libyeptris/src/include/yeptris/resolve.h +8 -0
- data/vendor/libyeptris/src/include/yeptris/validate.h +222 -21
- data/vendor/libyeptris/src/include/yeptris/values.h +8 -2
- data/vendor/libyeptris/src/yeptris/cbor/cbor.h +10 -0
- data/vendor/libyeptris/src/yeptris/cbor/decode.c +5 -7
- data/vendor/libyeptris/src/yeptris/cbor/encode.c +9 -9
- data/vendor/libyeptris/src/yeptris/cbor/from_marshal.c +1 -1
- data/vendor/libyeptris/src/yeptris/common/error.h +2 -0
- data/vendor/libyeptris/src/yeptris/doc.h +20 -0
- data/vendor/libyeptris/src/yeptris/emit/emit.c +85 -13
- data/vendor/libyeptris/src/yeptris/emit/writer.c +158 -642
- data/vendor/libyeptris/src/yeptris/emit/writer.h +15 -1
- data/vendor/libyeptris/src/yeptris/events/push.c +28 -3
- data/vendor/libyeptris/src/yeptris/events/values.c +24 -0
- data/vendor/libyeptris/src/yeptris/marshal/to_json.c +650 -0
- data/vendor/libyeptris/src/yeptris/parse/engine.c +121 -40
- data/vendor/libyeptris/src/yeptris/parse/stream_iter.c +326 -0
- data/vendor/libyeptris/src/yeptris/parse.c +173 -43
- data/vendor/libyeptris/src/yeptris/resolve/compat11.c +1 -1
- data/vendor/libyeptris/src/yeptris/resolve/core12.c +11 -3
- data/vendor/libyeptris/src/yeptris/resolve/full12.c +201 -0
- data/vendor/libyeptris/src/yeptris/resolve/json12.c +12 -6
- data/vendor/libyeptris/src/yeptris/resolve/resolver.h +20 -0
- data/vendor/libyeptris/src/yeptris/validate/compile.c +39 -3
- data/vendor/libyeptris/src/yeptris/validate/internal.h +223 -7
- data/vendor/libyeptris/src/yeptris/validate/jsonschema.c +645 -115
- data/vendor/libyeptris/src/yeptris/validate/output.c +246 -0
- data/vendor/libyeptris/src/yeptris/validate/regexp.c +623 -157
- data/vendor/libyeptris/src/yeptris/validate/registry.c +118 -0
- data/vendor/libyeptris/src/yeptris/validate/resources.c +601 -0
- data/vendor/libyeptris/src/yeptris/validate/stream.c +1155 -0
- data/vendor/libyeptris/src/yeptris/validate/validate.c +288 -145
- metadata +8 -1
|
@@ -36,6 +36,24 @@ YEPTRIS_API const char* yeptris_last_error(uint32_t* line, uint32_t* col) {
|
|
|
36
36
|
static YeptrisDocument parse_impl(const char* buf, size_t len, const YeptrisParseOptions* opts,
|
|
37
37
|
int json_mode, int lazy, YeptrisStatus* status);
|
|
38
38
|
|
|
39
|
+
/* The schema -> resolver dispatch point (OCP: the typing SSOT's ONE
|
|
40
|
+
* switch). Every build route selects its resolver here — the event
|
|
41
|
+
* entry, the engine and the direct-builder DOM, the fused block
|
|
42
|
+
* runner (and validate/stream.c's event route, through the event
|
|
43
|
+
* entry). */
|
|
44
|
+
const yep_resolver* resolver_for_schema(YeptrisSchema schema) {
|
|
45
|
+
switch (schema) {
|
|
46
|
+
case YEPTRIS_SCHEMA_11_COMPAT:
|
|
47
|
+
return yep_resolver_compat11();
|
|
48
|
+
case YEPTRIS_SCHEMA_12_JSON:
|
|
49
|
+
return yep_resolver_json12();
|
|
50
|
+
case YEPTRIS_SCHEMA_12_FULL:
|
|
51
|
+
return yep_resolver_full12();
|
|
52
|
+
default:
|
|
53
|
+
return yep_resolver_core12(); /* 12_CORE and the zero default */
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
39
57
|
YEPTRIS_API YeptrisDocument yeptris_parse(const char* buf, size_t len, YeptrisStatus* status) {
|
|
40
58
|
return yeptris_parse_ex(buf, len, NULL, status);
|
|
41
59
|
}
|
|
@@ -63,8 +81,7 @@ yep_dom* yep_doc_dom(yeptris_document* doc) {
|
|
|
63
81
|
yep_ytape* t = (yep_ytape*)doc->lazy_tape;
|
|
64
82
|
yep_dom* dom = yep_dom_create(doc->sys);
|
|
65
83
|
if (dom != NULL) {
|
|
66
|
-
dom->resolver = (doc->schema
|
|
67
|
-
: yep_resolver_core12();
|
|
84
|
+
dom->resolver = resolver_for_schema(doc->schema);
|
|
68
85
|
}
|
|
69
86
|
if (dom != NULL && dom_from_ytape(dom, t) == 0) {
|
|
70
87
|
doc->dom = dom;
|
|
@@ -228,22 +245,22 @@ jfail:
|
|
|
228
245
|
return NULL;
|
|
229
246
|
}
|
|
230
247
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
248
|
+
/* The shared parse front-end (the encoding law, one home): BOM sniff;
|
|
249
|
+
* borrow UTF-8, transcode the rest; printability; NEL normalization.
|
|
250
|
+
* Returns 0 ok, -1 failed (*st carries ENCODING). *transcoded owns any
|
|
251
|
+
* copy (the system allocator frees it); *data and *data_len point at
|
|
252
|
+
* the bytes the engine runs on. json_mode skips the YAML-only passes (the
|
|
253
|
+
* JSON reader validated its own grammar and well-formedness at the
|
|
254
|
+
* entry and keeps U+0085 raw inside strings). */
|
|
255
|
+
static int parse_frontend(const char* buf, size_t len, const yep_allocator* sys, int json_mode,
|
|
256
|
+
const char** data_out, size_t* data_len_out,
|
|
257
|
+
unsigned char** transcoded_out, size_t* transcoded_len_out,
|
|
258
|
+
int* norm_kept_raw_out, YeptrisStatus* st) {
|
|
239
259
|
/* a NULL/0-byte stream is a valid empty input: parse it through a
|
|
240
260
|
* non-null base so the BOM arithmetic never computes NULL+0
|
|
241
261
|
* (clang's UBSan flags the zero-offset-null form; gcc's does not —
|
|
242
262
|
* the schema-econ BadInputs pin surfaced it on the macOS arm64 leg) */
|
|
243
263
|
const char* src = buf != NULL ? buf : "";
|
|
244
|
-
const yep_allocator* sys = yep_system_allocator();
|
|
245
|
-
|
|
246
|
-
/* Encoding front-end: BOM sniff; borrow UTF-8, transcode the rest. */
|
|
247
264
|
const char* data = src;
|
|
248
265
|
size_t data_len = len;
|
|
249
266
|
unsigned char* transcoded = NULL;
|
|
@@ -258,7 +275,12 @@ static YeptrisDocument parse_impl(const char* buf, size_t len, const YeptrisPars
|
|
|
258
275
|
* entry: no BOM, no transcoding, and never the printable set —
|
|
259
276
|
* RFC 8259 allows DEL and noncharacters that YAML c-printable
|
|
260
277
|
* excludes. The engine takes the bytes directly. */
|
|
261
|
-
|
|
278
|
+
*data_out = data;
|
|
279
|
+
*data_len_out = data_len;
|
|
280
|
+
*transcoded_out = NULL;
|
|
281
|
+
*transcoded_len_out = 0;
|
|
282
|
+
*norm_kept_raw_out = 0;
|
|
283
|
+
return 0;
|
|
262
284
|
}
|
|
263
285
|
|
|
264
286
|
size_t bom = yep_bom_sniff((const unsigned char*)src, len, &enc);
|
|
@@ -274,8 +296,8 @@ static YeptrisDocument parse_impl(const char* buf, size_t len, const YeptrisPars
|
|
|
274
296
|
yep_error_set(yep_error_tls(), YEP_ERR_ENCODING, 0, 0, terr,
|
|
275
297
|
"ill-formed input encoding");
|
|
276
298
|
yep_free(sys, transcoded);
|
|
277
|
-
st = YEPTRIS_ERROR_ENCODING;
|
|
278
|
-
|
|
299
|
+
*st = YEPTRIS_ERROR_ENCODING;
|
|
300
|
+
return -1;
|
|
279
301
|
}
|
|
280
302
|
data = (const char*)transcoded;
|
|
281
303
|
data_len = transcoded_len;
|
|
@@ -284,8 +306,8 @@ static YeptrisDocument parse_impl(const char* buf, size_t len, const YeptrisPars
|
|
|
284
306
|
yep_error_set(yep_error_tls(), YEP_ERR_ENCODING, 0, 0, pverr,
|
|
285
307
|
"non-printable character at byte %zu", pverr);
|
|
286
308
|
yep_free(sys, transcoded);
|
|
287
|
-
st = YEPTRIS_ERROR_ENCODING;
|
|
288
|
-
|
|
309
|
+
*st = YEPTRIS_ERROR_ENCODING;
|
|
310
|
+
return -1;
|
|
289
311
|
}
|
|
290
312
|
} else {
|
|
291
313
|
/* The encoding gate rides the cheap gate_scan kernel; sizing
|
|
@@ -299,8 +321,8 @@ static YeptrisDocument parse_impl(const char* buf, size_t len, const YeptrisPars
|
|
|
299
321
|
if (!yep_printable_validate((const unsigned char*)data, data_len, &verr)) {
|
|
300
322
|
yep_error_set(yep_error_tls(), YEP_ERR_ENCODING, 0, 0, verr,
|
|
301
323
|
"ill-formed or non-printable UTF-8 at byte %zu", verr);
|
|
302
|
-
st = YEPTRIS_ERROR_ENCODING;
|
|
303
|
-
|
|
324
|
+
*st = YEPTRIS_ERROR_ENCODING;
|
|
325
|
+
return -1;
|
|
304
326
|
}
|
|
305
327
|
}
|
|
306
328
|
}
|
|
@@ -325,18 +347,131 @@ static YeptrisDocument parse_impl(const char* buf, size_t len, const YeptrisPars
|
|
|
325
347
|
}
|
|
326
348
|
}
|
|
327
349
|
|
|
350
|
+
*data_out = data;
|
|
351
|
+
*data_len_out = data_len;
|
|
352
|
+
*transcoded_out = transcoded;
|
|
353
|
+
*transcoded_len_out = transcoded_len;
|
|
354
|
+
*norm_kept_raw_out = norm_kept_raw;
|
|
355
|
+
return 0;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/* parse.c's sink-entry seam (#902): the DOM route's exact front-end and
|
|
359
|
+
* engine run into a caller sink — no DOM, no tape. Statuses are the
|
|
360
|
+
* parse route's (OK, or PARSE / DEPTH / ENCODING / MEMORY; the sink's
|
|
361
|
+
* own failure rides MEMORY like the DOM builder's). on_input (may be
|
|
362
|
+
* NULL) receives the run's input view — the DOM route's input_base law
|
|
363
|
+
* (the transcoded copy, or the caller's bytes) and its post-BOM length
|
|
364
|
+
* — immediately before the engine runs, because the events' borrowed
|
|
365
|
+
* views subtract from it. The bytes live only for the run. */
|
|
366
|
+
YeptrisStatus yep_parse_events(const char* buf, size_t len, YeptrisSchema schema,
|
|
367
|
+
const yep_sink* sink,
|
|
368
|
+
void (*on_input)(void* ctx, const char* base, size_t len),
|
|
369
|
+
void* on_ctx) {
|
|
370
|
+
if (sink == NULL || (buf == NULL && len != 0)) {
|
|
371
|
+
return YEPTRIS_ERROR_ARG;
|
|
372
|
+
}
|
|
373
|
+
const yep_allocator* sys = yep_system_allocator();
|
|
374
|
+
const char* data = NULL;
|
|
375
|
+
size_t data_len = 0;
|
|
376
|
+
unsigned char* transcoded = NULL;
|
|
377
|
+
size_t transcoded_len = 0;
|
|
378
|
+
int norm_kept_raw = 0;
|
|
379
|
+
YeptrisStatus st = YEPTRIS_OK;
|
|
380
|
+
if (parse_frontend(buf, len, sys, 0, &data, &data_len, &transcoded, &transcoded_len,
|
|
381
|
+
&norm_kept_raw, &st) != 0) {
|
|
382
|
+
return st;
|
|
383
|
+
}
|
|
384
|
+
yep_engine* eng = yep_engine_create(sys);
|
|
385
|
+
if (eng == NULL) {
|
|
386
|
+
yep_free(sys, transcoded);
|
|
387
|
+
return YEPTRIS_ERROR_MEMORY;
|
|
388
|
+
}
|
|
389
|
+
/* the typing SSOT: the family's resolver (resolver_for_schema is
|
|
390
|
+
* the one switch) */
|
|
391
|
+
yep_engine_set_resolver(eng, resolver_for_schema(schema));
|
|
392
|
+
if (schema == YEPTRIS_SCHEMA_11_COMPAT) {
|
|
393
|
+
/* the compat schema is the psych/libyaml surface (see
|
|
394
|
+
* parse_impl's twin comment) */
|
|
395
|
+
yep_engine_set_compat_grammar(eng, 1);
|
|
396
|
+
}
|
|
397
|
+
{
|
|
398
|
+
/* the nametab reserve survives: a memchr chain for '&' is far
|
|
399
|
+
* cheaper than the class sweep it replaced */
|
|
400
|
+
yep_text_stats amp_only = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
401
|
+
const char* q = data == NULL ? "" : data;
|
|
402
|
+
const char* end = q + data_len;
|
|
403
|
+
if (q != NULL) {
|
|
404
|
+
while ((q = memchr(q, '&', (size_t)(end - q))) != NULL) {
|
|
405
|
+
amp_only.amp++;
|
|
406
|
+
q++;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
yep_engine_prepare(eng, &_only);
|
|
410
|
+
}
|
|
411
|
+
yep_engine_front_done(eng);
|
|
412
|
+
if (on_input != NULL) {
|
|
413
|
+
on_input(on_ctx, transcoded ? (const char*)transcoded : buf, data_len);
|
|
414
|
+
}
|
|
415
|
+
int rc = yep_engine_run(eng, data, data_len, sink);
|
|
416
|
+
if (rc != 0) {
|
|
417
|
+
/* the error CODE decides the public status — the parse route's
|
|
418
|
+
* law (parse_impl carries the full comment): a CLEAN channel
|
|
419
|
+
* under a failed run means the SINK failed */
|
|
420
|
+
const yep_error* ee = yep_engine_error(eng);
|
|
421
|
+
yep_err_code code = (ee && ee->code != YEP_ERR_NONE) ? ee->code : YEP_ERR_MEMORY;
|
|
422
|
+
if (ee) {
|
|
423
|
+
yep_error* tls = yep_error_tls();
|
|
424
|
+
*tls = *ee;
|
|
425
|
+
}
|
|
426
|
+
yep_pool* finish = yep_engine_detach_pool(eng);
|
|
427
|
+
yep_pool_destroy(finish);
|
|
428
|
+
yep_engine_destroy(eng);
|
|
429
|
+
yep_free(sys, transcoded);
|
|
430
|
+
return (code == YEP_ERR_MEMORY) ? YEPTRIS_ERROR_MEMORY
|
|
431
|
+
: (code == YEP_ERR_DEPTH) ? YEPTRIS_ERROR_DEPTH
|
|
432
|
+
: YEPTRIS_ERROR_PARSE;
|
|
433
|
+
}
|
|
434
|
+
yep_pool* finish = yep_engine_detach_pool(eng);
|
|
435
|
+
yep_pool_destroy(finish); /* nothing borrows the finish pool: every
|
|
436
|
+
* consumer copies (snapshot law) */
|
|
437
|
+
yep_engine_destroy(eng);
|
|
438
|
+
yep_free(sys, transcoded);
|
|
439
|
+
return YEPTRIS_OK;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
static YeptrisDocument parse_impl(const char* buf, size_t len, const YeptrisParseOptions* opts,
|
|
443
|
+
int json_mode, int lazy, YeptrisStatus* status) {
|
|
444
|
+
YeptrisStatus st = YEPTRIS_OK;
|
|
445
|
+
if (buf == NULL && len != 0) {
|
|
446
|
+
st = YEPTRIS_ERROR_ARG;
|
|
447
|
+
goto fail;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const yep_allocator* sys = yep_system_allocator();
|
|
451
|
+
|
|
452
|
+
/* Encoding front-end (parse_frontend owns the law): BOM sniff;
|
|
453
|
+
* borrow UTF-8, transcode the rest; NEL normalization. */
|
|
454
|
+
const char* data = NULL;
|
|
455
|
+
size_t data_len = 0;
|
|
456
|
+
unsigned char* transcoded = NULL;
|
|
457
|
+
size_t transcoded_len = 0;
|
|
458
|
+
int norm_kept_raw = 0; /* raw NEL/LS/PS survived the quote-aware
|
|
459
|
+
* walk: the engine route owns the document
|
|
460
|
+
* (the tape runner predates the cut) */
|
|
461
|
+
|
|
462
|
+
if (parse_frontend(buf, len, sys, json_mode, &data, &data_len, &transcoded, &transcoded_len,
|
|
463
|
+
&norm_kept_raw, &st) != 0) {
|
|
464
|
+
goto fail;
|
|
465
|
+
}
|
|
466
|
+
|
|
328
467
|
/* Engine → DOM. */
|
|
468
|
+
const YeptrisSchema schema = opts != NULL ? opts->schema : YEPTRIS_SCHEMA_12_CORE;
|
|
329
469
|
yep_engine* eng = NULL;
|
|
330
|
-
engine_enter:
|
|
331
470
|
eng = yep_engine_create(sys);
|
|
332
471
|
if (opts != NULL) {
|
|
333
|
-
/* the typing SSOT: the family's resolver (
|
|
334
|
-
*
|
|
335
|
-
yep_engine_set_resolver(eng,
|
|
336
|
-
? yep_resolver_compat11()
|
|
337
|
-
: (opts->schema == YEPTRIS_SCHEMA_12_JSON
|
|
338
|
-
? yep_resolver_json12()
|
|
339
|
-
: yep_resolver_core12()));
|
|
472
|
+
/* the typing SSOT: the family's resolver (resolver_for_schema
|
|
473
|
+
* is the one switch) */
|
|
474
|
+
yep_engine_set_resolver(eng, resolver_for_schema(schema));
|
|
340
475
|
if (opts->schema == YEPTRIS_SCHEMA_11_COMPAT) {
|
|
341
476
|
/* the compat schema is the psych/libyaml surface: its grammar
|
|
342
477
|
* must accept what libyaml accepts (the flow floor is suite-
|
|
@@ -366,13 +501,7 @@ engine_enter:
|
|
|
366
501
|
}
|
|
367
502
|
/* the direct builders resolve with the engine's schema (the typing
|
|
368
503
|
* SSOT — one resolver decision per scalar, whichever path builds) */
|
|
369
|
-
|
|
370
|
-
dom->resolver = yep_resolver_compat11();
|
|
371
|
-
} else if (opts != NULL && opts->schema == YEPTRIS_SCHEMA_12_JSON) {
|
|
372
|
-
dom->resolver = yep_resolver_json12();
|
|
373
|
-
} else {
|
|
374
|
-
dom->resolver = yep_resolver_core12();
|
|
375
|
-
}
|
|
504
|
+
dom->resolver = resolver_for_schema(schema);
|
|
376
505
|
/* BEFORE the run: input-slice strings borrow as views (zero copy)
|
|
377
506
|
* — the document keeps `transcoded` alive for exactly this. Set
|
|
378
507
|
* after, every scalar was arena-copied (found 2026-09-10). */
|
|
@@ -433,11 +562,7 @@ engine_enter:
|
|
|
433
562
|
yep_engine_destroy(eng);
|
|
434
563
|
eng = NULL;
|
|
435
564
|
} else if (have_tape && !norm_kept_raw &&
|
|
436
|
-
ytap_fused_run(&yt, (
|
|
437
|
-
? yep_resolver_compat11()
|
|
438
|
-
: (opts != NULL && opts->schema == YEPTRIS_SCHEMA_12_JSON)
|
|
439
|
-
? yep_resolver_json12()
|
|
440
|
-
: yep_resolver_core12()) == 0) {
|
|
565
|
+
ytap_fused_run(&yt, resolver_for_schema(schema)) == 0) {
|
|
441
566
|
/* the fused block runner owned the whole document — the engine
|
|
442
567
|
* never runs (TODO.restructure/79). Its only cost was the parse
|
|
443
568
|
* front-end above; on a bail it unwound and the engine runs. */
|
|
@@ -695,10 +820,14 @@ YEPTRIS_API YeptrisTagId yeptris_node_tag_id(YeptrisNode handle) {
|
|
|
695
820
|
if (n == NULL) {
|
|
696
821
|
return YEPTRIS_TAG_CUSTOM;
|
|
697
822
|
}
|
|
698
|
-
|
|
823
|
+
/* the INTERNAL kind space here (#904 item 6): n->kind is the
|
|
824
|
+
* dom's yep_dom_kind — the public YEPTRIS_NODE_* constants ride
|
|
825
|
+
* the same values today only by coincidence, and the boundary
|
|
826
|
+
* conversion is yeptris_node_kind's switch alone */
|
|
827
|
+
if (n->kind == YEP_DOM_SEQUENCE) {
|
|
699
828
|
return YEPTRIS_TAG_SEQ;
|
|
700
829
|
}
|
|
701
|
-
if (n->kind ==
|
|
830
|
+
if (n->kind == YEP_DOM_MAPPING) {
|
|
702
831
|
return YEPTRIS_TAG_MAP;
|
|
703
832
|
}
|
|
704
833
|
return (YeptrisTagId)n->tag_id;
|
|
@@ -847,7 +976,8 @@ YEPTRIS_API size_t yeptris_node_map_count(YeptrisNode handle) {
|
|
|
847
976
|
YEPTRIS_API int yeptris_node_map_at(YeptrisNode handle, size_t index, YeptrisNode* key,
|
|
848
977
|
YeptrisNode* value) {
|
|
849
978
|
const yep_dnode* n = node_of(handle);
|
|
850
|
-
|
|
979
|
+
/* the internal kind space (#904 item 6, see yeptris_node_tag_id) */
|
|
980
|
+
if (n == NULL || n->kind != YEP_DOM_MAPPING || index >= n->count / 2) {
|
|
851
981
|
return -1;
|
|
852
982
|
}
|
|
853
983
|
yeptris_node* h = (yeptris_node*)handle;
|
|
@@ -335,7 +335,7 @@ static yep_tag_id compat11(void* ctx, const char* p, uint32_t n) {
|
|
|
335
335
|
return 0; /* str */
|
|
336
336
|
}
|
|
337
337
|
|
|
338
|
-
static const yep_resolver k_compat11 = {compat11, NULL, NULL};
|
|
338
|
+
static const yep_resolver k_compat11 = {compat11, NULL, NULL, 0};
|
|
339
339
|
|
|
340
340
|
const yep_resolver* yep_resolver_compat11(void) {
|
|
341
341
|
return &k_compat11;
|
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
* and signs take the number walk. The old chain tried the word memcmps
|
|
12
12
|
* for every length-4/5 scalar before ruling (anchor-heavy: 42 cycles
|
|
13
13
|
* per call on gate-passing strings).
|
|
14
|
+
*
|
|
15
|
+
* yep_core12_plain is the ONE classifier (the yep_json12_plain
|
|
16
|
+
* precedent): the resolver wraps it, and the full-1.2 family composes
|
|
17
|
+
* it (full12.c's timestamp lane rides the same truth table).
|
|
14
18
|
*/
|
|
15
19
|
|
|
16
20
|
#include <stdint.h>
|
|
@@ -18,8 +22,7 @@
|
|
|
18
22
|
|
|
19
23
|
#include "resolver.h"
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
(void)ctx;
|
|
25
|
+
yep_tag_id yep_core12_plain(const char* p, uint32_t n) {
|
|
23
26
|
if (n == 0) {
|
|
24
27
|
return 4; /* null */
|
|
25
28
|
}
|
|
@@ -133,12 +136,17 @@ static yep_tag_id core12(void* ctx, const char* p, uint32_t n) {
|
|
|
133
136
|
return 0;
|
|
134
137
|
}
|
|
135
138
|
|
|
139
|
+
static yep_tag_id core12(void* ctx, const char* p, uint32_t n) {
|
|
140
|
+
(void)ctx;
|
|
141
|
+
return yep_core12_plain(p, n);
|
|
142
|
+
}
|
|
143
|
+
|
|
136
144
|
static yep_tag_id core12_number(void* ctx, int is_float) {
|
|
137
145
|
(void)ctx;
|
|
138
146
|
return is_float ? 2 : 1;
|
|
139
147
|
}
|
|
140
148
|
|
|
141
|
-
static const yep_resolver k_core12 = {core12, core12_number, NULL};
|
|
149
|
+
static const yep_resolver k_core12 = {core12, core12_number, NULL, 0};
|
|
142
150
|
|
|
143
151
|
const yep_resolver* yep_resolver_core12(void) {
|
|
144
152
|
return &k_core12;
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/* full12.c — the FULL YAML 1.2 family resolver (#906 slice 3).
|
|
2
|
+
*
|
|
3
|
+
* The spec's schema chapter names two families (10.2 JSON, 10.3 core)
|
|
4
|
+
* and leaves the rest to 10.4 "Other Schemas": interoperable schemas
|
|
5
|
+
* "make use of global tags", may add their own resolution regexes, and
|
|
6
|
+
* are "strongly recommended ... based on the core schema". The
|
|
7
|
+
* historical full schema is the yaml.org type repository carried
|
|
8
|
+
* forward in practice — libyaml implements nothing of it; PyYAML's
|
|
9
|
+
* SafeLoader resolves timestamps, and that is the de-facto full
|
|
10
|
+
* behavior the ecosystem knows. This resolver is that family:
|
|
11
|
+
*
|
|
12
|
+
* core12's productions verbatim (the spec-refereed base — NOT
|
|
13
|
+
* psych's numeric quirks: underscores, sexagesimal, leading-0
|
|
14
|
+
* octal and y/n booleans stay in compat11, which is the 1.1
|
|
15
|
+
* practice lane, not a 1.2 family);
|
|
16
|
+
* + timestamps on untagged plains: spec example 2.22's four shapes —
|
|
17
|
+
* canonical (2001-12-15T02:59:43.1Z), iso8601
|
|
18
|
+
* (2001-12-14t21:59:43.10-05:00), spaced (2001-12-14 21:59:43.10
|
|
19
|
+
* -5), date (2002-12-14) — classified by PyYAML 6.0.2's
|
|
20
|
+
* timestamp regex, the practice oracle (see pyyaml_timestamp
|
|
21
|
+
* below for the production-by-production account);
|
|
22
|
+
* + the merge/value words ('<<' / '='), the 1.1 full schema's
|
|
23
|
+
* grammar-adjacent tags PyYAML also resolves implicitly;
|
|
24
|
+
* - binary: NEVER implicit (nowhere, in any edition) — the explicit
|
|
25
|
+
* !!binary tag rides as consumer-defined passthrough (PyYAML's
|
|
26
|
+
* base64 shaping is a CONSTRUCTION step; keeping shaping out of
|
|
27
|
+
* resolution is the documented passthrough policy).
|
|
28
|
+
*
|
|
29
|
+
* The validator family-checks explicit !!timestamp content through the
|
|
30
|
+
* same classifier (the resolver's `timestamped` capability), so a
|
|
31
|
+
* resolution change lands once and both move together.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
#include <stdint.h>
|
|
35
|
+
|
|
36
|
+
#include "resolver.h"
|
|
37
|
+
|
|
38
|
+
/* PyYAML 6.0.2 resolver.py's timestamp_regexp, production by
|
|
39
|
+
* production (re.X whitespace stripped):
|
|
40
|
+
*
|
|
41
|
+
* [0-9]{4}-[0-9]{2}-[0-9]{2} date-only: month
|
|
42
|
+
* and day are EXACTLY
|
|
43
|
+
* two digits
|
|
44
|
+
* | [0-9]{4}-[0-9]{1,2}-[0-9]{1,2} with a time part:
|
|
45
|
+
* month/day/hour are
|
|
46
|
+
* 1-2 digits, the
|
|
47
|
+
* (?:[Tt]|[ \t]+)[0-9]{1,2} separator T/t/space-
|
|
48
|
+
* run, minutes and
|
|
49
|
+
* :[0-9]{2}:[0-9]{2}(?:\.[0-9]*)? seconds EXACTLY two,
|
|
50
|
+
* fraction zero+ digits
|
|
51
|
+
* (?:[ \t]*(?:Z|[-+][0-9]{1,2}(?::[0-9]{2})?))? tz OPTIONAL: Z or a
|
|
52
|
+
* signed 1-2-digit
|
|
53
|
+
* offset, minutes
|
|
54
|
+
* optional
|
|
55
|
+
*
|
|
56
|
+
* The two-oracle divergences this keeps (PyYAML's resolver is the
|
|
57
|
+
* oracle, deliberately): NO sign on the year (psych allows -?); NO
|
|
58
|
+
* calendar range checks (2001-13-14 resolves timestamp — PyYAML's own
|
|
59
|
+
* resolver agrees, only its constructor raises; resolution is not
|
|
60
|
+
* calendar validation, the consumer's date type owns ranges). The
|
|
61
|
+
* spec's plain-scalar colon law is what keeps the spaced forms
|
|
62
|
+
* unquoted ("a:1" is a plain scalar; "a: 1" is a pair — 5.3.2's note
|
|
63
|
+
* cites timestamps as the canonical case).
|
|
64
|
+
*/
|
|
65
|
+
static int pyyaml_timestamp(const char* p, uint32_t n) {
|
|
66
|
+
uint32_t k = 0;
|
|
67
|
+
for (int d = 0; d < 4; d++) { /* year: exactly four digits, no sign */
|
|
68
|
+
if (k >= n || p[k] < '0' || p[k] > '9') {
|
|
69
|
+
return 0;
|
|
70
|
+
}
|
|
71
|
+
k++;
|
|
72
|
+
}
|
|
73
|
+
if (k >= n || p[k] != '-') {
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
k++;
|
|
77
|
+
uint32_t mdig = 0, ddig = 0;
|
|
78
|
+
for (int seg = 0; seg < 2; seg++) {
|
|
79
|
+
const uint32_t start = k;
|
|
80
|
+
while (k < n && k - start < 2 && p[k] >= '0' && p[k] <= '9') {
|
|
81
|
+
k++;
|
|
82
|
+
}
|
|
83
|
+
if (seg == 0) {
|
|
84
|
+
mdig = k - start;
|
|
85
|
+
if (mdig == 0 || k >= n || p[k] != '-') {
|
|
86
|
+
return 0;
|
|
87
|
+
}
|
|
88
|
+
k++;
|
|
89
|
+
} else {
|
|
90
|
+
ddig = k - start;
|
|
91
|
+
if (ddig == 0) {
|
|
92
|
+
return 0;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (k == n) {
|
|
97
|
+
/* date-only: the branch demands EXACTLY two-digit month/day
|
|
98
|
+
* ("2001-1-1" is a string — PyYAML's own verdict) */
|
|
99
|
+
return mdig == 2 && ddig == 2;
|
|
100
|
+
}
|
|
101
|
+
/* the time part is MANDATORY on the second branch */
|
|
102
|
+
if (p[k] == 'T' || p[k] == 't') {
|
|
103
|
+
k++;
|
|
104
|
+
} else if (p[k] == ' ' || p[k] == '\t') {
|
|
105
|
+
while (k < n && (p[k] == ' ' || p[k] == '\t')) {
|
|
106
|
+
k++;
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
return 0;
|
|
110
|
+
}
|
|
111
|
+
const uint32_t hstart = k; /* hour: 1-2 digits */
|
|
112
|
+
while (k < n && k - hstart < 2 && p[k] >= '0' && p[k] <= '9') {
|
|
113
|
+
k++;
|
|
114
|
+
}
|
|
115
|
+
if (k == hstart) {
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
118
|
+
for (int seg = 0; seg < 2; seg++) { /* :dd:dd — exactly two each */
|
|
119
|
+
if (k >= n || p[k] != ':') {
|
|
120
|
+
return 0;
|
|
121
|
+
}
|
|
122
|
+
k++;
|
|
123
|
+
const uint32_t start = k;
|
|
124
|
+
while (k < n && k - start < 2 && p[k] >= '0' && p[k] <= '9') {
|
|
125
|
+
k++;
|
|
126
|
+
}
|
|
127
|
+
if (k - start != 2) {
|
|
128
|
+
return 0;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (k < n && p[k] == '.') { /* fraction: dot then zero+ digits */
|
|
132
|
+
k++;
|
|
133
|
+
while (k < n && p[k] >= '0' && p[k] <= '9') {
|
|
134
|
+
k++;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/* tz: greedy, OPTIONAL — a malformed tail backtracks to empty and
|
|
138
|
+
* the overall match then fails at the tail byte (regex semantics) */
|
|
139
|
+
const uint32_t save = k;
|
|
140
|
+
while (k < n && (p[k] == ' ' || p[k] == '\t')) {
|
|
141
|
+
k++;
|
|
142
|
+
}
|
|
143
|
+
if (k < n && p[k] == 'Z') {
|
|
144
|
+
k++;
|
|
145
|
+
} else if (k < n && (p[k] == '+' || p[k] == '-')) {
|
|
146
|
+
k++;
|
|
147
|
+
const uint32_t ostart = k; /* offset hour: 1-2 digits */
|
|
148
|
+
while (k < n && k - ostart < 2 && p[k] >= '0' && p[k] <= '9') {
|
|
149
|
+
k++;
|
|
150
|
+
}
|
|
151
|
+
int ok = k - ostart > 0;
|
|
152
|
+
if (ok && k < n && p[k] == ':') { /* optional :mm — exactly two */
|
|
153
|
+
const uint32_t mstart = k + 1;
|
|
154
|
+
ok = mstart + 1 < n && p[mstart] >= '0' && p[mstart] <= '9' && p[mstart + 1] >= '0' &&
|
|
155
|
+
p[mstart + 1] <= '9';
|
|
156
|
+
if (ok) {
|
|
157
|
+
k = mstart + 2;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (!ok) {
|
|
161
|
+
k = save;
|
|
162
|
+
}
|
|
163
|
+
} else {
|
|
164
|
+
k = save;
|
|
165
|
+
}
|
|
166
|
+
return k == n;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
static yep_tag_id full12(void* ctx, const char* p, uint32_t n) {
|
|
170
|
+
(void)ctx;
|
|
171
|
+
/* the merge/value words: exact compares, ahead of everything (the
|
|
172
|
+
* 1.1 full schema's grammar-adjacent tags, PyYAML-resolved) */
|
|
173
|
+
if (n == 1 && p[0] == '=') {
|
|
174
|
+
return 10; /* value */
|
|
175
|
+
}
|
|
176
|
+
if (n == 2 && p[0] == '<' && p[1] == '<') {
|
|
177
|
+
return 9; /* merge */
|
|
178
|
+
}
|
|
179
|
+
/* the core base, verbatim */
|
|
180
|
+
yep_tag_id core = yep_core12_plain(p, n);
|
|
181
|
+
if (core != 0) {
|
|
182
|
+
return core;
|
|
183
|
+
}
|
|
184
|
+
/* timestamps: gate on the shape (four digits, '-', >= 10 bytes) —
|
|
185
|
+
* every accepted form starts with four digits */
|
|
186
|
+
if (n >= 10 && p[0] >= '0' && p[0] <= '9' && pyyaml_timestamp(p, n)) {
|
|
187
|
+
return 5;
|
|
188
|
+
}
|
|
189
|
+
return 0;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
static yep_tag_id full12_number(void* ctx, int is_float) {
|
|
193
|
+
(void)ctx;
|
|
194
|
+
return is_float ? 2 : 1; /* full numbers are core numbers */
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
static const yep_resolver k_full12 = {full12, full12_number, NULL, 1};
|
|
198
|
+
|
|
199
|
+
const yep_resolver* yep_resolver_full12(void) {
|
|
200
|
+
return &k_full12;
|
|
201
|
+
}
|
|
@@ -6,17 +6,23 @@
|
|
|
6
6
|
* bool: true | false
|
|
7
7
|
* int: -? ( 0 | [1-9] [0-9]* )
|
|
8
8
|
* float: -? ( 0 | [1-9] [0-9]* ) ( \. [0-9]* )? ( [eE] [-+]? [0-9]+ )?
|
|
9
|
-
* * ->
|
|
9
|
+
* * -> str (see below: example 10.8 governs; the Error row does not)
|
|
10
10
|
*
|
|
11
11
|
* Deliberately NOT accepted (the core schema's extensions): "True"/
|
|
12
12
|
* "False"/"NULL" casings, "~"/empty nulls, +signs, 0o/0x radix, .inf/
|
|
13
13
|
* .nan, underscores. The example-10.8 Invalid list pins each one.
|
|
14
14
|
*
|
|
15
15
|
* Shape: yep_json12_plain is the ONE classifier — the resolver wraps
|
|
16
|
-
* it, and the validator re-asks it per plain scalar (no second
|
|
17
|
-
* table anywhere).
|
|
18
|
-
*
|
|
19
|
-
*
|
|
16
|
+
* it, and the validator re-asks it per plain scalar key (no second
|
|
17
|
+
* truth table anywhere). SPEC NOTE (the 10.2.2 contradiction): the
|
|
18
|
+
* table's `* -> Error` row says no-match plains "should be considered
|
|
19
|
+
* an error", but example 10.8 — the spec's own JSON Tag Resolution
|
|
20
|
+
* demonstration — resolves no-match plains as strings BOTH as keys
|
|
21
|
+
* ("A null", "Booleans") and as values (the "Invalid" list). Example
|
|
22
|
+
* 10.8 wins: a scalar outside the productions resolves str so the DOM
|
|
23
|
+
* stays family-neutral, and the validation face does NOT turn no-match
|
|
24
|
+
* plains into diagnostics; the family's teeth are the string-key
|
|
25
|
+
* demand (12_JSON) and explicit non-JSON tags.
|
|
20
26
|
*/
|
|
21
27
|
|
|
22
28
|
#include <stdint.h>
|
|
@@ -96,7 +102,7 @@ static yep_tag_id json12_number(void* ctx, int is_float) {
|
|
|
96
102
|
return is_float ? 2 : 1;
|
|
97
103
|
}
|
|
98
104
|
|
|
99
|
-
static const yep_resolver k_json12 = {json12, json12_number, NULL};
|
|
105
|
+
static const yep_resolver k_json12 = {json12, json12_number, NULL, 0};
|
|
100
106
|
|
|
101
107
|
const yep_resolver* yep_resolver_json12(void) {
|
|
102
108
|
return &k_json12;
|
|
@@ -25,6 +25,12 @@ typedef struct yep_resolver {
|
|
|
25
25
|
* skips the digit re-walk resolve() would pay. NULL = resolve(). */
|
|
26
26
|
yep_tag_id (*resolve_number)(void* ctx, int is_float);
|
|
27
27
|
void* ctx;
|
|
28
|
+
/* 1 when timestamp:yaml.org,2002 is IN the family's implicit set —
|
|
29
|
+
* the validator family-checks explicit !!timestamp content through
|
|
30
|
+
* resolve() (#906 slice 3). Core leaves it consumer-defined (the
|
|
31
|
+
* core schema has no timestamp, spec 10.3); 11_COMPAT is not a
|
|
32
|
+
* validation family; json12 rejects the tag wholesale. */
|
|
33
|
+
int timestamped;
|
|
28
34
|
} yep_resolver;
|
|
29
35
|
|
|
30
36
|
/* Typing for a validated number span, through whichever hook the
|
|
@@ -39,6 +45,9 @@ static inline yep_tag_id yep_resolve_number(const yep_resolver* r, int is_float)
|
|
|
39
45
|
/* The two built-in resolvers (no ctx). */
|
|
40
46
|
const yep_resolver* yep_resolver_core12(void);
|
|
41
47
|
const yep_resolver* yep_resolver_compat11(void);
|
|
48
|
+
/* The core schema's plain-scalar classifier (the core truth table):
|
|
49
|
+
* the resolver wraps it and the full family composes it. */
|
|
50
|
+
yep_tag_id yep_core12_plain(const char* p, uint32_t n);
|
|
42
51
|
|
|
43
52
|
/* The spec 10.2 JSON schema resolver (#882): null/true/false exact,
|
|
44
53
|
* the JSON int and float productions, everything else str. Parse stays
|
|
@@ -49,6 +58,17 @@ const yep_resolver* yep_resolver_json12(void);
|
|
|
49
58
|
* truth table for the 12_JSON family (validate/ re-asks it). */
|
|
50
59
|
int yep_json12_plain(const char* p, uint32_t n);
|
|
51
60
|
|
|
61
|
+
/* The FULL 1.2 family resolver (#906 slice 3): the core productions
|
|
62
|
+
* verbatim (spec 10.4: interoperable schemas are "strongly recommended
|
|
63
|
+
* ... based on the core schema") extended with the yaml.org type
|
|
64
|
+
* repository's carried-forward set — RFC3339-era timestamps on untagged
|
|
65
|
+
* plains (spec 2.22's canonical/iso8601/spaced/date forms; PyYAML's
|
|
66
|
+
* timestamp regex as the practice oracle) and the merge/value words.
|
|
67
|
+
* The 1.1 NUMERIC quirks (underscores, sexagesimal, leading-0 octal,
|
|
68
|
+
* y/n booleans) stay in compat11 — full is spec-refereed, not
|
|
69
|
+
* psych-refereed. */
|
|
70
|
+
const yep_resolver* yep_resolver_full12(void);
|
|
71
|
+
|
|
52
72
|
/* core tag id <-> canonical URI (tags.c is the identity SSOT). */
|
|
53
73
|
const char* yep_tag_uri(yep_tag_id id);
|
|
54
74
|
|