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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/ext/yeptris_native/version.h +2 -2
  3. data/lib/yeptris/3.4/native.so +0 -0
  4. data/lib/yeptris/ffi.rb +19 -0
  5. data/lib/yeptris/yaml.rb +61 -0
  6. data/lib/yeptris.rb +1 -1
  7. data/libyeptris.so +0 -0
  8. data/vendor/libyeptris/CMakeLists.txt +1 -1
  9. data/vendor/libyeptris/src/CMakeLists.txt +7 -0
  10. data/vendor/libyeptris/src/include/yeptris/emit.h +30 -2
  11. data/vendor/libyeptris/src/include/yeptris/parse.h +52 -0
  12. data/vendor/libyeptris/src/include/yeptris/resolve.h +8 -0
  13. data/vendor/libyeptris/src/include/yeptris/validate.h +222 -21
  14. data/vendor/libyeptris/src/include/yeptris/values.h +8 -2
  15. data/vendor/libyeptris/src/yeptris/cbor/cbor.h +10 -0
  16. data/vendor/libyeptris/src/yeptris/cbor/decode.c +5 -7
  17. data/vendor/libyeptris/src/yeptris/cbor/encode.c +9 -9
  18. data/vendor/libyeptris/src/yeptris/cbor/from_marshal.c +1 -1
  19. data/vendor/libyeptris/src/yeptris/common/error.h +2 -0
  20. data/vendor/libyeptris/src/yeptris/doc.h +20 -0
  21. data/vendor/libyeptris/src/yeptris/emit/emit.c +85 -13
  22. data/vendor/libyeptris/src/yeptris/emit/writer.c +158 -642
  23. data/vendor/libyeptris/src/yeptris/emit/writer.h +15 -1
  24. data/vendor/libyeptris/src/yeptris/events/push.c +28 -3
  25. data/vendor/libyeptris/src/yeptris/events/values.c +24 -0
  26. data/vendor/libyeptris/src/yeptris/marshal/to_json.c +650 -0
  27. data/vendor/libyeptris/src/yeptris/parse/engine.c +121 -40
  28. data/vendor/libyeptris/src/yeptris/parse/stream_iter.c +326 -0
  29. data/vendor/libyeptris/src/yeptris/parse.c +173 -43
  30. data/vendor/libyeptris/src/yeptris/resolve/compat11.c +1 -1
  31. data/vendor/libyeptris/src/yeptris/resolve/core12.c +11 -3
  32. data/vendor/libyeptris/src/yeptris/resolve/full12.c +201 -0
  33. data/vendor/libyeptris/src/yeptris/resolve/json12.c +12 -6
  34. data/vendor/libyeptris/src/yeptris/resolve/resolver.h +20 -0
  35. data/vendor/libyeptris/src/yeptris/validate/compile.c +39 -3
  36. data/vendor/libyeptris/src/yeptris/validate/internal.h +223 -7
  37. data/vendor/libyeptris/src/yeptris/validate/jsonschema.c +645 -115
  38. data/vendor/libyeptris/src/yeptris/validate/output.c +246 -0
  39. data/vendor/libyeptris/src/yeptris/validate/regexp.c +623 -157
  40. data/vendor/libyeptris/src/yeptris/validate/registry.c +118 -0
  41. data/vendor/libyeptris/src/yeptris/validate/resources.c +601 -0
  42. data/vendor/libyeptris/src/yeptris/validate/stream.c +1155 -0
  43. data/vendor/libyeptris/src/yeptris/validate/validate.c +288 -145
  44. 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 == YEPTRIS_SCHEMA_11_COMPAT) ? yep_resolver_compat11()
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
- static YeptrisDocument parse_impl(const char* buf, size_t len, const YeptrisParseOptions* opts,
232
- int json_mode, int lazy, YeptrisStatus* status) {
233
- YeptrisStatus st = YEPTRIS_OK;
234
- if (buf == NULL && len != 0) {
235
- st = YEPTRIS_ERROR_ARG;
236
- goto fail;
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
- goto engine_enter;
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
- goto fail;
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
- goto fail;
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
- goto fail;
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, &amp_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 (compat11 when asked,
334
- * json12 for the 12_JSON validation family, core12 otherwise) */
335
- yep_engine_set_resolver(eng, opts->schema == YEPTRIS_SCHEMA_11_COMPAT
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
- if (opts != NULL && opts->schema == YEPTRIS_SCHEMA_11_COMPAT) {
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, (opts != NULL && opts->schema == YEPTRIS_SCHEMA_11_COMPAT)
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
- if (n->kind == YEPTRIS_NODE_SEQUENCE) {
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 == YEPTRIS_NODE_MAPPING) {
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
- if (n == NULL || n->kind != YEPTRIS_NODE_MAPPING || index >= n->count / 2) {
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
- static yep_tag_id core12(void* ctx, const char* p, uint32_t n) {
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
- * * -> Error (the validator's business; the resolver stays str)
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 truth
17
- * table anywhere). Parse never errors here: a scalar outside the
18
- * productions resolves str so the DOM stays family-neutral; the
19
- * validation face turns the spec's Error row into diagnostics.
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