yeptris 0.6.52.0-arm-linux-eabihf → 0.6.53.0-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.
@@ -49,7 +49,7 @@ typedef enum {
49
49
 
50
50
  /* One open collection frame (the engine's indentation stack). */
51
51
  typedef struct {
52
- uint16_t col;
52
+ uint32_t col;
53
53
  uint8_t kind;
54
54
  uint8_t inline_doc; /* opened on the --- line: deeper keys only */
55
55
  } yep_frame;
@@ -163,7 +163,7 @@ struct yep_engine {
163
163
  int last_root_flow; /* a root-level flow node just completed */
164
164
  int tag_undef; /* an unresolved !handle! was seen (QLJ7) */
165
165
  int doc_inline; /* content node opened on the --- line */
166
- uint16_t flow_floor; /* parent column of the current block-level flow */
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 —
169
169
  * the flow indent floor (9C9N/VJP3) is a
@@ -176,8 +176,8 @@ struct yep_engine {
176
176
 
177
177
  /* ------------------------------------------------------------ forwards */
178
178
 
179
- static int e_node(yep_engine* e, yep_ctx ctx, uint16_t floor_col);
180
- static int e_parse_value(yep_engine* e, yep_ctx ctx, uint16_t floor_col);
179
+ static int e_node(yep_engine* e, yep_ctx ctx, uint32_t floor_col);
180
+ static int e_parse_value(yep_engine* e, yep_ctx ctx, uint32_t floor_col);
181
181
 
182
182
  /* ---------------------------------------------------------------- util */
183
183
 
@@ -462,7 +462,7 @@ static uint32_t anchor_define(yep_engine* e, yep_view name) {
462
462
  /* Opens a collection frame unless the top frame already continues at
463
463
  * exactly this column (sibling entries reuse it). anchor/tag ride the
464
464
  * START event. Returns 0 on success. */
465
- static int e_open_seq(yep_engine* e, uint16_t col, uint32_t line, uint32_t coln, yep_view anchor,
465
+ static int e_open_seq(yep_engine* e, uint32_t col, uint32_t line, uint32_t coln, yep_view anchor,
466
466
  yep_view tag, uint32_t anchor_id) {
467
467
  if (e->depth > 0 && e->frames[e->depth - 1].kind == YEP_FRAME_SEQ &&
468
468
  e->frames[e->depth - 1].col == col) {
@@ -500,7 +500,7 @@ static int e_open_seq(yep_engine* e, uint16_t col, uint32_t line, uint32_t coln,
500
500
 
501
501
  /* silent: the sink's on_block_open built the node — push the frame
502
502
  * without emitting (TODO.restructure/57). */
503
- static int e_open_map(yep_engine* e, uint16_t col, uint32_t line, uint32_t coln, yep_view anchor,
503
+ static int e_open_map(yep_engine* e, uint32_t col, uint32_t line, uint32_t coln, yep_view anchor,
504
504
  yep_view tag, uint32_t anchor_id, int silent) {
505
505
  if (e->depth > 0 && e->frames[e->depth - 1].kind == YEP_FRAME_MAP &&
506
506
  e->frames[e->depth - 1].col == col) {
@@ -555,7 +555,7 @@ static int e_flush_q_value(yep_engine* e) {
555
555
  * value arrived inline) the innermost '?' pending resolves — without
556
556
  * this pop the stack keeps a stale entry and the outer pair's null
557
557
  * flushes inside the wrong map (the nightly's run 38387614146). */
558
- static int e_pair_value_done(yep_engine* e, yep_ctx ctx, uint16_t key_col) {
558
+ static int e_pair_value_done(yep_engine* e, yep_ctx ctx, uint32_t key_col) {
559
559
  /* the pair's own level, BEFORE its value parse: the value may open
560
560
  * deeper frames ("? a:\n ? b<EOF>") and a post-parse depth read
561
561
  * then matches the DEEPER pair's pending slot, popping the wrong
@@ -604,7 +604,7 @@ static int e_close_to(yep_engine* e, int to_depth) {
604
604
  /* ------------------------------------------------------------- scalars */
605
605
 
606
606
  /* Quoted scalar at the opening quote; spans lines when needed. */
607
- static int e_quoted_floor(yep_engine* e, yep_event* ev, uint16_t min_indent, int enforce) {
607
+ static int e_quoted_floor(yep_engine* e, yep_event* ev, uint32_t min_indent, int enforce) {
608
608
  unsigned char q = (unsigned char)e->p[e->pos];
609
609
  size_t content_start = e->pos + 1;
610
610
  int has_esc = 0;
@@ -885,12 +885,12 @@ static int e_block_scalar(yep_engine* e, yep_event* ev, int parent_col) {
885
885
  if (content_indent <= parent_col) {
886
886
  break; /* no block content */
887
887
  }
888
- } else if (li.indent <= parent_col) {
888
+ } else if ((int)li.indent <= parent_col) {
889
889
  break; /* dedent ends the block */
890
- } else if (li.indent < content_indent && li.indent <= parent_col) {
890
+ } else if ((int)li.indent < content_indent && (int)li.indent <= parent_col) {
891
891
  break;
892
892
  }
893
- if (li.indent < content_indent) {
893
+ if ((int)li.indent < content_indent) {
894
894
  break;
895
895
  }
896
896
  }
@@ -902,10 +902,10 @@ static int e_block_scalar(yep_engine* e, yep_event* ev, int parent_col) {
902
902
  if (content_indent <= parent_col) {
903
903
  break;
904
904
  }
905
- } else if (li.indent < content_indent) {
905
+ } else if ((int)li.indent < content_indent) {
906
906
  break;
907
907
  }
908
- if (li.indent >= content_indent) {
908
+ if ((int)li.indent >= content_indent) {
909
909
  uint32_t from2 = li.offset + (uint32_t)content_indent;
910
910
  if (e->fold_n >= YEP_MAX_FOLD_LINES) {
911
911
  return e_fail(e, YEP_ERR_MEMORY, e->pos);
@@ -945,7 +945,7 @@ static int e_block_scalar(yep_engine* e, yep_event* ev, int parent_col) {
945
945
  }
946
946
  /* a blank line whose spaces extend past the content indent is
947
947
  * CONTENT (kept verbatim); otherwise it is a pure break */
948
- if (content_indent >= 0 && li.indent > content_indent) {
948
+ if (content_indent >= 0 && (int)li.indent > content_indent) {
949
949
  if (e->fold_n >= YEP_MAX_FOLD_LINES) {
950
950
  return e_fail(e, YEP_ERR_MEMORY, e->pos);
951
951
  }
@@ -996,7 +996,7 @@ static int e_block_scalar(yep_engine* e, yep_event* ev, int parent_col) {
996
996
  /* leading blank lines belong to the block content */
997
997
  e->fold[e->fold_n].breaks_before = saw_content ? pending_breaks + 1 : pending_breaks;
998
998
  e->fold[e->fold_n].more_indented =
999
- li.indent > content_indent ||
999
+ (int)li.indent > content_indent ||
1000
1000
  (from < li.end && (e->p[from] == ' ' || e->p[from] == '\t'));
1001
1001
  e->fold_n++;
1002
1002
  saw_content = 1;
@@ -1893,7 +1893,7 @@ static int e_flow_key_after(const char* p, size_t len, size_t close) {
1893
1893
  * rejects it, and the fast path must agree (the nightly's run
1894
1894
  * 38280372447: whole vs chunked split across the fast/slow paths and
1895
1895
  * produced two different verdicts for the same bytes). */
1896
- static int e_flow_floor_ok(const char* p, size_t open, size_t close, uint16_t floor) {
1896
+ static int e_flow_floor_ok(const char* p, size_t open, size_t close, uint32_t floor) {
1897
1897
  size_t j = open;
1898
1898
  while (j < close) {
1899
1899
  /* the break rule is the scan's: \n, or a \r the next byte does
@@ -3036,7 +3036,7 @@ static int e_flow(yep_engine* e, yep_view anchor, yep_view tag, uint32_t anchor_
3036
3036
 
3037
3037
  /* Plain (optionally anchored) value: emit, fold continuations, land at
3038
3038
  * the line end. root_ctx is 0 — every arm has pushed a frame. */
3039
- static int e_shape_plain_value(yep_engine* e, const yep_line_shape* sh, uint16_t floor,
3039
+ static int e_shape_plain_value(yep_engine* e, const yep_line_shape* sh, uint32_t floor,
3040
3040
  yep_view anchor, uint32_t anchor_id) {
3041
3041
  yep_event ev;
3042
3042
  e_event_init(&ev, YEP_EV_SCALAR);
@@ -3078,7 +3078,7 @@ static int e_shape_plain_value(yep_engine* e, const yep_line_shape* sh, uint16_t
3078
3078
 
3079
3079
  /* Flow value: the JSON fast path first; anything else restores the
3080
3080
  * cursor and runs the value chain (which re-derives the flow arms). */
3081
- static int e_shape_flow_value(yep_engine* e, const yep_line_shape* sh, uint16_t floor,
3081
+ static int e_shape_flow_value(yep_engine* e, const yep_line_shape* sh, uint32_t floor,
3082
3082
  yep_ctx next) {
3083
3083
  yep_view none = {NULL, 0};
3084
3084
  e->pos = sh->val_start;
@@ -3119,7 +3119,7 @@ static int e_shape_alias_value(yep_engine* e, const yep_line_shape* sh, int unde
3119
3119
  if (e_simple_key_ok(e, sh->val_start, e->pos) != 0) {
3120
3120
  return -1;
3121
3121
  }
3122
- uint16_t key_col = (uint16_t)e_col(e, sh->val_start);
3122
+ uint32_t key_col = e_col(e, sh->val_start);
3123
3123
  int rc = e_open_map(e, key_col, e->line, key_col + 1, none, none, 0, 0);
3124
3124
  if (rc != 0) {
3125
3125
  return rc;
@@ -3141,7 +3141,7 @@ static int e_shape_alias_value(yep_engine* e, const yep_line_shape* sh, int unde
3141
3141
  * emit it: the pair path takes the whole line without events, and
3142
3142
  * building kv up front discarded ~48 stores per classified line
3143
3143
  * (TODO.restructure/63 phase 1). */
3144
- static void e_key_event(const yep_engine* e, const yep_line_shape* sh, uint16_t key_col,
3144
+ static void e_key_event(const yep_engine* e, const yep_line_shape* sh, uint32_t key_col,
3145
3145
  uint32_t line, yep_event* kv) {
3146
3146
  e_event_init(kv, YEP_EV_SCALAR);
3147
3147
  kv->style = YEP_STYLE_PLAIN;
@@ -3156,7 +3156,7 @@ static void e_key_event(const yep_engine* e, const yep_line_shape* sh, uint16_t
3156
3156
  e_pos_mark(e, sh->key_end, &kv->end_line, &kv->end_col);
3157
3157
  }
3158
3158
 
3159
- static int e_classified(yep_engine* e, uint16_t floor_col) {
3159
+ static int e_classified(yep_engine* e, uint32_t floor_col) {
3160
3160
  yep_line_info li = e_line_info_here(e);
3161
3161
  if (e->pos != e->line_start + li.indent) {
3162
3162
  return 0; /* mid-line entry: the chain keeps its dispatch */
@@ -3177,7 +3177,7 @@ static int e_classified(yep_engine* e, uint16_t floor_col) {
3177
3177
  default:
3178
3178
  return 0;
3179
3179
  }
3180
- uint16_t col = (uint16_t)e_col(e, e->pos);
3180
+ uint32_t col = e_col(e, e->pos);
3181
3181
  int rc = e_open_seq(e, col, e->line, col + 1, none, none, 0);
3182
3182
  if (rc != 0) {
3183
3183
  return rc;
@@ -3203,7 +3203,7 @@ static int e_classified(yep_engine* e, uint16_t floor_col) {
3203
3203
  if (e_simple_key_ok(e, sh->key_start, sh->key_end) != 0) {
3204
3204
  return -1;
3205
3205
  }
3206
- uint16_t key_col = (uint16_t)e_col(e, e->pos);
3206
+ uint32_t key_col = e_col(e, e->pos);
3207
3207
  /* `key:` with the value on following lines, opening a FRESH map:
3208
3208
  * the sink may build map+key in one call (TODO.restructure/57) —
3209
3209
  * the engine then opens its frame silently. Sibling keys (the map
@@ -3241,7 +3241,7 @@ static int e_classified(yep_engine* e, uint16_t floor_col) {
3241
3241
  /* the pair facts are captured BEFORE the fold: e_plain_multiline
3242
3242
  * advances line/pos across continuations */
3243
3243
  uint32_t pair_line = e->line;
3244
- uint16_t pair_val_col = (uint16_t)e_col(e, sh->val_start);
3244
+ uint32_t pair_val_col = e_col(e, sh->val_start);
3245
3245
  yep_block_value v;
3246
3246
  memset(&v, 0, sizeof(v));
3247
3247
  v.cls = (uint8_t)sh->val;
@@ -3375,7 +3375,7 @@ static int e_classified(yep_engine* e, uint16_t floor_col) {
3375
3375
  * pend props (parsed at the parent's value position on an earlier line)
3376
3376
  * belong to the VALUE node as a whole — a collection START event when
3377
3377
  * the value is a collection. */
3378
- static int e_node(yep_engine* e, yep_ctx ctx, uint16_t floor_col) {
3378
+ static int e_node(yep_engine* e, yep_ctx ctx, uint32_t floor_col) {
3379
3379
  size_t node_at = e->pos;
3380
3380
  yep_view pend_a = e->pend_anchor, pend_t = e->pend_tag;
3381
3381
  uint32_t pend_aid = e->pend_anchor_id;
@@ -3482,7 +3482,7 @@ static int e_node(yep_engine* e, yep_ctx ctx, uint16_t floor_col) {
3482
3482
  if (!yep_view_is_empty(anchor) || !yep_view_is_empty(tag)) {
3483
3483
  return e_fail(e, YEP_ERR_UNEXPECTED, e->pos); /* "&a - x" */
3484
3484
  }
3485
- uint16_t col = (uint16_t)e_col(e, e->pos);
3485
+ uint32_t col = e_col(e, e->pos);
3486
3486
  int rc = e_open_seq(e, col, e->line, col + 1, node_a, node_t, node_aid);
3487
3487
  if (rc != 0) {
3488
3488
  return rc;
@@ -3515,7 +3515,7 @@ static int e_node(yep_engine* e, yep_ctx ctx, uint16_t floor_col) {
3515
3515
  if (e->pos > 0 && e->p[e->pos - 1] == '\t') {
3516
3516
  return e_fail(e, YEP_ERR_UNEXPECTED, e->pos); /* "?\t-" (Y79Y) */
3517
3517
  }
3518
- uint16_t col = (uint16_t)e_col(e, e->pos);
3518
+ uint32_t col = e_col(e, e->pos);
3519
3519
  int rc = e_open_map(e, col, e->line, col + 1, pend_a, pend_t, pend_aid, 0);
3520
3520
  if (rc != 0) {
3521
3521
  return rc;
@@ -3639,7 +3639,7 @@ static int e_node(yep_engine* e, yep_ctx ctx, uint16_t floor_col) {
3639
3639
  if (e_simple_key_ok(e, node_at, e->pos) != 0) {
3640
3640
  return -1;
3641
3641
  }
3642
- uint16_t key_col = (uint16_t)e_col(e, node_at);
3642
+ uint32_t key_col = e_col(e, node_at);
3643
3643
  int rc = e_open_map(e, key_col, e->line, key_col + 1, pend_a, pend_t, pend_aid, 0);
3644
3644
  if (rc != 0) {
3645
3645
  return rc;
@@ -3683,7 +3683,7 @@ static int e_node(yep_engine* e, yep_ctx ctx, uint16_t floor_col) {
3683
3683
  if (e_simple_key_ok(e, node_at, e->pos) != 0) {
3684
3684
  return -1;
3685
3685
  }
3686
- uint16_t key_col = (uint16_t)e_col(e, node_at);
3686
+ uint32_t key_col = e_col(e, node_at);
3687
3687
  int rc = e_open_map(e, key_col, e->line, key_col + 1, pend_a, pend_t, pend_aid, 0);
3688
3688
  if (rc != 0) {
3689
3689
  return rc;
@@ -3722,7 +3722,7 @@ static int e_node(yep_engine* e, yep_ctx ctx, uint16_t floor_col) {
3722
3722
  if (e_simple_key_ok(e, node_at, e->pos) != 0) {
3723
3723
  return -1;
3724
3724
  }
3725
- uint16_t key_col = (uint16_t)e_col(e, node_at);
3725
+ uint32_t key_col = e_col(e, node_at);
3726
3726
  int rc = e_open_map(e, key_col, e->line, key_col + 1, pend_a, pend_t, pend_aid, 0);
3727
3727
  if (rc != 0) {
3728
3728
  return rc;
@@ -3777,7 +3777,7 @@ static int e_node(yep_engine* e, yep_ctx ctx, uint16_t floor_col) {
3777
3777
  if (e->doc_inline && (!yep_view_is_empty(anchor) || !yep_view_is_empty(tag))) {
3778
3778
  return e_fail(e, YEP_ERR_UNEXPECTED, e->pos); /* "--- &a k: v" */
3779
3779
  }
3780
- uint16_t key_col = (uint16_t)e_col(e, node_at);
3780
+ uint32_t key_col = e_col(e, node_at);
3781
3781
  int rc = e_open_map(e, key_col, e->line, key_col + 1, pend_a, pend_t, pend_aid, 0);
3782
3782
  if (rc != 0) {
3783
3783
  return rc;
@@ -3837,7 +3837,7 @@ static int e_node(yep_engine* e, yep_ctx ctx, uint16_t floor_col) {
3837
3837
  }
3838
3838
 
3839
3839
  /* Value after "key:" / "-" / "?": on this line, or on following lines. */
3840
- static int e_parse_value(yep_engine* e, yep_ctx ctx, uint16_t floor_col) {
3840
+ static int e_parse_value(yep_engine* e, yep_ctx ctx, uint32_t floor_col) {
3841
3841
  e_skip_inline_space(e);
3842
3842
  if (!e_at_eol(e)) {
3843
3843
  if (ctx == YEP_CTX_AFTER_COLON && e->p[e->pos] == '-' &&
@@ -3849,7 +3849,7 @@ static int e_parse_value(yep_engine* e, yep_ctx ctx, uint16_t floor_col) {
3849
3849
  (e->pos + 1 >= e->len || e->p[e->pos + 1] == ' ' || e->p[e->pos + 1] == '\t' ||
3850
3850
  e->p[e->pos + 1] == '\n' || e->p[e->pos + 1] == '\r')) {
3851
3851
  /* "- :" — a compact pair with an empty key */
3852
- uint16_t col = (uint16_t)e_col(e, e->pos);
3852
+ uint32_t col = e_col(e, e->pos);
3853
3853
  int rc = e_open_map(e, col, e->line, col + 1, e->pend_anchor, e->pend_tag,
3854
3854
  e->pend_anchor_id, 0);
3855
3855
  if (rc != 0) {
@@ -4201,15 +4201,10 @@ yep_engine* yep_engine_create(const yep_allocator* sys) {
4201
4201
  * length is not strlen()'s: the INPUT may carry NUL bytes of its own,
4202
4202
  * and a strlen() of the copy truncates the run at the first one (the
4203
4203
  * nightly's run 38524928004: "\0\n---\r<U+2028>" whole-parsed as an
4204
- * empty stream while the chunked cut skipped the copy). */
4205
- static const char* e_normalize_breaks(yep_engine* e, const char* p, size_t len, size_t* out_len) {
4206
- char* out = yep_engine_normalize_breaks(e->sys, p, len, out_len, NULL);
4207
- if (out == NULL) {
4208
- return p;
4209
- }
4210
- e->norm_buf = out;
4211
- return out;
4212
- }
4204
+ * empty stream while the chunked cut skipped the copy). The context
4205
+ * walk behind the copy lives below (issue #638); since #839 the
4206
+ * chunked route threads it across --- cuts so every document
4207
+ * normalizes to the same bytes the one-buffer walk produces. */
4213
4208
 
4214
4209
  /* The NEL/LS/PS -> '\n' copy (the one-shot path rides this BEFORE the
4215
4210
  * engine so the normalized bytes own the document's `transcoded` slot;
@@ -4258,6 +4253,13 @@ typedef struct yep_norm_ctx {
4258
4253
  int bs_content;
4259
4254
  uint32_t bs_indent; /* the header line's indent; (uint32_t)-1 = the
4260
4255
  * doc-inline root rule (markers only) */
4256
+ uint8_t started; /* at least one byte consumed: the walk's i == 0
4257
+ * clauses are stream-start, not buffer-start —
4258
+ * the chunked route resumes mid-stream at a ---
4259
+ * cut, and the carried context must say so */
4260
+ unsigned char prev; /* the last consumed byte (valid when started):
4261
+ * the '#' and line-start tests peek p[i - 1],
4262
+ * which at a resumed buffer is carried state */
4261
4263
  } yep_norm_ctx;
4262
4264
 
4263
4265
  typedef struct yep_norm_tok {
@@ -4415,8 +4417,12 @@ static yep_norm_tok norm_step(yep_norm_ctx* c, const char* p, size_t len, size_t
4415
4417
  norm_break_arms(c);
4416
4418
  return t;
4417
4419
  }
4418
- if (ch == '#' && (i == 0 || c->at_break || p[i - 1] == '\n' || p[i - 1] == '\r' ||
4419
- p[i - 1] == ' ' || p[i - 1] == '\t')) {
4420
+ /* the i == 0 clauses below are STREAM start: `started`/`prev` carry
4421
+ * them across the chunked route's per-document resumes (issue #839)
4422
+ * — a fresh context at a --- cut would otherwise read the resumed
4423
+ * buffer's first byte as a line start the whole walk never saw */
4424
+ if (ch == '#' && (!c->started || c->at_break || c->prev == '\n' || c->prev == '\r' ||
4425
+ c->prev == ' ' || c->prev == '\t')) {
4420
4426
  c->in_comment = 1;
4421
4427
  c->at_break = 0;
4422
4428
  return t;
@@ -4429,7 +4435,7 @@ static yep_norm_tok norm_step(yep_norm_ctx* c, const char* p, size_t len, size_t
4429
4435
  return t;
4430
4436
  }
4431
4437
 
4432
- int line_start = (i == 0 || c->at_break || p[i - 1] == '\n' || p[i - 1] == '\r');
4438
+ int line_start = (!c->started || c->at_break || c->prev == '\n' || c->prev == '\r');
4433
4439
 
4434
4440
  /* block scalars: inside the fold no quote opens and every break
4435
4441
  * keeps its fold; the region ends at a col-0 marker, a %-line
@@ -4519,7 +4525,7 @@ static yep_norm_tok norm_step(yep_norm_ctx* c, const char* p, size_t len, size_t
4519
4525
  if (!c->bs_open && ch == '-' && i + 2 < len && p[i + 1] == '-' && p[i + 2] == '-' &&
4520
4526
  (i + 3 >= len || p[i + 3] == ' ' || p[i + 3] == '\t' || p[i + 3] == '\n' ||
4521
4527
  p[i + 3] == '\r') &&
4522
- (c->at_break || i == 0)) {
4528
+ (c->at_break || !c->started)) {
4523
4529
  c->in_flow = 0; /* the marker bounds the document even when the
4524
4530
  * walk still believes a flow is open (the same
4525
4531
  * round-35 law as the in-quote rescue) */
@@ -4563,22 +4569,80 @@ static yep_norm_tok norm_step(yep_norm_ctx* c, const char* p, size_t len, size_t
4563
4569
  }
4564
4570
  c->tok_start = 0;
4565
4571
  }
4566
- if (ch == ',' || ch == ':' || ch == '[' || ch == '{' || ch == ']' || ch == '}') {
4567
- c->tok_start = 1; /* flow separators: the next byte may begin a
4568
- * token with no space ("{\"a\":...") */
4569
- }
4570
- if (ch == '[' || ch == '{') {
4572
+ if ((ch == '[' || ch == '{') && (c->tok_start || c->prev == ':')) {
4573
+ /* a flow opener begins a token slot — at a token position, or
4574
+ * directly after the value ':' ("k:{a: 1}" opens; the blank
4575
+ * twin "k: {" rides tok_start already). Unconditioned, a
4576
+ * plain's interior '[' ("ya200%[") opened a PHANTOM flow whose
4577
+ * ',' armed the next token slot and a following "!<" opened a
4578
+ * phantom verbatim — breaks inside verbatim ride unnormalized,
4579
+ * the raw NELs defeated the line-facts kernel, and the plain
4580
+ * fold rendered one fold-SPACE per break instead of the n-1
4581
+ * newline tally (the r98 nightly — the #836 phantom's flow
4582
+ * half; the comma half landed in #836 itself). */
4583
+ c->tok_start = 1;
4571
4584
  c->in_flow++;
4572
- } else if (ch == ']' || ch == '}') {
4585
+ } else if (ch == ',') {
4586
+ /* flow separators glue the next token — THE COMMA IN FLOW
4587
+ * ONLY. The unconditioned arm let a BLOCK plain's interior
4588
+ * comma arm tok_start, a following "!<" opened a phantom
4589
+ * verbatim tag that swallowed the rest of the input (breaks
4590
+ * inside ride unnormalized), and the raw NEL/LS/PS bytes then
4591
+ * defeated the line-facts kernel's \n/\r law — the plain
4592
+ * fold's blank-line tally lost them (the r96 nightly,
4593
+ * yeptris#836, "b,,,!<!<"+NEL^3). ':' stays unconditioned:
4594
+ * the BLOCK value position rides it ("k: 'x'" opens the
4595
+ * quote, the NormLsPs pins). */
4573
4596
  if (c->in_flow > 0) {
4574
- c->in_flow--;
4597
+ c->tok_start = 1;
4598
+ }
4599
+ } else if (ch == ':') {
4600
+ /* the separator law (#833's own predicate): a ':' arms the
4601
+ * next token slot only before a blank/EOL/EOF — "k: 'x'"
4602
+ * opens the value quote (the NormLsPs pins) while an
4603
+ * interior "x:!<" of a block plain does NOT (the phantom
4604
+ * verbatim again, the #836 colon-led twin) */
4605
+ if (i + 1 >= len || p[i + 1] == ' ' || p[i + 1] == '\t' || p[i + 1] == '\n' ||
4606
+ p[i + 1] == '\r') {
4607
+ c->tok_start = 1;
4575
4608
  }
4609
+ } else if (ch == ']' || ch == '}') {
4610
+ c->tok_start = 1;
4576
4611
  }
4577
4612
  return t;
4578
4613
  }
4579
4614
 
4615
+ char* yep_engine_normalize_breaks(const yep_allocator* sys, const char* p, size_t len,
4616
+ size_t* out_len, int* kept_raw);
4617
+
4618
+ static char* norm_breaks_run(const yep_allocator* sys, const char* p, size_t len, size_t* out_len,
4619
+ int* kept_raw, yep_norm_ctx* ctx);
4620
+
4580
4621
  char* yep_engine_normalize_breaks(const yep_allocator* sys, const char* p, size_t len,
4581
4622
  size_t* out_len, int* kept_raw) {
4623
+ return norm_breaks_run(sys, p, len, out_len, kept_raw, NULL);
4624
+ }
4625
+
4626
+ /* The context-threaded core (#839). ctx == NULL is the one-shot form:
4627
+ * fresh state, and the !exotic early exit stands (no walk, no state).
4628
+ * A non-NULL ctx rides in the step state: the chunked route parses
4629
+ * whole documents, so the walk never sees a mid-construct cut — the
4630
+ * divergence was the RESUME state. A --- cut lands exactly where the
4631
+ * whole walk holds some carried state (an open quote it has not been
4632
+ * rescued from, at_break, the raw prev byte); resuming fresh re-lexes
4633
+ * the marker line differently ("--- 'tail": whole's rescue leaves
4634
+ * tok_start 0 so the apostrophe is content, a fresh context's
4635
+ * tok_start 1 opens a quote). Carrying, never resetting: doc N's walk
4636
+ * sees the same bytes with the same state whole holds at the cut, so
4637
+ * doc N + 1's start state equals whole's there — the induction makes
4638
+ * the concatenated documents byte-identical to the one-buffer walk.
4639
+ * The probe ALWAYS runs in this form (even without exotic leads): the
4640
+ * carried state must advance over every byte, or the next resume
4641
+ * starts stale. Both outcomes keep zero-copy semantics: a document
4642
+ * whose exotic bytes all ride raw is used as-is, and those raw bytes
4643
+ * are exactly what the one-buffer walk's copy carries for them. */
4644
+ static char* norm_breaks_run(const yep_allocator* sys, const char* p, size_t len, size_t* out_len,
4645
+ int* kept_raw, yep_norm_ctx* ctx) {
4582
4646
  if (kept_raw != NULL) {
4583
4647
  *kept_raw = 0;
4584
4648
  }
@@ -4595,15 +4659,21 @@ char* yep_engine_normalize_breaks(const yep_allocator* sys, const char* p, size_
4595
4659
  exotic = 1; /* any NEL/LS/PS lead byte needs the context walk */
4596
4660
  break;
4597
4661
  }
4598
- if (!exotic) {
4599
- return NULL;
4600
- }
4601
4662
  yep_norm_ctx probe;
4602
- memset(&probe, 0, sizeof(probe));
4603
- probe.tok_start = 1;
4663
+ if (ctx != NULL) {
4664
+ probe = *ctx;
4665
+ } else {
4666
+ if (!exotic) {
4667
+ return NULL;
4668
+ }
4669
+ memset(&probe, 0, sizeof(probe));
4670
+ probe.tok_start = 1;
4671
+ }
4604
4672
  int found = 0;
4605
4673
  for (size_t j = 0; j < len;) {
4606
4674
  yep_norm_tok t = norm_step(&probe, p, len, j);
4675
+ probe.started = 1;
4676
+ probe.prev = (unsigned char)p[j + t.n - 1];
4607
4677
  if (t.fold_off >= 0) {
4608
4678
  found = 1;
4609
4679
  break;
@@ -4611,22 +4681,34 @@ char* yep_engine_normalize_breaks(const yep_allocator* sys, const char* p, size_
4611
4681
  j += t.n;
4612
4682
  }
4613
4683
  if (!found) {
4614
- if (kept_raw != NULL) {
4615
- *kept_raw = 1; /* every exotic byte rides raw */
4684
+ if (ctx != NULL) {
4685
+ *ctx = probe; /* the carried state advanced even with no fold */
4686
+ } else {
4687
+ if (kept_raw != NULL) {
4688
+ *kept_raw = 1; /* every exotic byte rides raw */
4689
+ }
4690
+ return NULL; /* the exotic bytes all ride inside quoted regions:
4691
+ * the zero-copy path keeps them raw (the fix) */
4616
4692
  }
4617
- return NULL; /* the exotic bytes all ride inside quoted regions:
4618
- * the zero-copy path keeps them raw (the fix) */
4693
+ return NULL;
4619
4694
  }
4620
4695
  char* out = yep_alloc(sys, len + 1);
4621
4696
  if (out == NULL) {
4622
4697
  return NULL;
4623
4698
  }
4624
4699
  yep_norm_ctx cp;
4625
- memset(&cp, 0, sizeof(cp));
4626
- cp.tok_start = 1;
4700
+ if (ctx != NULL) {
4701
+ cp = *ctx; /* restart from the same carried state: the probe's
4702
+ * partial walk (it broke at the fold) is discarded */
4703
+ } else {
4704
+ memset(&cp, 0, sizeof(cp));
4705
+ cp.tok_start = 1;
4706
+ }
4627
4707
  size_t o = 0;
4628
4708
  for (size_t j = 0; j < len;) {
4629
4709
  yep_norm_tok t = norm_step(&cp, p, len, j);
4710
+ cp.started = 1;
4711
+ cp.prev = (unsigned char)p[j + t.n - 1];
4630
4712
  if (t.fold_off < 0) {
4631
4713
  if (kept_raw != NULL && *kept_raw == 0) {
4632
4714
  for (size_t m = 0; m + 1 < t.n; m++) {
@@ -4652,6 +4734,19 @@ char* yep_engine_normalize_breaks(const yep_allocator* sys, const char* p, size_
4652
4734
  if (out_len != NULL) {
4653
4735
  *out_len = o;
4654
4736
  }
4737
+ if (ctx != NULL) {
4738
+ *ctx = cp;
4739
+ }
4740
+ return out;
4741
+ }
4742
+
4743
+ static const char* e_normalize_breaks(yep_engine* e, const char* p, size_t len, size_t* out_len,
4744
+ yep_norm_ctx* ctx) {
4745
+ char* out = norm_breaks_run(e->sys, p, len, out_len, NULL, ctx);
4746
+ if (out == NULL) {
4747
+ return p;
4748
+ }
4749
+ e->norm_buf = out;
4655
4750
  return out;
4656
4751
  }
4657
4752
 
@@ -4678,6 +4773,11 @@ typedef struct yep_stepstate {
4678
4773
  int at_break; /* the previous position was a line break
4679
4774
  * (multibyte breaks leave a non-break byte as
4680
4775
  * p[i-1], so the '#' prev-byte test misses them) */
4776
+ yep_norm_ctx nctx; /* the break normalizer's carried context (#839):
4777
+ * each document's walk resumes with exactly the
4778
+ * state the whole-stream walk holds at the ---
4779
+ * cut, so per-document normalization is
4780
+ * byte-identical to the one-buffer walk */
4681
4781
  } yep_stepstate;
4682
4782
 
4683
4783
  static int engine_run_impl(yep_engine* e, const char* buf, size_t len, const yep_sink* sink,
@@ -4834,6 +4934,19 @@ static size_t step_scan(yep_stepstate* st) {
4834
4934
  st->tok_start = 1;
4835
4935
  st->word_led = 0;
4836
4936
  st->at_break = 1;
4937
+ } else if (c == '\r' && (i + 1 >= st->len || (unsigned char)p[i + 1] != '\n')) {
4938
+ /* a lone CR is a line break too (CRLF rides the \n arm
4939
+ * next iteration; the state is identical either way).
4940
+ * Missing this arm kept the scan inside a comment whose
4941
+ * line ended CR, the tab-led `{ {?` flow opener on the
4942
+ * next line went untracked, and the cut landed at the
4943
+ * following --- INSIDE the engine's open flow (the r97
4944
+ * nightly, yeptris#839: whole errored at the flow's ---
4945
+ * while chunked cut and errored at the cut edge) */
4946
+ st->in_comment = 0;
4947
+ st->tok_start = 1;
4948
+ st->word_led = 0;
4949
+ st->at_break = 1;
4837
4950
  } else if (c == 0xC2 && i + 1 < st->len && (unsigned char)p[i + 1] == 0x85) {
4838
4951
  i++;
4839
4952
  st->in_comment = 0;
@@ -5191,6 +5304,10 @@ int yep_engine_step(yep_engine* e, const char* chunk, size_t len, int final, con
5191
5304
  }
5192
5305
  yep_stepstate* st = e->step;
5193
5306
  st->tok_start = 1; /* the buffer starts at a line start */
5307
+ if (st->nctx.started == 0) {
5308
+ st->nctx.tok_start = 1; /* the stream's first walk: the same
5309
+ * fresh state the one-shot starts */
5310
+ }
5194
5311
  if (len > 0) {
5195
5312
  if (st->len + len > st->cap) {
5196
5313
  size_t cap = st->cap ? st->cap : 4096;
@@ -5256,7 +5373,8 @@ static int engine_run_impl(yep_engine* e, const char* buf, size_t len, const yep
5256
5373
  * it, and the next transcode replaces it. */
5257
5374
  e->parsing_final = emit_end;
5258
5375
  size_t norm_len = len;
5259
- e->p = e_normalize_breaks(e, buf, len, &norm_len);
5376
+ yep_norm_ctx* nctx = e->step != NULL ? &((yep_stepstate*)e->step)->nctx : NULL;
5377
+ e->p = e_normalize_breaks(e, buf, len, &norm_len, nctx);
5260
5378
  e->len = norm_len;
5261
5379
  e->pos = 0;
5262
5380
  e->line = 1 + e->line_base;
@@ -5314,7 +5432,7 @@ static int engine_run_impl(yep_engine* e, const char* buf, size_t len, const yep
5314
5432
  e_fail(e, YEP_ERR_TAB_IN_INDENT, e->pos + li.indent);
5315
5433
  goto fail;
5316
5434
  }
5317
- li.indent = (uint16_t)(t - li.offset);
5435
+ li.indent = (uint32_t)(t - li.offset);
5318
5436
  }
5319
5437
  if (li.flags & YEP_LF_DIRECTIVE) {
5320
5438
  if (doc_open) {
@@ -5634,7 +5752,7 @@ static int engine_run_impl(yep_engine* e, const char* buf, size_t len, const yep
5634
5752
  * dash-blank test is computed once per line (TODO.restructure/75
5635
5753
  * — it was spelled out twice below); the top frame loads once
5636
5754
  * per iteration. */
5637
- uint16_t c = li.indent;
5755
+ uint32_t c = li.indent;
5638
5756
  const int dash_blank =
5639
5757
  li.first == '-' &&
5640
5758
  (li.offset + li.indent + 1 >= e->len || e->p[li.offset + li.indent + 1] == ' ' ||
@@ -90,13 +90,13 @@ typedef struct yep_sink {
90
90
  * placed, 0 = not handled (the engine emits the two events
91
91
  * exactly as before), <0 = abort. */
92
92
  int (*on_block_pair)(void* ctx, const yep_view* key, const struct yep_block_value* v,
93
- uint32_t line, uint16_t key_col, uint16_t val_col);
93
+ uint32_t line, uint32_t key_col, uint32_t val_col);
94
94
  /* Optional block-open fast path (TODO.restructure/57): a `key:`
95
95
  * line whose value lives on FOLLOWING lines, when the engine is
96
96
  * about to open a FRESH mapping. 1 = the sink built the map node
97
97
  * (placed, stack pushed) and the key node — the engine opens its
98
98
  * frame silently and skips both events; 0 = events as before. */
99
- int (*on_block_open)(void* ctx, const yep_view* key, uint32_t line, uint16_t key_col);
99
+ int (*on_block_open)(void* ctx, const yep_view* key, uint32_t line, uint32_t key_col);
100
100
  /* Optional dash-item fast path (TODO.restructure/78): a classified
101
101
  * `- value` plain item after the engine opened (or continued) the
102
102
  * block sequence. 1 = the sink built and placed the item node,
@@ -248,7 +248,8 @@ static yep_tag_id compat11(void* ctx, const char* p, uint32_t n) {
248
248
  if (n - i > 2 && p[i] == '0' && (p[i + 1] == 'b' || p[i + 1] == 'B')) {
249
249
  return is_digits_us(p + i + 2, n - i - 2) ? 1 : 0;
250
250
  }
251
- if (n - i > 2 && p[i] == '0' && (p[i + 1] == 'x' || p[i + 1] == 'X')) {
251
+ if (n - i > 2 && p[i] == '0' &&
252
+ p[i + 1] == 'x') { /* lowercase only — psych keeps "0X1A" a String */
252
253
  uint32_t k = i + 2;
253
254
  if (p[k] == '_') {
254
255
  k++;
@@ -78,7 +78,9 @@ static yep_tag_id core12(void* ctx, const char* p, uint32_t n) {
78
78
  }
79
79
  }
80
80
  }
81
- if (n - i > 2 && p[i] == '0' && (p[i + 1] == 'x' || p[i + 1] == 'X')) {
81
+ if (n - i > 2 && p[i] == '0' &&
82
+ p[i + 1] == 'x') { /* lowercase only: the core schema's int regexp; "0X1A" rides a string
83
+ (psych agrees) */
82
84
  for (uint32_t k = i + 2; k < n; k++) {
83
85
  char c = p[k];
84
86
  if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
@@ -250,6 +250,9 @@ int yep_json_number_scan(const char* p, size_t len, size_t* i, int* is_float, in
250
250
  double* dv) {
251
251
  size_t k = *i;
252
252
  int neg = 0;
253
+ if (k >= len) {
254
+ return 0; /* the caller's index is at the end: p[len] is OOB */
255
+ }
253
256
  if (p[k] == '-') {
254
257
  neg = 1;
255
258
  k++;
@@ -438,6 +441,9 @@ int yep_json_number(const char* p, size_t len, size_t* i) {
438
441
  * the full scan (the 2M tape-diff oracle pins the equivalence). */
439
442
  int yep_json_number_shape(const char* p, size_t len, size_t* i, int* is_float) {
440
443
  size_t k = *i;
444
+ if (k >= len) {
445
+ return 0; /* the caller's index is at the end: p[len] is OOB */
446
+ }
441
447
  if (p[k] == '-') {
442
448
  k++;
443
449
  }