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
|
@@ -51,7 +51,6 @@ typedef enum {
|
|
|
51
51
|
typedef struct {
|
|
52
52
|
uint32_t col;
|
|
53
53
|
uint8_t kind;
|
|
54
|
-
uint8_t inline_doc; /* opened on the --- line: deeper keys only */
|
|
55
54
|
} yep_frame;
|
|
56
55
|
|
|
57
56
|
struct yep_engine {
|
|
@@ -162,7 +161,8 @@ struct yep_engine {
|
|
|
162
161
|
overflow crasher) */
|
|
163
162
|
int last_root_flow; /* a root-level flow node just completed */
|
|
164
163
|
int tag_undef; /* an unresolved !handle! was seen (QLJ7) */
|
|
165
|
-
int doc_inline;
|
|
164
|
+
int doc_inline; /* the node opens on the --- line: block collections
|
|
165
|
+
* refuse there (the marker-line law, #900) */
|
|
166
166
|
uint32_t flow_floor; /* parent column of the current block-level flow */
|
|
167
167
|
int flow_enforce; /* flow continuation lines must out-indent it */
|
|
168
168
|
int compat_grammar; /* 11-COMPAT: libyaml/psych grammar parity —
|
|
@@ -218,6 +218,14 @@ static int e_fail(yep_engine* e, yep_err_code code, size_t at) {
|
|
|
218
218
|
return -1;
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
/* e_fail with an explicit message: the document-boundary diagnostics
|
|
222
|
+
* spell libyaml's exact words (issue #900 — the oracle's message
|
|
223
|
+
* surface, not the generic default-arm string). */
|
|
224
|
+
static int e_fail_msg(yep_engine* e, yep_err_code code, size_t at, const char* msg) {
|
|
225
|
+
yep_error_set(&e->err, code, e->line, e_col(e, at) + 1, at, "%s", msg);
|
|
226
|
+
return -1;
|
|
227
|
+
}
|
|
228
|
+
|
|
221
229
|
/* A simple key (no explicit '?') is limited to one line and 1024
|
|
222
230
|
* characters. The span runs from the node's first byte (opening
|
|
223
231
|
* quote, alias star — not its anchor/tag properties) to the ':'. */
|
|
@@ -237,20 +245,29 @@ static void e_anchor_mark(yep_engine* e, uint32_t id);
|
|
|
237
245
|
|
|
238
246
|
static int emit_now(yep_engine* e, const yep_event* ev) {
|
|
239
247
|
e_anchor_mark(e, ev->anchor_id);
|
|
240
|
-
if (ev->
|
|
241
|
-
/*
|
|
242
|
-
*
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
248
|
+
if (ev->tag.len > 0) {
|
|
249
|
+
/* Explicit-tag resolution (the typing SSOT for SCALARS AND
|
|
250
|
+
* COLLECTIONS): an event's tag_id names the explicit tag's id,
|
|
251
|
+
* so the family walk's consistency check can tell `!!map` on a
|
|
252
|
+
* mapping, `!!seq` on a sequence and a consumer-defined custom
|
|
253
|
+
* tag (CUSTOM, no family verdict) apart — a collection event's
|
|
254
|
+
* id used to stay 0 ("str"), which made EVERY tagged collection
|
|
255
|
+
* read as `!!str` and diagnose a false mismatch (#906 slice 4's
|
|
256
|
+
* tag-driven lane needs the true id). Both routes read this
|
|
257
|
+
* field, so the DOM and event walks stay in lockstep. */
|
|
258
|
+
yep_tag_id tid = yep_tag_from_uri((const char*)ev->tag.p, ev->tag.len);
|
|
259
|
+
/* psych's own dumped form is the LOCAL !binary shorthand,
|
|
260
|
+
* and stdlib to_ruby accepts it beside the core URI (#168)
|
|
261
|
+
* — compat schemas resolve it to BINARY as well */
|
|
262
|
+
if (ev->type == YEP_EV_SCALAR && tid == YEPTRIS_TAG_CUSTOM && ev->tag.len == 7 &&
|
|
263
|
+
memcmp(ev->tag.p, "!binary", 7) == 0 && e->resolver == yep_resolver_compat11()) {
|
|
264
|
+
tid = YEPTRIS_TAG_BINARY;
|
|
265
|
+
}
|
|
266
|
+
((yep_event*)ev)->tag_id = tid;
|
|
267
|
+
} else if (ev->type == YEP_EV_SCALAR) {
|
|
268
|
+
/* implicit typing: scalars only (a collection's id stays 0,
|
|
269
|
+
* its "no tag" value) */
|
|
270
|
+
if (ev->implicit && e->resolver != NULL) {
|
|
254
271
|
((yep_event*)ev)->tag_id = e->resolver->resolve(
|
|
255
272
|
e->resolver->ctx, (const char*)ev->value.p, (uint32_t)ev->value.len);
|
|
256
273
|
} else {
|
|
@@ -468,13 +485,24 @@ static int e_open_seq(yep_engine* e, uint32_t col, uint32_t line, uint32_t coln,
|
|
|
468
485
|
e->frames[e->depth - 1].col == col) {
|
|
469
486
|
return 0;
|
|
470
487
|
}
|
|
488
|
+
if (e->doc_inline) {
|
|
489
|
+
/* the marker-line law (issue #900): a block collection cannot
|
|
490
|
+
* open on the --- line. libyaml's scanner disables block
|
|
491
|
+
* entries after the document-start indicator until a line
|
|
492
|
+
* break; libyaml 0.2.5/90a56d4, Psych 5 and PyYAML (pure and
|
|
493
|
+
* C) all reject ("block sequence entries are not allowed in
|
|
494
|
+
* this context"). The suite has no seq-class pin — its
|
|
495
|
+
* 9KBC/CXX2 fail-pins cover the mapping twin in e_open_map —
|
|
496
|
+
* so the oracle's law governs here. The cursor sits at the
|
|
497
|
+
* dash, which is libyaml's problem mark. */
|
|
498
|
+
return e_fail_msg(e, YEP_ERR_DOC_BOUNDARY, e->pos,
|
|
499
|
+
"block sequence entries are not allowed in this context");
|
|
500
|
+
}
|
|
471
501
|
if (e->depth >= e->max_depth) {
|
|
472
502
|
return e_fail(e, YEP_ERR_DEPTH, e->pos);
|
|
473
503
|
}
|
|
474
504
|
e->frames[e->depth].col = col;
|
|
475
505
|
e->frames[e->depth].kind = YEP_FRAME_SEQ;
|
|
476
|
-
e->frames[e->depth].inline_doc = (uint8_t)e->doc_inline;
|
|
477
|
-
e->doc_inline = 0;
|
|
478
506
|
e->depth++;
|
|
479
507
|
yep_event ev;
|
|
480
508
|
e_event_init(&ev, YEP_EV_SEQ_START);
|
|
@@ -506,13 +534,38 @@ static int e_open_map(yep_engine* e, uint32_t col, uint32_t line, uint32_t coln,
|
|
|
506
534
|
e->frames[e->depth - 1].col == col) {
|
|
507
535
|
return 0;
|
|
508
536
|
}
|
|
537
|
+
if (e->doc_inline) {
|
|
538
|
+
/* the marker-line law (issue #900): a block mapping cannot open
|
|
539
|
+
* on the --- line. libyaml's scanner keeps simple keys disabled
|
|
540
|
+
* after the document-start indicator until a line break, so the
|
|
541
|
+
* pair separator reads as "mapping values are not allowed in
|
|
542
|
+
* this context" (the explicit-key form: "mapping keys ...");
|
|
543
|
+
* libyaml 0.2.5/90a56d4, Psych 5 and PyYAML (pure and C) all
|
|
544
|
+
* reject, and the suite pins the class INVALID (9KBC/CXX2).
|
|
545
|
+
* The mark is the pair separator itself — libyaml's problem
|
|
546
|
+
* mark ("--- a: 1" marks 1:6, the colon). */
|
|
547
|
+
size_t at = e->pos;
|
|
548
|
+
const char* msg;
|
|
549
|
+
if (at < e->len && e->p[at] == '?') {
|
|
550
|
+
msg = "mapping keys are not allowed in this context";
|
|
551
|
+
} else {
|
|
552
|
+
msg = "mapping values are not allowed in this context";
|
|
553
|
+
while (at < e->len && e->p[at] != '\n' && e->p[at] != '\r') {
|
|
554
|
+
if (e->p[at] == ':' &&
|
|
555
|
+
(at + 1 >= e->len || e->p[at + 1] == ' ' || e->p[at + 1] == '\t' ||
|
|
556
|
+
e->p[at + 1] == '\n' || e->p[at + 1] == '\r')) {
|
|
557
|
+
break;
|
|
558
|
+
}
|
|
559
|
+
at++;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
return e_fail_msg(e, YEP_ERR_DOC_BOUNDARY, at, msg);
|
|
563
|
+
}
|
|
509
564
|
if (e->depth >= e->max_depth) {
|
|
510
565
|
return e_fail(e, YEP_ERR_DEPTH, e->pos);
|
|
511
566
|
}
|
|
512
567
|
e->frames[e->depth].col = col;
|
|
513
568
|
e->frames[e->depth].kind = YEP_FRAME_MAP;
|
|
514
|
-
e->frames[e->depth].inline_doc = (uint8_t)e->doc_inline;
|
|
515
|
-
e->doc_inline = 0;
|
|
516
569
|
e->depth++;
|
|
517
570
|
if (silent) {
|
|
518
571
|
return 0;
|
|
@@ -3496,7 +3549,7 @@ static int e_node(yep_engine* e, yep_ctx ctx, uint32_t floor_col) {
|
|
|
3496
3549
|
}
|
|
3497
3550
|
if (c == '-' && (e->pos + 1 >= e->len || e->p[e->pos + 1] == ' ' || e->p[e->pos + 1] == '\t' ||
|
|
3498
3551
|
e->p[e->pos + 1] == '\n' || e->p[e->pos + 1] == '\r')) {
|
|
3499
|
-
if (e->pos > 0 && e->p[e->pos - 1] == '\t') {
|
|
3552
|
+
if (e->pos > 0 && e->p[e->pos - 1] == '\t' && !e->doc_inline) {
|
|
3500
3553
|
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos); /* "-\t-" (Y79Y) */
|
|
3501
3554
|
}
|
|
3502
3555
|
if (!yep_view_is_empty(anchor) || !yep_view_is_empty(tag)) {
|
|
@@ -3532,7 +3585,7 @@ static int e_node(yep_engine* e, yep_ctx ctx, uint32_t floor_col) {
|
|
|
3532
3585
|
* reference (the nightly's run 38355778230). */
|
|
3533
3586
|
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
|
|
3534
3587
|
}
|
|
3535
|
-
if (e->pos > 0 && e->p[e->pos - 1] == '\t') {
|
|
3588
|
+
if (e->pos > 0 && e->p[e->pos - 1] == '\t' && !e->doc_inline) {
|
|
3536
3589
|
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos); /* "?\t-" (Y79Y) */
|
|
3537
3590
|
}
|
|
3538
3591
|
uint32_t col = e_col(e, e->pos);
|
|
@@ -3788,15 +3841,17 @@ static int e_node(yep_engine* e, yep_ctx ctx, uint32_t floor_col) {
|
|
|
3788
3841
|
if (ctx == YEP_CTX_AFTER_COLON) {
|
|
3789
3842
|
return e_fail(e, YEP_ERR_UNEXPECTED, s.end);
|
|
3790
3843
|
}
|
|
3791
|
-
if (node_at > 0 && e->p[node_at - 1] == '\t') {
|
|
3844
|
+
if (node_at > 0 && e->p[node_at - 1] == '\t' && !e->doc_inline) {
|
|
3845
|
+
/* (on the --- line the marker-line gate names the same
|
|
3846
|
+
* refusal with libyaml's words — #900) */
|
|
3792
3847
|
return e_fail(e, YEP_ERR_UNEXPECTED, node_at); /* ":\tkey:" */
|
|
3793
3848
|
}
|
|
3794
3849
|
if (e_simple_key_ok(e, s.start, s.end) != 0) {
|
|
3795
3850
|
return -1;
|
|
3796
3851
|
}
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3852
|
+
/* ("--- &a k: v" rides the e_open_map gate — the marker-line
|
|
3853
|
+
* law has one home now; the propped form used to fail here
|
|
3854
|
+
* with the generic default-arm diagnostic.) */
|
|
3800
3855
|
uint32_t key_col = e_col(e, node_at);
|
|
3801
3856
|
int rc = e_open_map(e, key_col, e->line, key_col + 1, pend_a, pend_t, pend_aid, 0);
|
|
3802
3857
|
if (rc != 0) {
|
|
@@ -4886,8 +4941,8 @@ void yep_engine_destroy(yep_engine* e) {
|
|
|
4886
4941
|
}
|
|
4887
4942
|
if (e->step != NULL) {
|
|
4888
4943
|
yep_stepstate* st = e->step;
|
|
4889
|
-
|
|
4890
|
-
|
|
4944
|
+
yep_free(e->sys, st->buf);
|
|
4945
|
+
yep_free(e->sys, st);
|
|
4891
4946
|
e->step = NULL;
|
|
4892
4947
|
}
|
|
4893
4948
|
yep_nametab_free(&e->anchors);
|
|
@@ -5349,10 +5404,17 @@ int yep_engine_step(yep_engine* e, const char* chunk, size_t len, int final, con
|
|
|
5349
5404
|
return -1;
|
|
5350
5405
|
}
|
|
5351
5406
|
if (e->step == NULL) {
|
|
5352
|
-
|
|
5407
|
+
/* the step state rides the ENGINE's allocator (#904 item 3):
|
|
5408
|
+
* the raw calloc bypassed e->sys, so the fail-injection harness
|
|
5409
|
+
* (test_alloc_fail's counting allocator) could never reach the
|
|
5410
|
+
* streaming route's allocations — and the memory law
|
|
5411
|
+
* (allocator.h: nothing outside memory/ calls malloc directly)
|
|
5412
|
+
* does not stop at the engine's own struct */
|
|
5413
|
+
e->step = yep_alloc(e->sys, sizeof(yep_stepstate));
|
|
5353
5414
|
if (e->step == NULL) {
|
|
5354
5415
|
return -1;
|
|
5355
5416
|
}
|
|
5417
|
+
memset(e->step, 0, sizeof(yep_stepstate));
|
|
5356
5418
|
}
|
|
5357
5419
|
yep_stepstate* st = e->step;
|
|
5358
5420
|
st->tok_start = 1; /* the buffer starts at a line start */
|
|
@@ -5366,10 +5428,17 @@ int yep_engine_step(yep_engine* e, const char* chunk, size_t len, int final, con
|
|
|
5366
5428
|
while (cap < st->len + len) {
|
|
5367
5429
|
cap *= 2;
|
|
5368
5430
|
}
|
|
5369
|
-
|
|
5431
|
+
/* the pending buffer grows through e->sys too: the
|
|
5432
|
+
* allocator has no realloc, so growth is alloc + copy +
|
|
5433
|
+
* free (the arena.c pattern) */
|
|
5434
|
+
char* nb = yep_alloc(e->sys, cap);
|
|
5370
5435
|
if (nb == NULL) {
|
|
5371
5436
|
return -1;
|
|
5372
5437
|
}
|
|
5438
|
+
if (st->buf != NULL) {
|
|
5439
|
+
memcpy(nb, st->buf, st->len);
|
|
5440
|
+
yep_free(e->sys, st->buf);
|
|
5441
|
+
}
|
|
5373
5442
|
st->buf = nb;
|
|
5374
5443
|
st->cap = cap;
|
|
5375
5444
|
}
|
|
@@ -5429,8 +5498,8 @@ int yep_engine_step(yep_engine* e, const char* chunk, size_t len, int final, con
|
|
|
5429
5498
|
/* final: parse everything that remains and close the stream */
|
|
5430
5499
|
e->line_base = st->line_base;
|
|
5431
5500
|
int rc = engine_run_impl(e, st->buf, st->len, sink, !st->started, 1);
|
|
5432
|
-
|
|
5433
|
-
|
|
5501
|
+
yep_free(e->sys, st->buf);
|
|
5502
|
+
yep_free(e->sys, st);
|
|
5434
5503
|
e->step = NULL;
|
|
5435
5504
|
e->line_base = 0;
|
|
5436
5505
|
return rc;
|
|
@@ -5837,6 +5906,9 @@ static int engine_run_impl(yep_engine* e, const char* buf, size_t len, const yep
|
|
|
5837
5906
|
(li.offset + li.indent + 1 >= e->len || e->p[li.offset + li.indent + 1] == ' ' ||
|
|
5838
5907
|
e->p[li.offset + li.indent + 1] == '\t' || e->p[li.offset + li.indent + 1] == '\n' ||
|
|
5839
5908
|
e->p[li.offset + li.indent + 1] == '\r');
|
|
5909
|
+
int map_closed_on_dash = 0; /* the root map closed because this line is a
|
|
5910
|
+
* seq entry — libyaml's parser-state name is
|
|
5911
|
+
* "did not find expected key" */
|
|
5840
5912
|
while (e->depth > 0) {
|
|
5841
5913
|
const yep_frame* fr = &e->frames[e->depth - 1];
|
|
5842
5914
|
if (fr->col > c) {
|
|
@@ -5850,11 +5922,12 @@ static int engine_run_impl(yep_engine* e, const char* buf, size_t len, const yep
|
|
|
5850
5922
|
int continues;
|
|
5851
5923
|
if (fr->kind == YEP_FRAME_SEQ) {
|
|
5852
5924
|
continues = dash_blank;
|
|
5853
|
-
} else if (fr->inline_doc) {
|
|
5854
|
-
continues = 0; /* "--- k: v" maps take deeper keys only */
|
|
5855
5925
|
} else {
|
|
5856
5926
|
continues = yep_scan_is_key_start(li.first) || li.first == ':' ||
|
|
5857
5927
|
li.first == '?' || (li.first == '-' && !dash_blank);
|
|
5928
|
+
if (!continues && dash_blank && e->depth == 1) {
|
|
5929
|
+
map_closed_on_dash = 1;
|
|
5930
|
+
}
|
|
5858
5931
|
}
|
|
5859
5932
|
if (!continues) {
|
|
5860
5933
|
int rc = e_close_to(e, e->depth - 1);
|
|
@@ -5868,10 +5941,19 @@ static int engine_run_impl(yep_engine* e, const char* buf, size_t len, const yep
|
|
|
5868
5941
|
}
|
|
5869
5942
|
|
|
5870
5943
|
if (e->depth == 0 && e->doc_content) {
|
|
5871
|
-
/* a completed root
|
|
5872
|
-
* root
|
|
5873
|
-
|
|
5874
|
-
|
|
5944
|
+
/* a completed root node followed by more content: a second
|
|
5945
|
+
* root in the same document ("word1 # c\nword2",
|
|
5946
|
+
* "---\n[1]\n[2]"). The oracle law is libyaml's parser
|
|
5947
|
+
* error — the stream wants a document marker and finds a
|
|
5948
|
+
* node ("did not find expected <document start>" at the
|
|
5949
|
+
* offending line's first byte; libyaml marks 2:1/3:1). A
|
|
5950
|
+
* seq entry where the just-closed root map wanted a key is
|
|
5951
|
+
* libyaml's "did not find expected key". Issue #900's
|
|
5952
|
+
* inline-map rows never reach this arm: they die at the
|
|
5953
|
+
* marker-line gate in e_open_map/e_open_seq. */
|
|
5954
|
+
return e_fail_msg(e, YEP_ERR_DOC_BOUNDARY, e->pos + li.indent,
|
|
5955
|
+
map_closed_on_dash ? "did not find expected key"
|
|
5956
|
+
: "did not find expected <document start>");
|
|
5875
5957
|
}
|
|
5876
5958
|
if (e->depth > 0 && e->frames[e->depth - 1].col < c) {
|
|
5877
5959
|
/* deeper than any open container at main dispatch: nested
|
|
@@ -5953,8 +6035,7 @@ static int engine_run_impl(yep_engine* e, const char* buf, size_t len, const yep
|
|
|
5953
6035
|
* the chain below unchanged. */
|
|
5954
6036
|
if (e->depth > 0 && e->pend_anchor.p == NULL && e->pend_tag.p == NULL &&
|
|
5955
6037
|
e->pend_anchor_id == 0 && !e->q_key_pending && !e->q_value_pending &&
|
|
5956
|
-
((e->frames[e->depth - 1].kind == YEP_FRAME_MAP && e->frames[e->depth - 1].col == c
|
|
5957
|
-
!e->frames[e->depth - 1].inline_doc) ||
|
|
6038
|
+
((e->frames[e->depth - 1].kind == YEP_FRAME_MAP && e->frames[e->depth - 1].col == c) ||
|
|
5958
6039
|
(e->frames[e->depth - 1].kind == YEP_FRAME_SEQ && e->frames[e->depth - 1].col == c &&
|
|
5959
6040
|
dash_blank))) {
|
|
5960
6041
|
int frc = e_classified(e, e->frames[e->depth - 1].col);
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/* stream_iter.c — tolerant per-document stream iteration (#921).
|
|
2
|
+
*
|
|
3
|
+
* The load_stream contract parses the whole stream and returns nothing
|
|
4
|
+
* when any document fails; consumers that must keep the valid
|
|
5
|
+
* documents of a stream cannot use the engine for recovery. This
|
|
6
|
+
* iterator frames documents ENGINE-SIDE (the abort-at-DOCUMENT_END
|
|
7
|
+
* run iterparse uses — scanner-accurate: a quoted scalar spanning a
|
|
8
|
+
* line that looks like a marker never splits) and reports failures
|
|
9
|
+
* per document: on a parse-class error the iterator records that
|
|
10
|
+
* document's verdict (the engine's line/col, which ARE the failed
|
|
11
|
+
* document's own — each run starts at the frame) and resyncs to the
|
|
12
|
+
* next line-start marker, so the host keeps the valid documents.
|
|
13
|
+
*
|
|
14
|
+
* The resync after a FAILURE is a raw marker scan: the failed
|
|
15
|
+
* document's tail is ill-formed by definition, so the scanner-grade
|
|
16
|
+
* framing above no longer has a well-defined subject; the raw scan is
|
|
17
|
+
* the libyaml-familiar best effort and only ever runs on the recovery
|
|
18
|
+
* path (valid documents never hit it).
|
|
19
|
+
*
|
|
20
|
+
* Cost note: each document parses twice (the boundary run + the slice
|
|
21
|
+
* parse). The tolerance contract is the point; a single-pass upgrade
|
|
22
|
+
* rides on the same sink seam when a workload demands it. */
|
|
23
|
+
|
|
24
|
+
#include <stdlib.h>
|
|
25
|
+
#include <string.h>
|
|
26
|
+
|
|
27
|
+
#include "../../include/yeptris/dom.h"
|
|
28
|
+
#include "../../include/yeptris/parse.h"
|
|
29
|
+
#include "../common/error.h"
|
|
30
|
+
#include "../common/simd_text.h"
|
|
31
|
+
#include "../encoding/encoding.h"
|
|
32
|
+
#include "../memory/allocator.h"
|
|
33
|
+
#include "../resolve/resolver.h"
|
|
34
|
+
#include "doc.h"
|
|
35
|
+
#include "engine.h"
|
|
36
|
+
|
|
37
|
+
/* The boundary finder: a sink that aborts the run at the first
|
|
38
|
+
* DOCUMENT_END (iterparse's law — the marker line stays unconsumed
|
|
39
|
+
* and doubles as the next document's start). Events are discarded;
|
|
40
|
+
* the run's pos IS the document's end offset. */
|
|
41
|
+
typedef struct {
|
|
42
|
+
int doc_ends;
|
|
43
|
+
} frame_sink;
|
|
44
|
+
|
|
45
|
+
static int frame_on_event(void* ctx, const yep_event* ev) {
|
|
46
|
+
frame_sink* s = (frame_sink*)ctx;
|
|
47
|
+
if (ev->type == YEP_EV_DOCUMENT_END) {
|
|
48
|
+
s->doc_ends++;
|
|
49
|
+
return 1; /* abort: the document is complete */
|
|
50
|
+
}
|
|
51
|
+
return 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
struct yeptris_stream_iter {
|
|
55
|
+
const char* buf;
|
|
56
|
+
size_t len;
|
|
57
|
+
size_t cursor;
|
|
58
|
+
int done;
|
|
59
|
+
int gated; /* the encoding front-end ran once (iterparse's
|
|
60
|
+
* law: after it, cursor and engine pos live in
|
|
61
|
+
* the same buffer space) */
|
|
62
|
+
unsigned char* owned; /* UTF-16/32 transcode output */
|
|
63
|
+
char* norm; /* NEL/LS/PS-normalized copy */
|
|
64
|
+
const YeptrisParseOptions* opts; /* borrowed */
|
|
65
|
+
size_t index; /* 0-based stream document index (errors consume
|
|
66
|
+
* one too — the host's yield order is the stream's) */
|
|
67
|
+
YeptrisStatus last_status; /* the LAST next()'s per-document verdict */
|
|
68
|
+
uint32_t last_line;
|
|
69
|
+
uint32_t last_col;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
yeptris_stream_iter* yeptris_stream_iter_new(const char* buf, size_t len,
|
|
73
|
+
const YeptrisParseOptions* opts) {
|
|
74
|
+
if (buf == NULL && len != 0) {
|
|
75
|
+
return NULL;
|
|
76
|
+
}
|
|
77
|
+
struct yeptris_stream_iter* it = calloc(1, sizeof(*it));
|
|
78
|
+
if (it == NULL) {
|
|
79
|
+
return NULL;
|
|
80
|
+
}
|
|
81
|
+
it->buf = buf;
|
|
82
|
+
it->len = len;
|
|
83
|
+
it->opts = opts;
|
|
84
|
+
return it;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* The resync: the next line-start marker (--- / ...) followed by
|
|
88
|
+
* space, break, or EOF — the c-forbidden lookahead. Raw bytes: the
|
|
89
|
+
* recovery path's best effort (see the file comment). The scan starts
|
|
90
|
+
* AFTER the failed frame's first line: the frame opened at `from`'s
|
|
91
|
+
* line, and the engine's failure pos can sit at EOF (the run scans to
|
|
92
|
+
* the end before reporting), so anchoring on `from` itself is the only
|
|
93
|
+
* stable reference. */
|
|
94
|
+
static size_t next_marker(const char* p, size_t len, size_t from) {
|
|
95
|
+
size_t i = from;
|
|
96
|
+
while (i < len && p[i] != '\n' && p[i] != '\r') {
|
|
97
|
+
i++;
|
|
98
|
+
}
|
|
99
|
+
if (i < len) {
|
|
100
|
+
i++;
|
|
101
|
+
}
|
|
102
|
+
int line_start = 1;
|
|
103
|
+
while (i < len) {
|
|
104
|
+
char ch = p[i];
|
|
105
|
+
if (ch == '\n' || ch == '\r') {
|
|
106
|
+
line_start = 1;
|
|
107
|
+
i++;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (line_start && ch == '-' && i + 2 < len && p[i + 1] == '-' && p[i + 2] == '-' &&
|
|
111
|
+
(i + 3 == len || p[i + 3] == ' ' || p[i + 3] == '\n' || p[i + 3] == '\r' ||
|
|
112
|
+
p[i + 3] == '\t')) {
|
|
113
|
+
return i;
|
|
114
|
+
}
|
|
115
|
+
if (line_start && ch == '.' && i + 2 < len && p[i + 1] == '.' && p[i + 2] == '.' &&
|
|
116
|
+
(i + 3 == len || p[i + 3] == ' ' || p[i + 3] == '\n' || p[i + 3] == '\r' ||
|
|
117
|
+
p[i + 3] == '\t')) {
|
|
118
|
+
return i;
|
|
119
|
+
}
|
|
120
|
+
line_start = 0;
|
|
121
|
+
i++;
|
|
122
|
+
}
|
|
123
|
+
return len; /* no marker: the tail is unrecoverable */
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
static void it_fail(struct yeptris_stream_iter* it, YeptrisStatus st, uint32_t line, uint32_t col) {
|
|
127
|
+
it->last_status = st;
|
|
128
|
+
it->last_line = line;
|
|
129
|
+
it->last_col = col;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
static int engine_opts(yep_engine* eng, const YeptrisParseOptions* opts) {
|
|
133
|
+
/* the typing SSOT: one switch (parse.c owns it) */
|
|
134
|
+
YeptrisSchema schema = opts != NULL ? opts->schema : YEPTRIS_SCHEMA_12_CORE;
|
|
135
|
+
yep_engine_set_resolver(eng, resolver_for_schema(schema));
|
|
136
|
+
if (opts != NULL && opts->schema == YEPTRIS_SCHEMA_11_COMPAT) {
|
|
137
|
+
/* the compat grammar is the psych/libyaml surface (parse_impl's
|
|
138
|
+
* law) */
|
|
139
|
+
yep_engine_set_compat_grammar(eng, 1);
|
|
140
|
+
}
|
|
141
|
+
if (opts != NULL && opts->max_depth > 0) {
|
|
142
|
+
yep_engine_set_max_depth(eng, opts->max_depth);
|
|
143
|
+
}
|
|
144
|
+
return 0;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
YeptrisStatus yeptris_stream_iter_next(struct yeptris_stream_iter* it, YeptrisDocument* out,
|
|
148
|
+
size_t* index) {
|
|
149
|
+
if (it == NULL || out == NULL) {
|
|
150
|
+
return YEPTRIS_ERROR_ARG;
|
|
151
|
+
}
|
|
152
|
+
*out = NULL;
|
|
153
|
+
if (index != NULL) {
|
|
154
|
+
*index = it->index;
|
|
155
|
+
}
|
|
156
|
+
for (;;) {
|
|
157
|
+
/* the encoding front-end, ONCE (iterparse's gate law: BOM
|
|
158
|
+
* strip, UTF-16/32 transcode, NEL/LS/PS normalization — cursor
|
|
159
|
+
* and engine pos must live in one buffer space or the resume
|
|
160
|
+
* arithmetic corrupts slices) */
|
|
161
|
+
if (!it->gated) {
|
|
162
|
+
it->gated = 1;
|
|
163
|
+
const yep_allocator* sys = yep_system_allocator();
|
|
164
|
+
yep_encoding enc = YEP_ENC_UNKNOWN;
|
|
165
|
+
size_t bom = yep_bom_sniff((const unsigned char*)it->buf, it->len, &enc);
|
|
166
|
+
if (enc == YEP_ENC_UTF16LE || enc == YEP_ENC_UTF16BE || enc == YEP_ENC_UTF32LE ||
|
|
167
|
+
enc == YEP_ENC_UTF32BE) {
|
|
168
|
+
size_t tlen = 0;
|
|
169
|
+
size_t terr = 0;
|
|
170
|
+
unsigned char* owned = NULL;
|
|
171
|
+
int rc = yep_transcode_to_utf8(sys, enc, (const unsigned char*)it->buf + bom,
|
|
172
|
+
it->len - bom, &owned, &tlen, &terr);
|
|
173
|
+
if (rc != 0) {
|
|
174
|
+
it->done = 1;
|
|
175
|
+
it_fail(it, YEPTRIS_ERROR_ENCODING, 1, 1);
|
|
176
|
+
return YEPTRIS_ERROR_ENCODING;
|
|
177
|
+
}
|
|
178
|
+
it->owned = owned;
|
|
179
|
+
it->buf = (const char*)owned;
|
|
180
|
+
it->len = tlen;
|
|
181
|
+
it->cursor = 0;
|
|
182
|
+
} else {
|
|
183
|
+
it->cursor = bom;
|
|
184
|
+
}
|
|
185
|
+
size_t nlen = 0;
|
|
186
|
+
int kept_raw = 0;
|
|
187
|
+
char* norm = yep_engine_normalize_breaks(sys, it->buf + it->cursor,
|
|
188
|
+
it->len - it->cursor, &nlen, &kept_raw);
|
|
189
|
+
if (norm != NULL) {
|
|
190
|
+
it->norm = norm;
|
|
191
|
+
it->buf = norm;
|
|
192
|
+
it->len = nlen;
|
|
193
|
+
it->cursor = 0;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (it->done) {
|
|
197
|
+
it_fail(it, YEPTRIS_OK, 0, 0);
|
|
198
|
+
return YEPTRIS_OK; /* *out stays NULL: the stream is over */
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/* one boundary run: abort at DOCUMENT_END, learn the extent */
|
|
202
|
+
frame_sink fs = {0};
|
|
203
|
+
yep_engine* eng = yep_engine_create(yep_system_allocator());
|
|
204
|
+
if (eng == NULL) {
|
|
205
|
+
it->done = 1;
|
|
206
|
+
it_fail(it, YEPTRIS_ERROR_MEMORY, 0, 0);
|
|
207
|
+
return YEPTRIS_ERROR_MEMORY;
|
|
208
|
+
}
|
|
209
|
+
engine_opts(eng, it->opts);
|
|
210
|
+
yep_sink sink = {.on_event = frame_on_event,
|
|
211
|
+
.ctx = &fs,
|
|
212
|
+
.on_flow_build = NULL,
|
|
213
|
+
.on_flow_commit = NULL,
|
|
214
|
+
.on_flow_rollback = NULL,
|
|
215
|
+
.on_block_pair = NULL,
|
|
216
|
+
.on_block_open = NULL,
|
|
217
|
+
.on_block_item = NULL};
|
|
218
|
+
yep_text_stats pst;
|
|
219
|
+
yep_text_active()->scan_stats(it->buf + it->cursor, it->len - it->cursor, &pst);
|
|
220
|
+
yep_engine_prepare(eng, &pst);
|
|
221
|
+
int rc = yep_engine_run(eng, it->buf + it->cursor, it->len - it->cursor, &sink);
|
|
222
|
+
size_t pos = yep_engine_pos(eng);
|
|
223
|
+
yep_err_code err_code = YEP_ERR_NONE;
|
|
224
|
+
uint32_t err_line = 0, err_col = 0;
|
|
225
|
+
{
|
|
226
|
+
const yep_error* err = yep_engine_error(eng);
|
|
227
|
+
if (err != NULL) {
|
|
228
|
+
err_code = err->code;
|
|
229
|
+
err_line = err->line;
|
|
230
|
+
err_col = err->col;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
yep_engine_destroy(eng);
|
|
234
|
+
|
|
235
|
+
if (rc == -1 && (err_code == YEP_ERR_MEMORY || err_code == YEP_ERR_ENCODING)) {
|
|
236
|
+
/* the stream-fatal classes: the arena state is unknown
|
|
237
|
+
* (memory) or the front-end's space is broken (encoding —
|
|
238
|
+
* it gated once, so this cannot be a per-document miss) */
|
|
239
|
+
it->done = 1;
|
|
240
|
+
it_fail(it, err_code == YEP_ERR_MEMORY ? YEPTRIS_ERROR_MEMORY : YEPTRIS_ERROR_ENCODING,
|
|
241
|
+
err_line, err_col);
|
|
242
|
+
return it->last_status;
|
|
243
|
+
}
|
|
244
|
+
if (rc == -1 || (rc != 0 && fs.doc_ends == 0)) {
|
|
245
|
+
/* THIS document failed (parse-class or the run died before
|
|
246
|
+
* any document completed): report the per-document verdict
|
|
247
|
+
* — the run started at the frame, so the engine's line/col
|
|
248
|
+
* ARE the failed document's own — then resync to the next
|
|
249
|
+
* marker and keep going */
|
|
250
|
+
it_fail(it, err_code == YEP_ERR_DEPTH ? YEPTRIS_ERROR_DEPTH : YEPTRIS_ERROR_PARSE,
|
|
251
|
+
err_line, err_col);
|
|
252
|
+
size_t at = next_marker(it->buf, it->len, it->cursor);
|
|
253
|
+
if (at >= it->len) {
|
|
254
|
+
it->done = 1; /* the tail is unrecoverable */
|
|
255
|
+
} else {
|
|
256
|
+
it->cursor = at;
|
|
257
|
+
}
|
|
258
|
+
it->index++;
|
|
259
|
+
if (index != NULL) {
|
|
260
|
+
*index = it->index - 1;
|
|
261
|
+
}
|
|
262
|
+
return it->last_status; /* *out stays NULL: the failed slot */
|
|
263
|
+
}
|
|
264
|
+
if (fs.doc_ends == 0) {
|
|
265
|
+
/* natural end or a blank tail: the stream is over */
|
|
266
|
+
it->done = 1;
|
|
267
|
+
it_fail(it, YEPTRIS_OK, 0, 0);
|
|
268
|
+
return YEPTRIS_OK;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/* the frame is [cursor, cursor+pos): parse it as ONE document
|
|
272
|
+
* (the slice's own marker or bare content rides parse_ex's
|
|
273
|
+
* contract; the default tape route is what load uses) */
|
|
274
|
+
YeptrisStatus st = YEPTRIS_OK;
|
|
275
|
+
YeptrisDocument doc = yeptris_parse_ex(it->buf + it->cursor, pos, it->opts, &st);
|
|
276
|
+
if (doc == NULL || st != YEPTRIS_OK) {
|
|
277
|
+
/* the boundary run accepted what the slice parse refused:
|
|
278
|
+
* report as a failed document and resync the same way */
|
|
279
|
+
it_fail(it, st == YEPTRIS_OK ? YEPTRIS_ERROR_PARSE : st, 0, 0);
|
|
280
|
+
yeptris_document_free(doc);
|
|
281
|
+
size_t at = next_marker(it->buf, it->len, it->cursor);
|
|
282
|
+
if (at >= it->len) {
|
|
283
|
+
it->done = 1;
|
|
284
|
+
} else {
|
|
285
|
+
it->cursor = at;
|
|
286
|
+
}
|
|
287
|
+
it->index++;
|
|
288
|
+
if (index != NULL) {
|
|
289
|
+
*index = it->index - 1;
|
|
290
|
+
}
|
|
291
|
+
return it->last_status;
|
|
292
|
+
}
|
|
293
|
+
it->cursor += pos;
|
|
294
|
+
it->index++;
|
|
295
|
+
if (index != NULL) {
|
|
296
|
+
*index = it->index - 1;
|
|
297
|
+
}
|
|
298
|
+
it_fail(it, YEPTRIS_OK, 0, 0);
|
|
299
|
+
*out = doc;
|
|
300
|
+
return YEPTRIS_OK;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
YeptrisStatus yeptris_stream_iter_status(const struct yeptris_stream_iter* it, uint32_t* line,
|
|
305
|
+
uint32_t* col) {
|
|
306
|
+
if (it == NULL) {
|
|
307
|
+
return YEPTRIS_ERROR_ARG;
|
|
308
|
+
}
|
|
309
|
+
if (line != NULL) {
|
|
310
|
+
*line = it->last_line;
|
|
311
|
+
}
|
|
312
|
+
if (col != NULL) {
|
|
313
|
+
*col = it->last_col;
|
|
314
|
+
}
|
|
315
|
+
return it->last_status;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
void yeptris_stream_iter_free(struct yeptris_stream_iter* it) {
|
|
319
|
+
if (it == NULL) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
const yep_allocator* sys = yep_system_allocator();
|
|
323
|
+
yep_free(sys, it->owned);
|
|
324
|
+
yep_free(sys, it->norm);
|
|
325
|
+
free(it);
|
|
326
|
+
}
|