yeptris 0.6.18.2-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 +7 -0
- data/README.adoc +141 -0
- data/ext/build_windows_native.rb +41 -0
- data/ext/libyeptris/extconf.rb +57 -0
- data/ext/yeptris_native/cbor_ruby.c +193 -0
- data/ext/yeptris_native/extconf.rb +47 -0
- data/ext/yeptris_native/json_ruby.c +354 -0
- data/ext/yeptris_native/yeptris_native.c +497 -0
- data/lib/yeptris/cbor.rb +130 -0
- data/lib/yeptris/document.rb +255 -0
- data/lib/yeptris/ffi.rb +426 -0
- data/lib/yeptris/json/descriptor.rb +266 -0
- data/lib/yeptris/json.rb +394 -0
- data/lib/yeptris/materializer.rb +354 -0
- data/lib/yeptris/native-3.4.so +0 -0
- data/lib/yeptris/node.rb +453 -0
- data/lib/yeptris/psych/class_loader.rb +24 -0
- data/lib/yeptris/psych/coder_shim.rb +55 -0
- data/lib/yeptris/psych/drop_in.rb +63 -0
- data/lib/yeptris/psych/encodable.rb +14 -0
- data/lib/yeptris/psych/handler.rb +90 -0
- data/lib/yeptris/psych/parser.rb +105 -0
- data/lib/yeptris/psych/scalar_scanner.rb +149 -0
- data/lib/yeptris/psych/visitors.rb +545 -0
- data/lib/yeptris/psych.rb +622 -0
- data/lib/yeptris/schema.rb +253 -0
- data/lib/yeptris/valueml.rb +455 -0
- data/lib/yeptris/yaml/descriptor.rb +56 -0
- data/lib/yeptris/yaml.rb +422 -0
- data/lib/yeptris.rb +113 -0
- data/libyeptris.so +0 -0
- data/vendor/libyeptris/CMakeLists.txt +254 -0
- data/vendor/libyeptris/cmake/yeptris-config.cmake.in +5 -0
- data/vendor/libyeptris/cmake/yeptris.pc.in +11 -0
- data/vendor/libyeptris/src/CMakeLists.txt +167 -0
- data/vendor/libyeptris/src/include/yeptris/api.h +32 -0
- data/vendor/libyeptris/src/include/yeptris/cbor.h +101 -0
- data/vendor/libyeptris/src/include/yeptris/dom.h +190 -0
- data/vendor/libyeptris/src/include/yeptris/emit.h +81 -0
- data/vendor/libyeptris/src/include/yeptris/error.h +41 -0
- data/vendor/libyeptris/src/include/yeptris/events.h +157 -0
- data/vendor/libyeptris/src/include/yeptris/json.h +45 -0
- data/vendor/libyeptris/src/include/yeptris/json.hpp +258 -0
- data/vendor/libyeptris/src/include/yeptris/jsonc_compat.h +115 -0
- data/vendor/libyeptris/src/include/yeptris/marshal.h +52 -0
- data/vendor/libyeptris/src/include/yeptris/parse.h +48 -0
- data/vendor/libyeptris/src/include/yeptris/plan.h +100 -0
- data/vendor/libyeptris/src/include/yeptris/resolve.h +71 -0
- data/vendor/libyeptris/src/include/yeptris/schema.h +121 -0
- data/vendor/libyeptris/src/include/yeptris/tape.h +123 -0
- data/vendor/libyeptris/src/include/yeptris/types.h +38 -0
- data/vendor/libyeptris/src/include/yeptris/values.h +102 -0
- data/vendor/libyeptris/src/include/yeptris/version.h.in +29 -0
- data/vendor/libyeptris/src/include/yeptris/visit.h +77 -0
- data/vendor/libyeptris/src/include/yeptris/yajl_compat.h +135 -0
- data/vendor/libyeptris/src/include/yeptris.h +23 -0
- data/vendor/libyeptris/src/yeptris/build.c +376 -0
- data/vendor/libyeptris/src/yeptris/cbor/cbor.h +25 -0
- data/vendor/libyeptris/src/yeptris/cbor/decode.c +981 -0
- data/vendor/libyeptris/src/yeptris/cbor/encode.c +835 -0
- data/vendor/libyeptris/src/yeptris/cbor/sink.h +58 -0
- data/vendor/libyeptris/src/yeptris/common/chartype.c +41 -0
- data/vendor/libyeptris/src/yeptris/common/chartype.h +77 -0
- data/vendor/libyeptris/src/yeptris/common/cpu.c +81 -0
- data/vendor/libyeptris/src/yeptris/common/cpu.h +40 -0
- data/vendor/libyeptris/src/yeptris/common/error.c +65 -0
- data/vendor/libyeptris/src/yeptris/common/error.h +79 -0
- data/vendor/libyeptris/src/yeptris/common/mutex.h +45 -0
- data/vendor/libyeptris/src/yeptris/common/nametab.c +275 -0
- data/vendor/libyeptris/src/yeptris/common/nametab.h +68 -0
- data/vendor/libyeptris/src/yeptris/common/port.h +74 -0
- data/vendor/libyeptris/src/yeptris/common/simd_text.c +49 -0
- data/vendor/libyeptris/src/yeptris/common/simd_text.h +295 -0
- data/vendor/libyeptris/src/yeptris/common/simd_text_avx2.c +534 -0
- data/vendor/libyeptris/src/yeptris/common/simd_text_neon.c +662 -0
- data/vendor/libyeptris/src/yeptris/common/simd_text_scalar.c +298 -0
- data/vendor/libyeptris/src/yeptris/common/string_view.c +34 -0
- data/vendor/libyeptris/src/yeptris/common/string_view.h +93 -0
- data/vendor/libyeptris/src/yeptris/doc.h +56 -0
- data/vendor/libyeptris/src/yeptris/dom/dom.c +1192 -0
- data/vendor/libyeptris/src/yeptris/dom/dom.h +312 -0
- data/vendor/libyeptris/src/yeptris/dom/hpool.c +107 -0
- data/vendor/libyeptris/src/yeptris/dom/mapindex.c +173 -0
- data/vendor/libyeptris/src/yeptris/dom/mapindex.h +61 -0
- data/vendor/libyeptris/src/yeptris/dom/mutate.c +431 -0
- data/vendor/libyeptris/src/yeptris/emit/emit.c +434 -0
- data/vendor/libyeptris/src/yeptris/emit/float/api.h +38 -0
- data/vendor/libyeptris/src/yeptris/emit/float/dragon.c +405 -0
- data/vendor/libyeptris/src/yeptris/emit/float/floatint.h +231 -0
- data/vendor/libyeptris/src/yeptris/emit/float/print.c +396 -0
- data/vendor/libyeptris/src/yeptris/emit/style.c +81 -0
- data/vendor/libyeptris/src/yeptris/emit/style.h +24 -0
- data/vendor/libyeptris/src/yeptris/emit/writer.c +816 -0
- data/vendor/libyeptris/src/yeptris/emit/writer.h +76 -0
- data/vendor/libyeptris/src/yeptris/encoding/bom.c +31 -0
- data/vendor/libyeptris/src/yeptris/encoding/encoding.h +63 -0
- data/vendor/libyeptris/src/yeptris/encoding/transcode.c +148 -0
- data/vendor/libyeptris/src/yeptris/encoding/utf8_validate.c +178 -0
- data/vendor/libyeptris/src/yeptris/events/capture.c +150 -0
- data/vendor/libyeptris/src/yeptris/events/capture.h +39 -0
- data/vendor/libyeptris/src/yeptris/events/iterparse.c +208 -0
- data/vendor/libyeptris/src/yeptris/events/pull.c +97 -0
- data/vendor/libyeptris/src/yeptris/events/push.c +104 -0
- data/vendor/libyeptris/src/yeptris/events/recorder.c +107 -0
- data/vendor/libyeptris/src/yeptris/events/values.c +527 -0
- data/vendor/libyeptris/src/yeptris/events/values_priv.h +51 -0
- data/vendor/libyeptris/src/yeptris/events/yaml_compat.c +86 -0
- data/vendor/libyeptris/src/yeptris/events/yaml_compat.h +90 -0
- data/vendor/libyeptris/src/yeptris/jsonapi/jsonc_compat.c +687 -0
- data/vendor/libyeptris/src/yeptris/jsonapi/yajl_compat.c +728 -0
- data/vendor/libyeptris/src/yeptris/marshal.c +782 -0
- data/vendor/libyeptris/src/yeptris/memory/allocator.c +20 -0
- data/vendor/libyeptris/src/yeptris/memory/allocator.h +43 -0
- data/vendor/libyeptris/src/yeptris/memory/arena.c +134 -0
- data/vendor/libyeptris/src/yeptris/memory/arena.h +52 -0
- data/vendor/libyeptris/src/yeptris/memory/pool.c +122 -0
- data/vendor/libyeptris/src/yeptris/memory/pool.h +36 -0
- data/vendor/libyeptris/src/yeptris/parse/engine.c +3698 -0
- data/vendor/libyeptris/src/yeptris/parse/engine.h +65 -0
- data/vendor/libyeptris/src/yeptris/parse/events.h +124 -0
- data/vendor/libyeptris/src/yeptris/parse/numbers.c +349 -0
- data/vendor/libyeptris/src/yeptris/parse/numbers.h +33 -0
- data/vendor/libyeptris/src/yeptris/parse/scalars.c +416 -0
- data/vendor/libyeptris/src/yeptris/parse/scalars.h +75 -0
- data/vendor/libyeptris/src/yeptris/parse.c +737 -0
- data/vendor/libyeptris/src/yeptris/plan.c +824 -0
- data/vendor/libyeptris/src/yeptris/resolve/compat11.c +334 -0
- data/vendor/libyeptris/src/yeptris/resolve/core12.c +143 -0
- data/vendor/libyeptris/src/yeptris/resolve/resolver.h +56 -0
- data/vendor/libyeptris/src/yeptris/resolve/tags.c +58 -0
- data/vendor/libyeptris/src/yeptris/scan/json.c +792 -0
- data/vendor/libyeptris/src/yeptris/scan/json.h +109 -0
- data/vendor/libyeptris/src/yeptris/scan/scan.c +463 -0
- data/vendor/libyeptris/src/yeptris/scan/scan.h +150 -0
- data/vendor/libyeptris/src/yeptris/schema.c +308 -0
- data/vendor/libyeptris/src/yeptris/tape.c +1308 -0
- data/vendor/libyeptris/src/yeptris/tape_in.h +19 -0
- data/vendor/libyeptris/src/yeptris/version.c +7 -0
- data/vendor/libyeptris/src/yeptris/visit/dom_visit.c +115 -0
- data/vendor/libyeptris/src/yeptris/visit/json_visit.c +293 -0
- data/vendor/libyeptris/src/yeptris/visit/yaml_visit.c +127 -0
- metadata +199 -0
|
@@ -0,0 +1,3698 @@
|
|
|
1
|
+
/* engine.c — the block machine + flow kernel (TODO.impl/07).
|
|
2
|
+
*
|
|
3
|
+
* One engine, two kernels: block context is a line/indent loop over an
|
|
4
|
+
* explicit frame stack; '[' or '{' switches to the iterative flow kernel.
|
|
5
|
+
* No recursion anywhere (depth = frame stack depth, guarded).
|
|
6
|
+
*
|
|
7
|
+
* Key invariants:
|
|
8
|
+
* - MAP_START always precedes its key events, including flow keys
|
|
9
|
+
* (a flow node that turns out to be a key is pre-scanned with
|
|
10
|
+
* e_skip_flow before its events are emitted);
|
|
11
|
+
* - a collection frame is pushed exactly once: sibling entries at the
|
|
12
|
+
* frame's column reuse the open frame;
|
|
13
|
+
* - anchors defined before use (engine tracks names; the DOM binds nodes).
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
#include <stdio.h>
|
|
17
|
+
#include <stdlib.h>
|
|
18
|
+
#include <string.h>
|
|
19
|
+
|
|
20
|
+
#include "common/chartype.h"
|
|
21
|
+
#include "common/nametab.h"
|
|
22
|
+
#include "common/simd_text.h"
|
|
23
|
+
#include "engine.h"
|
|
24
|
+
#include "memory/allocator.h"
|
|
25
|
+
#include "memory/pool.h"
|
|
26
|
+
#include "parse/scalars.h"
|
|
27
|
+
#include "resolve/resolver.h"
|
|
28
|
+
#include "scan/json.h"
|
|
29
|
+
#include "scan/scan.h"
|
|
30
|
+
#include <yeptris/resolve.h> /* YEPTRIS_TAG_* names (#168) */
|
|
31
|
+
|
|
32
|
+
#define YEP_MAX_DEPTH 1000
|
|
33
|
+
#define YEP_MAX_FOLD_LINES 8192
|
|
34
|
+
#define YEP_MAX_SIMPLE_KEY \
|
|
35
|
+
1024 /* YAML 1.2: simple keys are one line, \
|
|
36
|
+
* at most 1024 characters (libyaml parity) */
|
|
37
|
+
|
|
38
|
+
typedef enum { YEP_FRAME_SEQ = 0, YEP_FRAME_MAP } yep_frame_kind;
|
|
39
|
+
|
|
40
|
+
typedef enum {
|
|
41
|
+
YEP_CTX_FRESH = 0, /* fresh line entry: may open collections */
|
|
42
|
+
YEP_CTX_AFTER_DASH, /* value of a seq entry: may open collections (compact) */
|
|
43
|
+
YEP_CTX_AFTER_COLON, /* value of a map key: collections via following lines only */
|
|
44
|
+
YEP_CTX_AFTER_Q, /* explicit-key content: may ALIGN with the '?' column */
|
|
45
|
+
YEP_CTX_VALUE_LINE, /* value of "key:" on a FOLLOWING line: indentless
|
|
46
|
+
* sequences allowed, same-line prohibitions lifted */
|
|
47
|
+
} yep_ctx;
|
|
48
|
+
|
|
49
|
+
/* One open collection frame (the engine's indentation stack). */
|
|
50
|
+
typedef struct {
|
|
51
|
+
uint16_t col;
|
|
52
|
+
uint8_t kind;
|
|
53
|
+
uint8_t inline_doc; /* opened on the --- line: deeper keys only */
|
|
54
|
+
} yep_frame;
|
|
55
|
+
|
|
56
|
+
struct yep_engine {
|
|
57
|
+
const yep_allocator* sys;
|
|
58
|
+
yep_pool* pool; /* finish pool: folded/escaped scalar content */
|
|
59
|
+
const char* p;
|
|
60
|
+
size_t len;
|
|
61
|
+
size_t pos;
|
|
62
|
+
uint32_t line; /* 1-based current line */
|
|
63
|
+
size_t line_start; /* offset of the current line start */
|
|
64
|
+
yep_line_info li_cache; /* per-line scan_line memo: every site
|
|
65
|
+
positioned at line_start reads it (plain continuation checks,
|
|
66
|
+
literal blocks, the main loop), so a line is scanned once */
|
|
67
|
+
uint32_t li_cache_line; /* 0 = no entry (line numbers are 1-based) */
|
|
68
|
+
yep_line_shape shape_cache; /* per-line shape memo (TODO.restructure/49):
|
|
69
|
+
the dispatch decision is made once */
|
|
70
|
+
uint32_t shape_cache_line; /* 0 = no entry */
|
|
71
|
+
uint32_t line_base; /* lines consumed by earlier stepped runs */
|
|
72
|
+
yep_error err;
|
|
73
|
+
const yep_sink* sink;
|
|
74
|
+
|
|
75
|
+
yep_frame frames[YEP_MAX_DEPTH];
|
|
76
|
+
int depth;
|
|
77
|
+
|
|
78
|
+
yep_nametab anchors; /* name interning: O(1) define/lookup at any scale */
|
|
79
|
+
uint32_t anchor_serial; /* 1-based ordinals; the value IS the id */
|
|
80
|
+
/* repeat-alias memo (TODO.restructure/71): offset/len/ordinal of
|
|
81
|
+
* the last alias resolved — hit by memcmp, not by the table */
|
|
82
|
+
uint32_t alias_memo_at;
|
|
83
|
+
uint32_t alias_memo_len;
|
|
84
|
+
uint32_t alias_memo_ord;
|
|
85
|
+
/* the line-facts memo (TODO.restructure/76): one kernel pass per
|
|
86
|
+
* line feeds both the line info and the shape */
|
|
87
|
+
yep_line_facts facts_cache;
|
|
88
|
+
uint32_t facts_cache_line;
|
|
89
|
+
|
|
90
|
+
yep_fold_line fold[YEP_MAX_FOLD_LINES];
|
|
91
|
+
size_t fold_n;
|
|
92
|
+
|
|
93
|
+
/* properties that belong to a value parsed on following lines */
|
|
94
|
+
yep_view pend_anchor, pend_tag;
|
|
95
|
+
uint32_t pend_anchor_id; /* the pend anchor's ordinal */
|
|
96
|
+
|
|
97
|
+
int doc_content; /* any node emitted for the current document */
|
|
98
|
+
int q_key_pending; /* an explicit '?' key awaits its ':' value line */
|
|
99
|
+
int q_map_depth; /* frame depth of the '?' mapping (flush guard) */
|
|
100
|
+
int q_value_pending; /* an explicit key was emitted; its value is null
|
|
101
|
+
until a ':' line supplies one */
|
|
102
|
+
/* %TAG handle map for the current document (engine = tag SSOT) */
|
|
103
|
+
struct {
|
|
104
|
+
yep_view handle, prefix;
|
|
105
|
+
} tagmap[8];
|
|
106
|
+
int tagmap_n;
|
|
107
|
+
int saw_yaml; /* %YAML seen for the pending document */
|
|
108
|
+
void* step; /* yep_stepstate: resumable stepping (07) */
|
|
109
|
+
|
|
110
|
+
/* Flow single-pair deferral: a sequence entry's events are buffered
|
|
111
|
+
* until we know whether ':' follows ("[a: b]" needs MAP_START before
|
|
112
|
+
* the key's events, but ':' is only seen after them). */
|
|
113
|
+
yep_event* ev_buf;
|
|
114
|
+
uint32_t ev_buf_n, ev_buf_cap;
|
|
115
|
+
uint32_t ev_scopes; /* active entry-buffer scopes */
|
|
116
|
+
char* norm_buf; /* owned input copy with exotic breaks normalized */
|
|
117
|
+
const struct yep_resolver* resolver; /* implicit-typing schema (10) */
|
|
118
|
+
int max_depth; /* runtime nesting limit (options) */
|
|
119
|
+
int last_root_flow; /* a root-level flow node just completed */
|
|
120
|
+
int tag_undef; /* an unresolved !handle! was seen (QLJ7) */
|
|
121
|
+
int doc_inline; /* content node opened on the --- line */
|
|
122
|
+
uint16_t flow_floor; /* parent column of the current block-level flow */
|
|
123
|
+
int flow_enforce; /* flow continuation lines must out-indent it */
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/* ------------------------------------------------------------ forwards */
|
|
127
|
+
|
|
128
|
+
static int e_node(yep_engine* e, yep_ctx ctx, uint16_t floor_col);
|
|
129
|
+
static int e_parse_value(yep_engine* e, yep_ctx ctx, uint16_t floor_col);
|
|
130
|
+
|
|
131
|
+
/* ---------------------------------------------------------------- util */
|
|
132
|
+
|
|
133
|
+
static uint32_t e_col(const yep_engine* e, size_t at) {
|
|
134
|
+
return at >= e->line_start ? (uint32_t)(at - e->line_start) : 0;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
static int e_fail(yep_engine* e, yep_err_code code, size_t at) {
|
|
138
|
+
yep_error_set(&e->err, code, e->line, e_col(e, at) + 1, at, NULL);
|
|
139
|
+
return -1;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* A simple key (no explicit '?') is limited to one line and 1024
|
|
143
|
+
* characters. The span runs from the node's first byte (opening
|
|
144
|
+
* quote, alias star — not its anchor/tag properties) to the ':'. */
|
|
145
|
+
static int e_simple_key_ok(yep_engine* e, size_t key_at, size_t colon_at) {
|
|
146
|
+
if (colon_at - key_at > YEP_MAX_SIMPLE_KEY) {
|
|
147
|
+
return e_fail(e, YEP_ERR_KEY_TOO_LONG, key_at);
|
|
148
|
+
}
|
|
149
|
+
return 0;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
static void e_event_init(yep_event* ev, yep_event_type t) {
|
|
153
|
+
memset(ev, 0, sizeof(*ev));
|
|
154
|
+
ev->type = t;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
static int emit_now(yep_engine* e, const yep_event* ev) {
|
|
158
|
+
if (ev->type == YEP_EV_SCALAR) {
|
|
159
|
+
/* Resolution happens exactly here: every scalar path flows
|
|
160
|
+
* through this choke point (the resolver is the typing SSOT) */
|
|
161
|
+
if (ev->tag.len > 0) {
|
|
162
|
+
yep_tag_id tid = yep_tag_from_uri((const char*)ev->tag.p, ev->tag.len);
|
|
163
|
+
/* psych's own dumped form is the LOCAL !binary shorthand,
|
|
164
|
+
* and stdlib to_ruby accepts it beside the core URI (#168)
|
|
165
|
+
* — compat schemas resolve it to BINARY as well */
|
|
166
|
+
if (tid == YEPTRIS_TAG_CUSTOM && ev->tag.len == 7 &&
|
|
167
|
+
memcmp(ev->tag.p, "!binary", 7) == 0 && e->resolver == yep_resolver_compat11()) {
|
|
168
|
+
tid = YEPTRIS_TAG_BINARY;
|
|
169
|
+
}
|
|
170
|
+
((yep_event*)ev)->tag_id = tid;
|
|
171
|
+
} else if (ev->implicit && e->resolver != NULL) {
|
|
172
|
+
((yep_event*)ev)->tag_id = e->resolver->resolve(
|
|
173
|
+
e->resolver->ctx, (const char*)ev->value.p, (uint32_t)ev->value.len);
|
|
174
|
+
} else {
|
|
175
|
+
((yep_event*)ev)->tag_id = 0; /* str: quoted or tag-free */
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (ev->type == YEP_EV_SCALAR && e->ev_scopes == 0 && e->sink != NULL &&
|
|
179
|
+
e->sink->on_scalar != NULL) {
|
|
180
|
+
return e->sink->on_scalar(e->sink->ctx, &ev->value, &ev->tag, &ev->anchor, ev->anchor_id,
|
|
181
|
+
ev->tag_id, ev->style, ev->implicit, ev->flow, ev->borrowed);
|
|
182
|
+
}
|
|
183
|
+
if (e->ev_scopes > 0) {
|
|
184
|
+
if (e->ev_buf_n == e->ev_buf_cap) {
|
|
185
|
+
uint32_t cap = e->ev_buf_cap ? e->ev_buf_cap * 2 : 16;
|
|
186
|
+
yep_event* nb = yep_pool_alloc(e->pool, (size_t)cap * sizeof(yep_event), 16);
|
|
187
|
+
if (nb == NULL) {
|
|
188
|
+
e_fail(e, YEP_ERR_MEMORY, e->pos);
|
|
189
|
+
return -2;
|
|
190
|
+
}
|
|
191
|
+
if (e->ev_buf_n > 0) {
|
|
192
|
+
memcpy(nb, e->ev_buf, (size_t)e->ev_buf_n * sizeof(yep_event));
|
|
193
|
+
}
|
|
194
|
+
e->ev_buf = nb;
|
|
195
|
+
e->ev_buf_cap = cap;
|
|
196
|
+
}
|
|
197
|
+
e->ev_buf[e->ev_buf_n++] = *ev;
|
|
198
|
+
return 0;
|
|
199
|
+
}
|
|
200
|
+
return e->sink ? e->sink->on_event(e->sink->ctx, ev) : 0;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* Emits buffered events from `from` to the sink and truncates. */
|
|
204
|
+
static int e_buf_send(yep_engine* e, uint32_t from) {
|
|
205
|
+
for (uint32_t i = from; i < e->ev_buf_n; i++) {
|
|
206
|
+
if (e->sink && e->sink->on_event(e->sink->ctx, &e->ev_buf[i]) != 0) {
|
|
207
|
+
return -2;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
e->ev_buf_n = from;
|
|
211
|
+
return 0;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/* Inserts a flow MAP_START at `from`, wrapping the buffered entry. */
|
|
215
|
+
static int e_buf_wrap(yep_engine* e, uint32_t from) {
|
|
216
|
+
if (e->ev_buf_n >= e->ev_buf_cap) {
|
|
217
|
+
uint32_t cap = e->ev_buf_cap ? e->ev_buf_cap * 2 : 16;
|
|
218
|
+
yep_event* nb = yep_pool_alloc(e->pool, (size_t)cap * sizeof(yep_event), 16);
|
|
219
|
+
if (nb == NULL) {
|
|
220
|
+
e_fail(e, YEP_ERR_MEMORY, e->pos);
|
|
221
|
+
return -2;
|
|
222
|
+
}
|
|
223
|
+
if (e->ev_buf_n > 0) {
|
|
224
|
+
memcpy(nb, e->ev_buf, (size_t)e->ev_buf_n * sizeof(yep_event));
|
|
225
|
+
}
|
|
226
|
+
e->ev_buf = nb;
|
|
227
|
+
e->ev_buf_cap = cap;
|
|
228
|
+
}
|
|
229
|
+
memmove(&e->ev_buf[from + 1], &e->ev_buf[from],
|
|
230
|
+
(size_t)(e->ev_buf_n - from) * sizeof(yep_event));
|
|
231
|
+
e_event_init(&e->ev_buf[from], YEP_EV_MAP_START);
|
|
232
|
+
e->ev_buf[from].flow = 1;
|
|
233
|
+
e->ev_buf_n++;
|
|
234
|
+
return 0;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
static void e_line_done(yep_engine* e, size_t at) {
|
|
238
|
+
size_t br = yep_scan_break_len(e->p, e->len, at);
|
|
239
|
+
if (br > 0) {
|
|
240
|
+
e->pos = at + br;
|
|
241
|
+
e->line++;
|
|
242
|
+
e->line_start = e->pos;
|
|
243
|
+
e->li_cache_line = 0;
|
|
244
|
+
e->shape_cache_line = 0;
|
|
245
|
+
e->facts_cache_line = 0;
|
|
246
|
+
} else {
|
|
247
|
+
e->pos = at;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/* The line-facts memo (TODO.restructure/76): ONE kernel pass per
|
|
252
|
+
* line; li and the shape both derive from it. */
|
|
253
|
+
static const yep_line_facts* e_facts_here(yep_engine* e) {
|
|
254
|
+
if (e->facts_cache_line != e->line) {
|
|
255
|
+
yep_scan_facts(e->p, e->len, e->line_start, &e->facts_cache);
|
|
256
|
+
e->facts_cache_line = e->line;
|
|
257
|
+
e->li_cache_line = 0; /* derived caches are stale now */
|
|
258
|
+
e->shape_cache_line = 0;
|
|
259
|
+
}
|
|
260
|
+
return &e->facts_cache;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/* scan_line for the current line, memoized for the flow loop (the
|
|
264
|
+
* loop head and the post-ws check each need it every line). */
|
|
265
|
+
static yep_line_info e_line_info_here(yep_engine* e) {
|
|
266
|
+
const yep_line_facts* f = e_facts_here(e);
|
|
267
|
+
if (e->li_cache_line != e->line) {
|
|
268
|
+
yep_scan_line_f(e->p, e->len, e->line_start, f, &e->li_cache);
|
|
269
|
+
e->li_cache_line = e->line;
|
|
270
|
+
}
|
|
271
|
+
return e->li_cache;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/* The line's classified shape, memoized beside the line facts: the
|
|
275
|
+
* engine decides dispatch ONCE per line (TODO.restructure/49). */
|
|
276
|
+
static const yep_line_shape* e_shape_here(yep_engine* e) {
|
|
277
|
+
const yep_line_facts* f = e_facts_here(e);
|
|
278
|
+
e_line_info_here(e);
|
|
279
|
+
if (e->shape_cache_line != e->line) {
|
|
280
|
+
yep_scan_shape_f(e->p, e->len, &e->li_cache, f, &e->shape_cache);
|
|
281
|
+
e->shape_cache_line = e->line;
|
|
282
|
+
}
|
|
283
|
+
return &e->shape_cache;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
static void e_skip_inline_space(yep_engine* e) {
|
|
287
|
+
while (e->pos < e->len && (e->p[e->pos] == ' ' || e->p[e->pos] == '\t')) {
|
|
288
|
+
e->pos++;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/* '#' only opens a comment when a blank (or nothing) precedes it. */
|
|
293
|
+
static int e_comment_at(const yep_engine* e) {
|
|
294
|
+
return e->p[e->pos] == '#' &&
|
|
295
|
+
(e->pos == 0 || e->p[e->pos - 1] == ' ' || e->p[e->pos - 1] == '\t' ||
|
|
296
|
+
e->p[e->pos - 1] == '\n' || e->p[e->pos - 1] == '\r');
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
static int e_at_eol(yep_engine* e) {
|
|
300
|
+
return e->pos >= e->len || e->p[e->pos] == '\n' || e->p[e->pos] == '\r' || e_comment_at(e);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
static int e_skip_to_eol(yep_engine* e) {
|
|
304
|
+
while (e->pos < e->len && e->p[e->pos] != '\n' && e->p[e->pos] != '\r') {
|
|
305
|
+
e->pos++;
|
|
306
|
+
}
|
|
307
|
+
return 0;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/* ':' terminates a node here (blank/EOL after; flow indicators are
|
|
311
|
+
* handled by the flow kernel's own check). */
|
|
312
|
+
static int e_colon_at(yep_engine* e, size_t at) {
|
|
313
|
+
if (at >= e->len || e->p[at] != ':') {
|
|
314
|
+
return 0;
|
|
315
|
+
}
|
|
316
|
+
size_t n = at + 1;
|
|
317
|
+
return n >= e->len || e->p[n] == ' ' || e->p[n] == '\n' || e->p[n] == '\r';
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/* -------------------------------------------------------------- anchors */
|
|
321
|
+
|
|
322
|
+
/* The ordinal the nametab assigned at define time (0 when absent). */
|
|
323
|
+
static uint32_t anchor_id_of(yep_engine* e, yep_view name) {
|
|
324
|
+
/* Repeat-alias memo (TODO.restructure/71): merge-key YAML re-uses
|
|
325
|
+
* the same alias thousands of times in a row; the memo answers
|
|
326
|
+
* those with one input compare, skipping the hash + a likely
|
|
327
|
+
* cache-missing probe into the (multi-MB, reserved) slots table. */
|
|
328
|
+
if (e->alias_memo_len == name.len && e->alias_memo_len != 0 &&
|
|
329
|
+
memcmp(e->p + e->alias_memo_at, name.p, name.len) == 0) {
|
|
330
|
+
return e->alias_memo_ord;
|
|
331
|
+
}
|
|
332
|
+
uint32_t id = yep_nametab_get(&e->anchors, name);
|
|
333
|
+
if (id == YEP_NAMETAB_NIL) {
|
|
334
|
+
return 0;
|
|
335
|
+
}
|
|
336
|
+
e->alias_memo_at = (uint32_t)(name.p - e->p);
|
|
337
|
+
e->alias_memo_len = name.len;
|
|
338
|
+
e->alias_memo_ord = id;
|
|
339
|
+
return id;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
static uint32_t anchor_define(yep_engine* e, yep_view name) {
|
|
343
|
+
/* Last definition wins (libyaml parity); OOM leaves the anchor
|
|
344
|
+
* undefined so its alias errors. The VALUE is the anchor's 1-based
|
|
345
|
+
* ordinal — sinks key on it and skip the name hash entirely. */
|
|
346
|
+
uint32_t id = ++e->anchor_serial;
|
|
347
|
+
yep_nametab_set(&e->anchors, name, id);
|
|
348
|
+
return id;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/* --------------------------------------------------------------- frames */
|
|
352
|
+
|
|
353
|
+
/* Opens a collection frame unless the top frame already continues at
|
|
354
|
+
* exactly this column (sibling entries reuse it). anchor/tag ride the
|
|
355
|
+
* START event. Returns 0 on success. */
|
|
356
|
+
static int e_open_seq(yep_engine* e, uint16_t col, uint32_t line, uint32_t coln, yep_view anchor,
|
|
357
|
+
yep_view tag, uint32_t anchor_id) {
|
|
358
|
+
if (e->depth > 0 && e->frames[e->depth - 1].kind == YEP_FRAME_SEQ &&
|
|
359
|
+
e->frames[e->depth - 1].col == col) {
|
|
360
|
+
return 0;
|
|
361
|
+
}
|
|
362
|
+
if (e->depth >= e->max_depth) {
|
|
363
|
+
return e_fail(e, YEP_ERR_DEPTH, e->pos);
|
|
364
|
+
}
|
|
365
|
+
e->frames[e->depth].col = col;
|
|
366
|
+
e->frames[e->depth].kind = YEP_FRAME_SEQ;
|
|
367
|
+
e->frames[e->depth].inline_doc = (uint8_t)e->doc_inline;
|
|
368
|
+
e->doc_inline = 0;
|
|
369
|
+
e->depth++;
|
|
370
|
+
yep_event ev;
|
|
371
|
+
e_event_init(&ev, YEP_EV_SEQ_START);
|
|
372
|
+
ev.anchor = anchor;
|
|
373
|
+
ev.anchor_id = anchor_id;
|
|
374
|
+
ev.tag = tag;
|
|
375
|
+
ev.line = line;
|
|
376
|
+
ev.col = coln;
|
|
377
|
+
return emit_now(e, &ev) == 0 ? 0 : -2;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/* silent: the sink's on_block_open built the node — push the frame
|
|
381
|
+
* without emitting (TODO.restructure/57). */
|
|
382
|
+
static int e_open_map(yep_engine* e, uint16_t col, uint32_t line, uint32_t coln, yep_view anchor,
|
|
383
|
+
yep_view tag, uint32_t anchor_id, int silent) {
|
|
384
|
+
if (e->depth > 0 && e->frames[e->depth - 1].kind == YEP_FRAME_MAP &&
|
|
385
|
+
e->frames[e->depth - 1].col == col) {
|
|
386
|
+
return 0;
|
|
387
|
+
}
|
|
388
|
+
if (e->depth >= e->max_depth) {
|
|
389
|
+
return e_fail(e, YEP_ERR_DEPTH, e->pos);
|
|
390
|
+
}
|
|
391
|
+
e->frames[e->depth].col = col;
|
|
392
|
+
e->frames[e->depth].kind = YEP_FRAME_MAP;
|
|
393
|
+
e->frames[e->depth].inline_doc = (uint8_t)e->doc_inline;
|
|
394
|
+
e->doc_inline = 0;
|
|
395
|
+
e->depth++;
|
|
396
|
+
if (silent) {
|
|
397
|
+
return 0;
|
|
398
|
+
}
|
|
399
|
+
yep_event ev;
|
|
400
|
+
e_event_init(&ev, YEP_EV_MAP_START);
|
|
401
|
+
ev.anchor = anchor;
|
|
402
|
+
ev.anchor_id = anchor_id;
|
|
403
|
+
ev.tag = tag;
|
|
404
|
+
ev.line = line;
|
|
405
|
+
ev.col = coln;
|
|
406
|
+
return emit_now(e, &ev) == 0 ? 0 : -2;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/* Emits the null value for an explicit key that never got its ':' line. */
|
|
410
|
+
static int e_flush_q_value(yep_engine* e) {
|
|
411
|
+
if (!e->q_value_pending) {
|
|
412
|
+
return 0;
|
|
413
|
+
}
|
|
414
|
+
if (e->depth > e->q_map_depth) {
|
|
415
|
+
return 0; /* the key's own structure is still open */
|
|
416
|
+
}
|
|
417
|
+
e->q_value_pending = 0;
|
|
418
|
+
yep_event ev;
|
|
419
|
+
e_event_init(&ev, YEP_EV_SCALAR);
|
|
420
|
+
ev.style = YEP_STYLE_PLAIN;
|
|
421
|
+
ev.implicit = 1;
|
|
422
|
+
return emit_now(e, &ev) == 0 ? 0 : -2;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
static int e_close_to(yep_engine* e, int to_depth) {
|
|
426
|
+
while (e->depth > to_depth) {
|
|
427
|
+
/* the pending '?' value flushes once the key's structure closed */
|
|
428
|
+
int rc = e_flush_q_value(e);
|
|
429
|
+
if (rc != 0) {
|
|
430
|
+
return rc;
|
|
431
|
+
}
|
|
432
|
+
if (e->q_value_pending && e->depth == e->q_map_depth) {
|
|
433
|
+
continue; /* flushed the null; this frame closes next round */
|
|
434
|
+
}
|
|
435
|
+
e->depth--;
|
|
436
|
+
yep_event ev;
|
|
437
|
+
e_event_init(&ev,
|
|
438
|
+
e->frames[e->depth].kind == YEP_FRAME_SEQ ? YEP_EV_SEQ_END : YEP_EV_MAP_END);
|
|
439
|
+
if (emit_now(e, &ev) != 0) {
|
|
440
|
+
return -2;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
return 0;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/* ------------------------------------------------------------- scalars */
|
|
447
|
+
|
|
448
|
+
/* Quoted scalar at the opening quote; spans lines when needed. */
|
|
449
|
+
static int e_quoted_floor(yep_engine* e, yep_event* ev, uint16_t min_indent, int enforce) {
|
|
450
|
+
unsigned char q = (unsigned char)e->p[e->pos];
|
|
451
|
+
size_t content_start = e->pos + 1;
|
|
452
|
+
int has_esc = 0;
|
|
453
|
+
size_t close;
|
|
454
|
+
{
|
|
455
|
+
const yep_text_kernels* k = yep_text_active();
|
|
456
|
+
ptrdiff_t r =
|
|
457
|
+
k->quote_scan(e->p + content_start, e->len - content_start, (char)q, &has_esc);
|
|
458
|
+
if (r >= 0) {
|
|
459
|
+
close = content_start + (size_t)r;
|
|
460
|
+
} else {
|
|
461
|
+
/* unterminated on this span: manual multi-line scan */
|
|
462
|
+
size_t i = content_start;
|
|
463
|
+
close = e->len + 1;
|
|
464
|
+
while (i < e->len) {
|
|
465
|
+
if (q == '"' && e->p[i] == '\\') {
|
|
466
|
+
i += 2;
|
|
467
|
+
has_esc = 1;
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
if ((unsigned char)e->p[i] == q) {
|
|
471
|
+
if (q == '\'' && i + 1 < e->len && e->p[i + 1] == '\'') {
|
|
472
|
+
i += 2;
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
close = i;
|
|
476
|
+
break;
|
|
477
|
+
}
|
|
478
|
+
i++;
|
|
479
|
+
}
|
|
480
|
+
if (close == e->len + 1) {
|
|
481
|
+
return e_fail(e, YEP_ERR_UNTERMINATED_QUOTE, e->pos);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
uint32_t start = (uint32_t)content_start;
|
|
487
|
+
uint32_t end = (uint32_t)close;
|
|
488
|
+
int multiline = 0;
|
|
489
|
+
uint32_t breaks = 0; /* '\n' occurrences, folded with detection */
|
|
490
|
+
{
|
|
491
|
+
const yep_text_kernels* k = yep_text_active();
|
|
492
|
+
const yep_stopset* brk = &yep_break_stopset; /* scan's SSOT */
|
|
493
|
+
size_t i = start;
|
|
494
|
+
while (i < end) {
|
|
495
|
+
ptrdiff_t r = k->stopset_find(brk, e->p + i, end - i);
|
|
496
|
+
if (r < 0) {
|
|
497
|
+
break; /* single-line span: one SIMD call decided it */
|
|
498
|
+
}
|
|
499
|
+
size_t at = i + (size_t)r;
|
|
500
|
+
multiline = 1;
|
|
501
|
+
if (e->p[at] == '\n') {
|
|
502
|
+
breaks++;
|
|
503
|
+
}
|
|
504
|
+
/* each following line must be content, not a document marker,
|
|
505
|
+
* and in block context must out-indent the parent (QB6E) */
|
|
506
|
+
size_t br = yep_scan_break_len(e->p, e->len, at);
|
|
507
|
+
yep_line_info ql = yep_scan_line(e->p, e->len, at + br);
|
|
508
|
+
if (ql.flags & (YEP_LF_DOC_START | YEP_LF_DOC_END | YEP_LF_DIRECTIVE)) {
|
|
509
|
+
return e_fail(e, YEP_ERR_UNTERMINATED_QUOTE, at + br);
|
|
510
|
+
}
|
|
511
|
+
if (enforce && !(ql.flags & YEP_LF_BLANK) && ql.indent <= min_indent) {
|
|
512
|
+
return e_fail(e, YEP_ERR_BAD_INDENT, at + br + ql.indent);
|
|
513
|
+
}
|
|
514
|
+
i = at + br;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
/* escape pre-validation (double quotes only: ' has no escapes);
|
|
518
|
+
* quote_scan already reported whether any backslash exists */
|
|
519
|
+
for (size_t i = (q == '"' && has_esc) ? start : end; i < end; i++) {
|
|
520
|
+
if (e->p[i] != '\\' || i + 1 >= end) {
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
unsigned char esc = (unsigned char)e->p[i + 1];
|
|
524
|
+
if (esc == 'x' || esc == 'u' || esc == 'U') {
|
|
525
|
+
/* hex escapes carry exact digit counts: 2/4/8 */
|
|
526
|
+
size_t need = (esc == 'x') ? 2 : (esc == 'u') ? 4 : 8;
|
|
527
|
+
if (i + 1 + need >= end) {
|
|
528
|
+
return e_fail(e, YEP_ERR_INVALID_ESCAPE, i); /* truncated "\uDd" */
|
|
529
|
+
}
|
|
530
|
+
for (size_t k = 1; k <= need; k++) {
|
|
531
|
+
if (!yep_ct_is((unsigned char)e->p[i + 1 + k], YEP_CT_HEXDIGIT)) {
|
|
532
|
+
return e_fail(e, YEP_ERR_INVALID_ESCAPE, i + 1 + k);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
if (esc == 'u') {
|
|
536
|
+
uint32_t cp = 0;
|
|
537
|
+
for (size_t k = 1; k <= 4; k++) {
|
|
538
|
+
cp = (cp << 4) | (uint32_t)hexval((unsigned char)e->p[i + 1 + k]);
|
|
539
|
+
}
|
|
540
|
+
if (cp >= 0xD800 && cp <= 0xDBFF) {
|
|
541
|
+
/* high surrogate: the low escape must follow */
|
|
542
|
+
size_t j = i + 6; /* at the low escape's backslash */
|
|
543
|
+
uint32_t lo = 0;
|
|
544
|
+
if (j + 6 <= end && e->p[j] == '\\' && e->p[j + 1] == 'u') {
|
|
545
|
+
int lok = 1;
|
|
546
|
+
for (int k = 0; k < 4; k++) {
|
|
547
|
+
int hv = hexval((unsigned char)e->p[j + 2 + (size_t)k]);
|
|
548
|
+
if (hv < 0) {
|
|
549
|
+
lok = 0;
|
|
550
|
+
break;
|
|
551
|
+
}
|
|
552
|
+
lo = (lo << 4) | (uint32_t)hv;
|
|
553
|
+
}
|
|
554
|
+
if (lok && lo >= 0xDC00 && lo <= 0xDFFF) {
|
|
555
|
+
i = j + 5; /* at the low escape's last digit */
|
|
556
|
+
continue;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
return e_fail(e, YEP_ERR_INVALID_ESCAPE, i);
|
|
560
|
+
}
|
|
561
|
+
if (cp >= 0xDC00 && cp <= 0xDFFF) {
|
|
562
|
+
return e_fail(e, YEP_ERR_INVALID_ESCAPE, i); /* lone low */
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
i += 1 + need; /* the digits are not escapes themselves */
|
|
566
|
+
continue;
|
|
567
|
+
}
|
|
568
|
+
if (esc == '\n' || esc == '\r') {
|
|
569
|
+
continue; /* line continuation: checked by finish_double */
|
|
570
|
+
}
|
|
571
|
+
switch (esc) {
|
|
572
|
+
case '0':
|
|
573
|
+
case 'a':
|
|
574
|
+
case 'b':
|
|
575
|
+
case 't':
|
|
576
|
+
case '\t':
|
|
577
|
+
case 'n':
|
|
578
|
+
case 'v':
|
|
579
|
+
case 'f':
|
|
580
|
+
case 'r':
|
|
581
|
+
case 'e':
|
|
582
|
+
case ' ':
|
|
583
|
+
case '"':
|
|
584
|
+
case '/':
|
|
585
|
+
case '\\':
|
|
586
|
+
case 'N':
|
|
587
|
+
case '_':
|
|
588
|
+
case 'L':
|
|
589
|
+
case 'P':
|
|
590
|
+
break;
|
|
591
|
+
default:
|
|
592
|
+
return e_fail(e, YEP_ERR_INVALID_ESCAPE, i);
|
|
593
|
+
}
|
|
594
|
+
i++; /* skip the escaped byte */
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
ev->style = (q == '\'') ? YEP_STYLE_SINGLE_QUOTED : YEP_STYLE_DOUBLE_QUOTED;
|
|
598
|
+
if (q == '\'') {
|
|
599
|
+
ev->multiline = (uint8_t)multiline;
|
|
600
|
+
char* out = yep_finish_single(e->p, start, end, multiline, e->pool, &ev->value.len);
|
|
601
|
+
if (out == NULL) {
|
|
602
|
+
ev->value.p = e->p + start;
|
|
603
|
+
ev->value.len = end - start; /* finish_single returns before writing len */
|
|
604
|
+
ev->borrowed = 1;
|
|
605
|
+
} else {
|
|
606
|
+
ev->value.p = out;
|
|
607
|
+
ev->borrowed = 0;
|
|
608
|
+
}
|
|
609
|
+
} else if (!has_esc && !multiline) {
|
|
610
|
+
ev->value.p = e->p + start;
|
|
611
|
+
ev->value.len = end - start;
|
|
612
|
+
ev->borrowed = 1;
|
|
613
|
+
} else {
|
|
614
|
+
ev->multiline = (uint8_t)multiline;
|
|
615
|
+
char* out = yep_finish_double(e->p, start, end, multiline, e->pool, &ev->value.len);
|
|
616
|
+
if (out == NULL) {
|
|
617
|
+
return e_fail(e, YEP_ERR_MEMORY, e->pos);
|
|
618
|
+
}
|
|
619
|
+
ev->value.p = out;
|
|
620
|
+
ev->borrowed = 0;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
size_t after = close + 1;
|
|
624
|
+
e->line += breaks;
|
|
625
|
+
/* approximate: after a multi-line scalar the column resets */
|
|
626
|
+
if (multiline) {
|
|
627
|
+
size_t ls = close;
|
|
628
|
+
while (ls > start && e->p[ls - 1] != '\n') {
|
|
629
|
+
ls--;
|
|
630
|
+
}
|
|
631
|
+
e->line_start = ls;
|
|
632
|
+
}
|
|
633
|
+
e->pos = after;
|
|
634
|
+
e_skip_inline_space(e);
|
|
635
|
+
return 0;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/* Block scalar (| or >) with header, auto/explicit indent, chomping. */
|
|
639
|
+
static int e_block_scalar(yep_engine* e, yep_event* ev, int parent_col) {
|
|
640
|
+
int folded = (e->p[e->pos] == '>');
|
|
641
|
+
e->pos++;
|
|
642
|
+
int chomp = 0;
|
|
643
|
+
int explicit_indent = 0;
|
|
644
|
+
while (e->pos < e->len && e->p[e->pos] != '\n' && e->p[e->pos] != '\r') {
|
|
645
|
+
char c = e->p[e->pos];
|
|
646
|
+
if (c == ' ' || c == '\t') {
|
|
647
|
+
e->pos++;
|
|
648
|
+
continue;
|
|
649
|
+
}
|
|
650
|
+
if (e_comment_at(e)) {
|
|
651
|
+
e_skip_to_eol(e);
|
|
652
|
+
break;
|
|
653
|
+
}
|
|
654
|
+
if (c == '-') {
|
|
655
|
+
chomp = 1;
|
|
656
|
+
} else if (c == '+') {
|
|
657
|
+
chomp = 2;
|
|
658
|
+
} else if (c >= '1' && c <= '9') {
|
|
659
|
+
explicit_indent = c - '0';
|
|
660
|
+
} else {
|
|
661
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
|
|
662
|
+
}
|
|
663
|
+
e->pos++;
|
|
664
|
+
}
|
|
665
|
+
e_line_done(e, e->pos);
|
|
666
|
+
|
|
667
|
+
int content_indent =
|
|
668
|
+
explicit_indent ? (parent_col < 0 ? explicit_indent : parent_col + explicit_indent) : -1;
|
|
669
|
+
e->fold_n = 0;
|
|
670
|
+
uint32_t pending_breaks = 0;
|
|
671
|
+
uint32_t trailing_breaks = 0;
|
|
672
|
+
int saw_content = 0;
|
|
673
|
+
int blank_tab = 0;
|
|
674
|
+
int max_blank_indent = -1;
|
|
675
|
+
|
|
676
|
+
while (e->pos < e->len) {
|
|
677
|
+
/* trailing spaces after "|-" leave pos mid-line: scan from pos
|
|
678
|
+
* (the header tail), never the memo's full line */
|
|
679
|
+
yep_line_info li =
|
|
680
|
+
e->pos == e->line_start ? e_line_info_here(e) : yep_scan_line(e->p, e->len, e->pos);
|
|
681
|
+
int blank = (li.flags & YEP_LF_BLANK) != 0;
|
|
682
|
+
if (!blank) {
|
|
683
|
+
if (saw_content && parent_col < 0 &&
|
|
684
|
+
(li.flags & (YEP_LF_DOC_START | YEP_LF_DOC_END | YEP_LF_DIRECTIVE))) {
|
|
685
|
+
break; /* root block ends at ---/.../% even at the content column */
|
|
686
|
+
}
|
|
687
|
+
if (content_indent < 0) {
|
|
688
|
+
content_indent = li.indent;
|
|
689
|
+
if (content_indent <= parent_col) {
|
|
690
|
+
break; /* no block content */
|
|
691
|
+
}
|
|
692
|
+
} else if (li.indent <= parent_col) {
|
|
693
|
+
break; /* dedent ends the block */
|
|
694
|
+
} else if (li.indent < content_indent && li.indent <= parent_col) {
|
|
695
|
+
break;
|
|
696
|
+
}
|
|
697
|
+
if (li.indent < content_indent) {
|
|
698
|
+
break;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
if (blank) {
|
|
702
|
+
if (li.indent > 0 && memchr(e->p + li.offset, '\t', li.end - li.offset) != NULL) {
|
|
703
|
+
/* a tab after leading spaces is CONTENT (Y79Y#2) */
|
|
704
|
+
if (content_indent < 0) {
|
|
705
|
+
content_indent = li.indent;
|
|
706
|
+
if (content_indent <= parent_col) {
|
|
707
|
+
break;
|
|
708
|
+
}
|
|
709
|
+
} else if (li.indent < content_indent) {
|
|
710
|
+
break;
|
|
711
|
+
}
|
|
712
|
+
if (li.indent >= content_indent) {
|
|
713
|
+
uint32_t from2 = li.offset + (uint32_t)content_indent;
|
|
714
|
+
if (e->fold_n >= YEP_MAX_FOLD_LINES) {
|
|
715
|
+
return e_fail(e, YEP_ERR_MEMORY, e->pos);
|
|
716
|
+
}
|
|
717
|
+
e->fold[e->fold_n].content.p = e->p + from2;
|
|
718
|
+
e->fold[e->fold_n].content.len = li.end > from2 ? li.end - from2 : 0;
|
|
719
|
+
e->fold[e->fold_n].breaks_before =
|
|
720
|
+
saw_content ? pending_breaks + 1 : pending_breaks;
|
|
721
|
+
e->fold[e->fold_n].more_indented = 1;
|
|
722
|
+
e->fold_n++;
|
|
723
|
+
saw_content = 1;
|
|
724
|
+
pending_breaks = 0;
|
|
725
|
+
trailing_breaks = 0;
|
|
726
|
+
e_line_done(e, li.end);
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
/* a blank line whose spaces extend past the content indent is
|
|
731
|
+
* CONTENT (kept verbatim); otherwise it is a pure break */
|
|
732
|
+
if (content_indent >= 0 && li.indent > content_indent) {
|
|
733
|
+
if (e->fold_n >= YEP_MAX_FOLD_LINES) {
|
|
734
|
+
return e_fail(e, YEP_ERR_MEMORY, e->pos);
|
|
735
|
+
}
|
|
736
|
+
e->fold[e->fold_n].content.p = e->p + li.offset + (uint32_t)content_indent;
|
|
737
|
+
e->fold[e->fold_n].content.len =
|
|
738
|
+
li.end > li.offset + content_indent ? li.end - li.offset - content_indent : 0;
|
|
739
|
+
e->fold[e->fold_n].breaks_before =
|
|
740
|
+
saw_content ? pending_breaks + 1 : pending_breaks;
|
|
741
|
+
e->fold[e->fold_n].more_indented = 1;
|
|
742
|
+
e->fold_n++;
|
|
743
|
+
saw_content = 1;
|
|
744
|
+
pending_breaks = 0;
|
|
745
|
+
trailing_breaks = 0;
|
|
746
|
+
{
|
|
747
|
+
size_t at = li.end;
|
|
748
|
+
size_t br = yep_scan_break_len(e->p, e->len, at);
|
|
749
|
+
if (br > 0) {
|
|
750
|
+
trailing_breaks = 1;
|
|
751
|
+
e->pos = at + br;
|
|
752
|
+
e->line++;
|
|
753
|
+
e->line_start = e->pos;
|
|
754
|
+
e->li_cache_line = 0;
|
|
755
|
+
e->shape_cache_line = 0;
|
|
756
|
+
} else {
|
|
757
|
+
e->pos = at;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
if (!saw_content) {
|
|
763
|
+
if (memchr(e->p + li.offset, '\t', li.end - li.offset) != NULL) {
|
|
764
|
+
blank_tab = 1;
|
|
765
|
+
}
|
|
766
|
+
if ((int)li.indent > max_blank_indent) {
|
|
767
|
+
max_blank_indent = (int)li.indent;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
pending_breaks++;
|
|
771
|
+
e_line_done(e, li.end);
|
|
772
|
+
continue;
|
|
773
|
+
}
|
|
774
|
+
uint32_t from = li.offset + (uint32_t)content_indent;
|
|
775
|
+
if (e->fold_n >= YEP_MAX_FOLD_LINES) {
|
|
776
|
+
return e_fail(e, YEP_ERR_MEMORY, e->pos);
|
|
777
|
+
}
|
|
778
|
+
e->fold[e->fold_n].content.p = e->p + from;
|
|
779
|
+
e->fold[e->fold_n].content.len = li.end > from ? li.end - from : 0;
|
|
780
|
+
/* leading blank lines belong to the block content */
|
|
781
|
+
e->fold[e->fold_n].breaks_before = saw_content ? pending_breaks + 1 : pending_breaks;
|
|
782
|
+
e->fold[e->fold_n].more_indented =
|
|
783
|
+
li.indent > content_indent ||
|
|
784
|
+
(from < li.end && (e->p[from] == ' ' || e->p[from] == '\t'));
|
|
785
|
+
e->fold_n++;
|
|
786
|
+
saw_content = 1;
|
|
787
|
+
pending_breaks = 0;
|
|
788
|
+
trailing_breaks = 0;
|
|
789
|
+
/* the break after this content line counts toward trailing */
|
|
790
|
+
size_t at = li.end;
|
|
791
|
+
size_t br = yep_scan_break_len(e->p, e->len, at);
|
|
792
|
+
if (br > 0) {
|
|
793
|
+
trailing_breaks = 1;
|
|
794
|
+
e->pos = at + br;
|
|
795
|
+
e->line++;
|
|
796
|
+
e->line_start = e->pos;
|
|
797
|
+
} else {
|
|
798
|
+
e->pos = at;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
trailing_breaks += pending_breaks;
|
|
802
|
+
if (saw_content ? (max_blank_indent > content_indent) : blank_tab) {
|
|
803
|
+
/* a leading blank line out-indents the first content line; a
|
|
804
|
+
* tab-only line before any content is an error (5LLU/Y79Y) */
|
|
805
|
+
return e_fail(e, YEP_ERR_BAD_INDENT, e->pos);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
char* out = yep_finish_block(e->fold, e->fold_n, folded, chomp, trailing_breaks, e->pool,
|
|
809
|
+
&ev->value.len);
|
|
810
|
+
ev->value.p = out; /* may be NULL for an empty block; len 0 */
|
|
811
|
+
ev->borrowed = 0;
|
|
812
|
+
ev->style = folded ? YEP_STYLE_FOLDED : YEP_STYLE_LITERAL;
|
|
813
|
+
return 0;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/* Multi-line plain scalar continuation in block context. */
|
|
817
|
+
static int e_plain_multiline(yep_engine* e, yep_span s0, uint32_t block_floor, yep_event* ev,
|
|
818
|
+
int root_ctx) {
|
|
819
|
+
/* the caller already scanned this exact span for its colon/term
|
|
820
|
+
* decision — the rescan doubled every plain value's scan cost */
|
|
821
|
+
e->fold_n = 0;
|
|
822
|
+
{
|
|
823
|
+
e->fold[0].content.p = e->p + s0.start;
|
|
824
|
+
e->fold[0].content.len = s0.end - s0.start;
|
|
825
|
+
e->fold[0].breaks_before = 0;
|
|
826
|
+
e->fold_n = 1;
|
|
827
|
+
if (s0.term == YEP_TERM_COMMENT) {
|
|
828
|
+
e->pos = s0.end;
|
|
829
|
+
e_skip_to_eol(e);
|
|
830
|
+
return 0; /* the comment ends the scalar here */
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
uint32_t breaks = 0;
|
|
834
|
+
for (;;) {
|
|
835
|
+
if (e->pos >= e->len) {
|
|
836
|
+
break;
|
|
837
|
+
}
|
|
838
|
+
/* trailing spaces/tabs after a piece are stripped before the
|
|
839
|
+
* break — they must not inflate the break count */
|
|
840
|
+
while (e->pos < e->len && (e->p[e->pos] == ' ' || e->p[e->pos] == '\t')) {
|
|
841
|
+
e->pos++;
|
|
842
|
+
}
|
|
843
|
+
if (e->pos >= e->len) {
|
|
844
|
+
break;
|
|
845
|
+
}
|
|
846
|
+
if (e_comment_at(e)) {
|
|
847
|
+
e_skip_to_eol(e);
|
|
848
|
+
break; /* a comment terminates the plain scalar */
|
|
849
|
+
}
|
|
850
|
+
if (e->pos >= e->len) {
|
|
851
|
+
break;
|
|
852
|
+
}
|
|
853
|
+
breaks++;
|
|
854
|
+
e_line_done(e, e->pos);
|
|
855
|
+
yep_line_info li = e_line_info_here(e);
|
|
856
|
+
if (li.flags & YEP_LF_BLANK) {
|
|
857
|
+
/* Park at the line end: the loop's next break consumption
|
|
858
|
+
* counts this blank line's own break. */
|
|
859
|
+
e->pos = li.end;
|
|
860
|
+
continue;
|
|
861
|
+
}
|
|
862
|
+
/* At document root there is no parent block, so continuation
|
|
863
|
+
* lines may sit at column 0. */
|
|
864
|
+
int too_shallow = root_ctx ? 0 : (li.indent <= block_floor);
|
|
865
|
+
if (too_shallow || (li.flags & (YEP_LF_DOC_START | YEP_LF_DOC_END))) {
|
|
866
|
+
break; /* a directive-LIKE line is content (XLQ9) */
|
|
867
|
+
}
|
|
868
|
+
size_t cs = e->pos + li.indent;
|
|
869
|
+
while (cs < e->len && (e->p[cs] == ' ' || e->p[cs] == '\t')) {
|
|
870
|
+
cs++; /* continuation leading white space is stripped */
|
|
871
|
+
}
|
|
872
|
+
yep_span s = yep_scan_plain(e->p, e->len, cs, 0);
|
|
873
|
+
if (s.term == YEP_TERM_COLON) {
|
|
874
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, s.end);
|
|
875
|
+
}
|
|
876
|
+
if (e->fold_n >= YEP_MAX_FOLD_LINES) {
|
|
877
|
+
return e_fail(e, YEP_ERR_MEMORY, e->pos);
|
|
878
|
+
}
|
|
879
|
+
e->fold[e->fold_n].content.p = e->p + s.start;
|
|
880
|
+
e->fold[e->fold_n].content.len = s.end - s.start;
|
|
881
|
+
e->fold[e->fold_n].breaks_before = breaks;
|
|
882
|
+
e->fold_n++;
|
|
883
|
+
breaks = 0;
|
|
884
|
+
e->pos = s.end;
|
|
885
|
+
if (s.term == YEP_TERM_COMMENT) {
|
|
886
|
+
e_skip_to_eol(e);
|
|
887
|
+
break; /* the comment ends the scalar; later lines are new */
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
if (e->fold_n == 1) {
|
|
891
|
+
return 0; /* single line: the borrowed view stands */
|
|
892
|
+
}
|
|
893
|
+
char* out = yep_fold_plain(e->fold, e->fold_n, e->pool, &ev->value.len);
|
|
894
|
+
if (out == NULL) {
|
|
895
|
+
return e_fail(e, YEP_ERR_MEMORY, e->pos);
|
|
896
|
+
}
|
|
897
|
+
ev->value.p = out;
|
|
898
|
+
ev->borrowed = 0;
|
|
899
|
+
return 0;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
/* ---------------------------------------------------------------- props */
|
|
903
|
+
|
|
904
|
+
/* Resolves a scanned tag against the document's %TAG map. !! is the
|
|
905
|
+
* YAML shorthand prefix; !<...> is verbatim; matched handles compose
|
|
906
|
+
* prefix+suffix in the finish pool. */
|
|
907
|
+
static int e_hex_val(unsigned char c) {
|
|
908
|
+
if (c >= '0' && c <= '9') {
|
|
909
|
+
return c - '0';
|
|
910
|
+
}
|
|
911
|
+
if (c >= 'a' && c <= 'f') {
|
|
912
|
+
return c - 'a' + 10;
|
|
913
|
+
}
|
|
914
|
+
if (c >= 'A' && c <= 'F') {
|
|
915
|
+
return c - 'A' + 10;
|
|
916
|
+
}
|
|
917
|
+
return -1;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
/* Tags carry URI escapes (%XX) in every form (verbatim, !!, %TAG). */
|
|
921
|
+
static yep_view e_tag_uri_decode(yep_engine* e, yep_view v) {
|
|
922
|
+
const char* p = v.p;
|
|
923
|
+
for (uint32_t i = 0; i + 2 < v.len; i++) {
|
|
924
|
+
if (p[i] == '%' && e_hex_val((unsigned char)p[i + 1]) >= 0 &&
|
|
925
|
+
e_hex_val((unsigned char)p[i + 2]) >= 0) {
|
|
926
|
+
char* out = yep_pool_alloc(e->pool, v.len, 16);
|
|
927
|
+
if (out == NULL) {
|
|
928
|
+
return v;
|
|
929
|
+
}
|
|
930
|
+
uint32_t o = 0;
|
|
931
|
+
for (uint32_t j = 0; j < v.len;) {
|
|
932
|
+
if (j + 2 < v.len && p[j] == '%' && e_hex_val((unsigned char)p[j + 1]) >= 0 &&
|
|
933
|
+
e_hex_val((unsigned char)p[j + 2]) >= 0) {
|
|
934
|
+
out[o++] = (char)(e_hex_val((unsigned char)p[j + 1]) * 16 +
|
|
935
|
+
e_hex_val((unsigned char)p[j + 2]));
|
|
936
|
+
j += 3;
|
|
937
|
+
} else {
|
|
938
|
+
out[o++] = p[j++];
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
yep_view d = {out, o};
|
|
942
|
+
return d;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
return v;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
static yep_view e_resolve_tag_raw(yep_engine* e, yep_view tag) {
|
|
949
|
+
if (tag.len >= 3 && tag.p[0] == '!' && tag.p[1] == '<' && tag.p[tag.len - 1] == '>') {
|
|
950
|
+
yep_view v = {tag.p + 2, tag.len - 3};
|
|
951
|
+
return v;
|
|
952
|
+
}
|
|
953
|
+
/* longest matching handle wins */
|
|
954
|
+
int best = -1;
|
|
955
|
+
uint32_t best_len = 0;
|
|
956
|
+
for (int i = 0; i < e->tagmap_n; i++) {
|
|
957
|
+
if (tag.len >= e->tagmap[i].handle.len && e->tagmap[i].handle.len > best_len &&
|
|
958
|
+
memcmp(tag.p, e->tagmap[i].handle.p, e->tagmap[i].handle.len) == 0) {
|
|
959
|
+
best = i;
|
|
960
|
+
best_len = e->tagmap[i].handle.len;
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
if (tag.len >= 2 && tag.p[0] == '!' && tag.p[1] == '!' && best < 0) {
|
|
964
|
+
static const char yns[] = "tag:yaml.org,2002:";
|
|
965
|
+
size_t plen = sizeof(yns) - 1;
|
|
966
|
+
char* out = yep_pool_alloc(e->pool, plen + tag.len - 2, 16);
|
|
967
|
+
if (out == NULL) {
|
|
968
|
+
return tag;
|
|
969
|
+
}
|
|
970
|
+
memcpy(out, yns, plen);
|
|
971
|
+
memcpy(out + plen, tag.p + 2, tag.len - 2);
|
|
972
|
+
yep_view v = {out, (uint32_t)(plen + tag.len - 2)};
|
|
973
|
+
return v;
|
|
974
|
+
}
|
|
975
|
+
if (best >= 0) {
|
|
976
|
+
const yep_view* h = &e->tagmap[best].handle;
|
|
977
|
+
const yep_view* pfx = &e->tagmap[best].prefix;
|
|
978
|
+
size_t n = pfx->len + tag.len - h->len;
|
|
979
|
+
char* out = yep_pool_alloc(e->pool, n ? n : 1, 16);
|
|
980
|
+
if (out == NULL) {
|
|
981
|
+
return tag;
|
|
982
|
+
}
|
|
983
|
+
memcpy(out, pfx->p, pfx->len);
|
|
984
|
+
memcpy(out + pfx->len, tag.p + h->len, tag.len - h->len);
|
|
985
|
+
yep_view v = {out, (uint32_t)n};
|
|
986
|
+
return v;
|
|
987
|
+
}
|
|
988
|
+
/* "!word!suffix" with no %TAG entry for the handle is undefined */
|
|
989
|
+
for (uint32_t k = 1; k + 1 < tag.len; k++) {
|
|
990
|
+
if (tag.p[k] == '!') {
|
|
991
|
+
yep_view h = {tag.p, k + 1};
|
|
992
|
+
char* m = yep_pool_alloc(e->pool, h.len, 16);
|
|
993
|
+
if (m != NULL) {
|
|
994
|
+
memcpy(m, h.p, h.len);
|
|
995
|
+
yep_view hv = {m, h.len};
|
|
996
|
+
yep_view res = e_resolve_tag_raw(e, hv);
|
|
997
|
+
if (res.p == hv.p) {
|
|
998
|
+
e->tag_undef = 1; /* e_props reports it */
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
break;
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
return tag;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
static yep_view e_resolve_tag(yep_engine* e, yep_view tag) {
|
|
1008
|
+
return e_tag_uri_decode(e, e_resolve_tag_raw(e, tag));
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
/* Parses &anchor / !tag runs at the cursor into *anchor / *tag. */
|
|
1012
|
+
static void e_props(yep_engine* e, yep_view* anchor, yep_view* tag) {
|
|
1013
|
+
for (;;) {
|
|
1014
|
+
e_skip_inline_space(e);
|
|
1015
|
+
if (e->pos >= e->len) {
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
unsigned char c = (unsigned char)e->p[e->pos];
|
|
1019
|
+
if (c == '&') {
|
|
1020
|
+
size_t start = e->pos + 1;
|
|
1021
|
+
e->pos = yep_scan_prop_end(e->p, e->len, start);
|
|
1022
|
+
anchor->p = e->p + start;
|
|
1023
|
+
anchor->len = (uint32_t)(e->pos - start);
|
|
1024
|
+
} else if (c == '!') {
|
|
1025
|
+
size_t start = e->pos;
|
|
1026
|
+
e->pos++;
|
|
1027
|
+
if (e->pos < e->len && e->p[e->pos] == '<') {
|
|
1028
|
+
while (e->pos < e->len && e->p[e->pos] != '>') {
|
|
1029
|
+
e->pos++;
|
|
1030
|
+
}
|
|
1031
|
+
if (e->pos < e->len) {
|
|
1032
|
+
e->pos++;
|
|
1033
|
+
}
|
|
1034
|
+
} else {
|
|
1035
|
+
while (e->pos < e->len && yep_scan_prop_char((unsigned char)e->p[e->pos])) {
|
|
1036
|
+
e->pos++;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
yep_view raw = {e->p + start, (uint32_t)(e->pos - start)};
|
|
1040
|
+
*tag = e_resolve_tag(e, raw);
|
|
1041
|
+
if (e->tag_undef) {
|
|
1042
|
+
e->tag_undef = 0;
|
|
1043
|
+
e_fail(e, YEP_ERR_UNEXPECTED, start);
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
} else {
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
/* ------------------------------------------------------------- aliases */
|
|
1053
|
+
|
|
1054
|
+
static int e_alias(yep_engine* e, yep_event* ev) {
|
|
1055
|
+
size_t star = e->pos;
|
|
1056
|
+
size_t start = ++e->pos;
|
|
1057
|
+
while (e->pos < e->len && yep_scan_prop_char((unsigned char)e->p[e->pos])) {
|
|
1058
|
+
if (e->p[e->pos] == ':') {
|
|
1059
|
+
/* ':' separates only before blank or flow indicators; at
|
|
1060
|
+
* EOL it belongs to the name ("*a:" — 2SXE), and interior
|
|
1061
|
+
* colons always do ("*an:chor" — Y2GN) */
|
|
1062
|
+
size_t n2 = e->pos + 1;
|
|
1063
|
+
if (n2 < e->len && (e->p[n2] == ' ' || e->p[n2] == '\t' ||
|
|
1064
|
+
yep_ct_is((unsigned char)e->p[n2], YEP_CT_FLOW_IND))) {
|
|
1065
|
+
break;
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
e->pos++;
|
|
1069
|
+
}
|
|
1070
|
+
yep_view name = {e->p + start, (uint32_t)(e->pos - start)};
|
|
1071
|
+
if (name.len == 0) {
|
|
1072
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, star);
|
|
1073
|
+
}
|
|
1074
|
+
uint32_t target = anchor_id_of(e, name);
|
|
1075
|
+
if (target == 0) {
|
|
1076
|
+
e->pos = star;
|
|
1077
|
+
return e_fail(e, YEP_ERR_UNDEFINED_ALIAS, star);
|
|
1078
|
+
}
|
|
1079
|
+
ev->type = YEP_EV_ALIAS;
|
|
1080
|
+
ev->value = name;
|
|
1081
|
+
ev->borrowed = 1; /* the name is an input view (TODO.restructure/73):
|
|
1082
|
+
the DOM borrows it like any plain scalar — no
|
|
1083
|
+
arena copy per alias */
|
|
1084
|
+
ev->anchor_id = target;
|
|
1085
|
+
e_skip_inline_space(e);
|
|
1086
|
+
return 0;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
/* ---------------------------------------------------------------- flow */
|
|
1090
|
+
|
|
1091
|
+
static int e_flow_ws(yep_engine* e) {
|
|
1092
|
+
for (;;) {
|
|
1093
|
+
while (e->pos < e->len && (e->p[e->pos] == ' ' || e->p[e->pos] == '\t')) {
|
|
1094
|
+
e->pos++;
|
|
1095
|
+
}
|
|
1096
|
+
if (e->pos < e->len && e_comment_at(e)) {
|
|
1097
|
+
e_skip_to_eol(e);
|
|
1098
|
+
continue;
|
|
1099
|
+
}
|
|
1100
|
+
if (e->pos < e->len && (e->p[e->pos] == '\n' || e->p[e->pos] == '\r')) {
|
|
1101
|
+
e_line_done(e, e->pos);
|
|
1102
|
+
if (e->pos < e->len && e->pos == e->line_start) {
|
|
1103
|
+
yep_line_info fl = e_line_info_here(e);
|
|
1104
|
+
if ((fl.flags & YEP_LF_TAB) && !(fl.flags & YEP_LF_BLANK) && fl.indent == 0) {
|
|
1105
|
+
/* a col-0 tab before flow content errors; tabs after
|
|
1106
|
+
* spaces are separation (6HB6) */
|
|
1107
|
+
size_t t = e->line_start + fl.indent;
|
|
1108
|
+
while (t < fl.end && (e->p[t] == ' ' || e->p[t] == '\t')) {
|
|
1109
|
+
t++;
|
|
1110
|
+
}
|
|
1111
|
+
if (t < fl.end && e->p[t] != '[' && e->p[t] != ']' && e->p[t] != '{' &&
|
|
1112
|
+
e->p[t] != '}' && e->p[t] != ',' && e->p[t] != '#' && e->p[t] != ':') {
|
|
1113
|
+
e_fail(e, YEP_ERR_TAB_IN_INDENT, t); /* Y79Y#4 */
|
|
1114
|
+
return -1;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
continue;
|
|
1119
|
+
}
|
|
1120
|
+
return 0;
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
/* Pre-scans a flow collection from its opening bracket, returning 0 with
|
|
1125
|
+
* e->pos just past the matching close (no events; for the flow-as-key
|
|
1126
|
+
* decision MAP_START must precede the key's events). */
|
|
1127
|
+
static int e_skip_flow(yep_engine* e) {
|
|
1128
|
+
int stk[YEP_MAX_DEPTH];
|
|
1129
|
+
int n = 0;
|
|
1130
|
+
stk[n++] = (e->p[e->pos] == '[') ? 0 : 1;
|
|
1131
|
+
e->pos++;
|
|
1132
|
+
while (e->pos < e->len && n > 0) {
|
|
1133
|
+
unsigned char c = (unsigned char)e->p[e->pos];
|
|
1134
|
+
if (c == '"' || c == '\'') {
|
|
1135
|
+
int esc2 = 0;
|
|
1136
|
+
const yep_text_kernels* k = yep_text_active();
|
|
1137
|
+
ptrdiff_t r = k->quote_scan(e->p + e->pos + 1, e->len - e->pos - 1, (char)c, &esc2);
|
|
1138
|
+
if (r < 0) {
|
|
1139
|
+
return e_fail(e, YEP_ERR_UNTERMINATED_QUOTE, e->pos);
|
|
1140
|
+
}
|
|
1141
|
+
e->pos += 1 + (size_t)r + 1;
|
|
1142
|
+
continue;
|
|
1143
|
+
}
|
|
1144
|
+
if (c == '#' && e_comment_at(e)) {
|
|
1145
|
+
e_skip_to_eol(e);
|
|
1146
|
+
continue;
|
|
1147
|
+
}
|
|
1148
|
+
if (c == '\n' || c == '\r') {
|
|
1149
|
+
e_line_done(e, e->pos);
|
|
1150
|
+
continue;
|
|
1151
|
+
}
|
|
1152
|
+
if (c == '[') {
|
|
1153
|
+
if (n >= e->max_depth) {
|
|
1154
|
+
return e_fail(e, YEP_ERR_DEPTH, e->pos);
|
|
1155
|
+
}
|
|
1156
|
+
stk[n++] = 0;
|
|
1157
|
+
} else if (c == '{') {
|
|
1158
|
+
if (n >= e->max_depth) {
|
|
1159
|
+
return e_fail(e, YEP_ERR_DEPTH, e->pos);
|
|
1160
|
+
}
|
|
1161
|
+
stk[n++] = 1;
|
|
1162
|
+
} else if (c == ']') {
|
|
1163
|
+
if (stk[n - 1] != 0) {
|
|
1164
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
|
|
1165
|
+
}
|
|
1166
|
+
n--;
|
|
1167
|
+
} else if (c == '}') {
|
|
1168
|
+
if (stk[n - 1] != 1) {
|
|
1169
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
|
|
1170
|
+
}
|
|
1171
|
+
n--;
|
|
1172
|
+
}
|
|
1173
|
+
e->pos++;
|
|
1174
|
+
}
|
|
1175
|
+
if (n != 0) {
|
|
1176
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->len);
|
|
1177
|
+
}
|
|
1178
|
+
return 0;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
/* Parses one flow node at the cursor into *ev. Return: 1 node parsed,
|
|
1182
|
+
* 0 no node (separator/close next), 2 nested opener, -1 error. */
|
|
1183
|
+
static int e_flow_node(yep_engine* e, yep_event* ev, int keyish) {
|
|
1184
|
+
(void)keyish;
|
|
1185
|
+
if (e_flow_ws(e) != 0) {
|
|
1186
|
+
return -1;
|
|
1187
|
+
}
|
|
1188
|
+
if (e->pos >= e->len) {
|
|
1189
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
|
|
1190
|
+
}
|
|
1191
|
+
unsigned char c = (unsigned char)e->p[e->pos];
|
|
1192
|
+
if (c == '[' || c == '{') {
|
|
1193
|
+
return 2;
|
|
1194
|
+
}
|
|
1195
|
+
if (c == ']' || c == '}' || c == ',') {
|
|
1196
|
+
return 0; /* separator / close */
|
|
1197
|
+
}
|
|
1198
|
+
if (c == ':') {
|
|
1199
|
+
/* ':' separates when followed by blank/EOL/flow indicator, or
|
|
1200
|
+
* directly after a flow close ("{a: b}:c" — JSON-style); ":x" is
|
|
1201
|
+
* a plain scalar starting with a colon */
|
|
1202
|
+
size_t n2 = e->pos + 1;
|
|
1203
|
+
if (n2 >= e->len || e->p[n2] == ' ' || e->p[n2] == '\n' || e->p[n2] == '\r' ||
|
|
1204
|
+
yep_ct_is((unsigned char)e->p[n2], YEP_CT_FLOW_IND) ||
|
|
1205
|
+
(e->pos > 0 && (e->p[e->pos - 1] == ']' || e->p[e->pos - 1] == '}'))) {
|
|
1206
|
+
return 0;
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
e_event_init(ev, YEP_EV_SCALAR);
|
|
1211
|
+
ev->line = e->line;
|
|
1212
|
+
ev->col = e_col(e, e->pos) + 1;
|
|
1213
|
+
yep_view anchor = {0}, tag = {0};
|
|
1214
|
+
e_props(e, &anchor, &tag);
|
|
1215
|
+
ev->anchor = anchor;
|
|
1216
|
+
ev->tag = tag;
|
|
1217
|
+
if (!yep_view_is_empty(anchor)) {
|
|
1218
|
+
ev->anchor_id = anchor_define(e, anchor);
|
|
1219
|
+
}
|
|
1220
|
+
if (e_flow_ws(e) != 0) {
|
|
1221
|
+
return -1;
|
|
1222
|
+
}
|
|
1223
|
+
c = (unsigned char)e->p[e->pos];
|
|
1224
|
+
if ((c == ',' || c == ']' || c == '}' || c == ':') &&
|
|
1225
|
+
(!yep_view_is_empty(anchor) || !yep_view_is_empty(tag))) {
|
|
1226
|
+
ev->implicit = 1; /* properties with no node: a tagged null */
|
|
1227
|
+
return 1;
|
|
1228
|
+
}
|
|
1229
|
+
if (c == '"' || c == '\'') {
|
|
1230
|
+
ev->implicit = 0;
|
|
1231
|
+
return e_quoted_floor(e, ev, 0, 0) == 0 ? 1 : -1;
|
|
1232
|
+
}
|
|
1233
|
+
if (c == '*') {
|
|
1234
|
+
return e_alias(e, ev) == 0 ? 1 : -1;
|
|
1235
|
+
}
|
|
1236
|
+
if (c == '[' || c == '{') {
|
|
1237
|
+
return 2;
|
|
1238
|
+
}
|
|
1239
|
+
/* plain: may span lines (folded); a leading non-structural ':' is
|
|
1240
|
+
* part of the scalar ("::x" per the suite) — keep it in the first
|
|
1241
|
+
* piece by scanning from the colon itself */
|
|
1242
|
+
const char* piece_start_fix = e->p + e->pos;
|
|
1243
|
+
if ((unsigned char)*piece_start_fix == ':') {
|
|
1244
|
+
e->pos++;
|
|
1245
|
+
if (e_flow_ws(e) != 0) {
|
|
1246
|
+
return -1;
|
|
1247
|
+
}
|
|
1248
|
+
if (e->pos >= e->len) {
|
|
1249
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
|
|
1250
|
+
}
|
|
1251
|
+
/* rescan from the colon so the piece includes it */
|
|
1252
|
+
e->pos = (size_t)(piece_start_fix - e->p);
|
|
1253
|
+
}
|
|
1254
|
+
e->fold_n = 0;
|
|
1255
|
+
int crossed_break = 0;
|
|
1256
|
+
for (;;) {
|
|
1257
|
+
yep_span s = yep_scan_plain(e->p, e->len, e->pos, 1);
|
|
1258
|
+
if (s.end == s.start && e->fold_n == 0) {
|
|
1259
|
+
return 0; /* empty token: separator/close comes next */
|
|
1260
|
+
}
|
|
1261
|
+
if (e->fold_n >= YEP_MAX_FOLD_LINES) {
|
|
1262
|
+
return e_fail(e, YEP_ERR_MEMORY, e->pos);
|
|
1263
|
+
}
|
|
1264
|
+
if (s.end > s.start) {
|
|
1265
|
+
e->fold[e->fold_n].content.p = e->p + s.start;
|
|
1266
|
+
e->fold[e->fold_n].content.len = s.end - s.start;
|
|
1267
|
+
e->fold[e->fold_n].breaks_before = (e->fold_n == 0) ? 0 : 1;
|
|
1268
|
+
e->fold_n++;
|
|
1269
|
+
}
|
|
1270
|
+
if (s.term == YEP_TERM_EOL || s.term == YEP_TERM_COMMENT) {
|
|
1271
|
+
e->pos = s.end;
|
|
1272
|
+
if (s.term == YEP_TERM_COMMENT) {
|
|
1273
|
+
e_skip_to_eol(e);
|
|
1274
|
+
break; /* a comment ends the scalar: the next token is new */
|
|
1275
|
+
}
|
|
1276
|
+
e_line_done(e, e->pos);
|
|
1277
|
+
crossed_break = 1;
|
|
1278
|
+
{
|
|
1279
|
+
yep_line_info cl = e_line_info_here(e);
|
|
1280
|
+
if (cl.flags & YEP_LF_COMMENT) {
|
|
1281
|
+
e_line_done(e, cl.end);
|
|
1282
|
+
break; /* a comment line ends the scalar too */
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
if (e_flow_ws(e) != 0) {
|
|
1286
|
+
return -1;
|
|
1287
|
+
}
|
|
1288
|
+
if (e->pos < e->len && e->p[e->pos] != ']' && e->p[e->pos] != '}' &&
|
|
1289
|
+
e->p[e->pos] != ',' && !e_comment_at(e) && e->p[e->pos] != '\n' &&
|
|
1290
|
+
e->p[e->pos] != '\r') {
|
|
1291
|
+
continue; /* continuation on a following line */
|
|
1292
|
+
}
|
|
1293
|
+
break; /* at a boundary; pos is on the separator/close */
|
|
1294
|
+
}
|
|
1295
|
+
e->pos = s.end;
|
|
1296
|
+
break;
|
|
1297
|
+
}
|
|
1298
|
+
if (e->fold_n == 1) {
|
|
1299
|
+
ev->value.p = e->fold[0].content.p;
|
|
1300
|
+
ev->value.len = e->fold[0].content.len;
|
|
1301
|
+
ev->borrowed = 1;
|
|
1302
|
+
} else {
|
|
1303
|
+
char* out = yep_fold_plain(e->fold, e->fold_n, e->pool, &ev->value.len);
|
|
1304
|
+
if (out == NULL) {
|
|
1305
|
+
return e_fail(e, YEP_ERR_MEMORY, e->pos);
|
|
1306
|
+
}
|
|
1307
|
+
ev->value.p = out;
|
|
1308
|
+
ev->borrowed = 0;
|
|
1309
|
+
}
|
|
1310
|
+
if (e->fold_n == 1 && e->fold[0].content.len == 1 && e->fold[0].content.p[0] == '-') {
|
|
1311
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos); /* "[-, -]": bare dash entry */
|
|
1312
|
+
}
|
|
1313
|
+
ev->style = YEP_STYLE_PLAIN;
|
|
1314
|
+
ev->implicit = 1;
|
|
1315
|
+
ev->multiline = (e->fold_n > 1) || crossed_break;
|
|
1316
|
+
return 1;
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
/* --------------------------------------------------- JSON fast path */
|
|
1320
|
+
|
|
1321
|
+
/* The JSON-class fast path (TODO.impl/08A): one validating scan of the
|
|
1322
|
+
* span, then bulk event emission — pure-JSON flow collections skip the
|
|
1323
|
+
* general kernel's per-node guards. STRICTLY conservative: anything not
|
|
1324
|
+
* byte-for-byte JSON (tabs, comments, YAML scalars, trailing commas,
|
|
1325
|
+
* indent anomalies under a block parent) returns 0 and the general
|
|
1326
|
+
* kernel handles it, so semantics can never diverge — only speed. */
|
|
1327
|
+
|
|
1328
|
+
#define YEP_JSON_MAX_DEPTH 256
|
|
1329
|
+
|
|
1330
|
+
/* Line/col across a gap: the walk is scan's SSOT (yep_scan_advance_
|
|
1331
|
+
* line) — the DOM direct builder shares it so line/col cannot drift
|
|
1332
|
+
* between the two paths. */
|
|
1333
|
+
static void jx_advance_line(yep_engine* e, size_t* from, size_t to, uint32_t* line,
|
|
1334
|
+
size_t* line_start) {
|
|
1335
|
+
yep_scan_advance_line(e->p, from, to, line, line_start);
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
/* A flow node followed by ':' on the same line is a KEY: the caller
|
|
1339
|
+
* must wrap it in a mapping first — refuse so the general path's
|
|
1340
|
+
* is_key logic runs. Shared by both fast paths (grammar fact). */
|
|
1341
|
+
static int e_flow_key_after(const char* p, size_t len, size_t close) {
|
|
1342
|
+
size_t j = close + 1;
|
|
1343
|
+
while (j < len && p[j] == ' ') {
|
|
1344
|
+
j++;
|
|
1345
|
+
}
|
|
1346
|
+
return j < len && p[j] == ':' &&
|
|
1347
|
+
(j + 1 >= len || p[j + 1] == ' ' || p[j + 1] == '\n' || p[j + 1] == '\r' ||
|
|
1348
|
+
p[j + 1] == '\t');
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
/* Block-level flow lines must out-indent the parent (9C9N): any line
|
|
1352
|
+
* start inside the span at or left of the floor falls back. */
|
|
1353
|
+
static int e_flow_floor_ok(const char* p, size_t open, size_t close, uint16_t floor) {
|
|
1354
|
+
size_t j = open;
|
|
1355
|
+
while (j < close) {
|
|
1356
|
+
if (p[j] == '\n') {
|
|
1357
|
+
size_t k = j + 1;
|
|
1358
|
+
uint32_t ind = 0;
|
|
1359
|
+
while (k < close && p[k] == ' ') {
|
|
1360
|
+
ind++;
|
|
1361
|
+
k++;
|
|
1362
|
+
}
|
|
1363
|
+
if (k < close && p[k] != '\n' && p[k] != '\r' && ind <= floor) {
|
|
1364
|
+
return 0;
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
j++;
|
|
1368
|
+
}
|
|
1369
|
+
return 1;
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
/* Returns 1 = events emitted and consumed (e->pos past the close),
|
|
1373
|
+
* 0 = not JSON-class (fall back), -1 = error set, -2 = sink abort. */
|
|
1374
|
+
static int e_flow_json(yep_engine* e, yep_view anchor, yep_view tag, uint32_t anchor_id) {
|
|
1375
|
+
const char* p = e->p;
|
|
1376
|
+
size_t len = e->len;
|
|
1377
|
+
size_t open_pos = e->pos;
|
|
1378
|
+
if (len - open_pos < 24) {
|
|
1379
|
+
return 0; /* tiny spans: the general kernel is cheaper */
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
/* ---- fused path (TODO.restructure/53): the sink validates AND
|
|
1383
|
+
* builds in one walk; the engine's grammar checks run on the
|
|
1384
|
+
* builder's close, then commit or roll back. ---- */
|
|
1385
|
+
if (e->ev_scopes == 0 && e->sink != NULL && e->sink->on_flow_build != NULL) {
|
|
1386
|
+
size_t bclose = 0;
|
|
1387
|
+
int rc = e->sink->on_flow_build(e->sink->ctx, p, open_pos, len, e->line, e->line_start,
|
|
1388
|
+
anchor, tag, anchor_id, e->max_depth, &bclose);
|
|
1389
|
+
if (rc < 0) {
|
|
1390
|
+
return -2;
|
|
1391
|
+
}
|
|
1392
|
+
if (rc == 0) {
|
|
1393
|
+
return 0; /* not JSON-class: the general kernel */
|
|
1394
|
+
}
|
|
1395
|
+
if (rc == 1) {
|
|
1396
|
+
int single = (memchr(p + open_pos, '\n', bclose - open_pos) == NULL);
|
|
1397
|
+
if (e_flow_key_after(p, len, bclose) ||
|
|
1398
|
+
(e->flow_enforce && !single &&
|
|
1399
|
+
!e_flow_floor_ok(p, open_pos, bclose, e->flow_floor))) {
|
|
1400
|
+
e->sink->on_flow_rollback(e->sink->ctx);
|
|
1401
|
+
return 0;
|
|
1402
|
+
}
|
|
1403
|
+
if (e->sink->on_flow_commit(e->sink->ctx) != 1) {
|
|
1404
|
+
return -2;
|
|
1405
|
+
}
|
|
1406
|
+
size_t from = open_pos;
|
|
1407
|
+
uint32_t ln = e->line;
|
|
1408
|
+
size_t ls = e->line_start;
|
|
1409
|
+
jx_advance_line(e, &from, bclose + 1, &ln, &ls);
|
|
1410
|
+
e->line = ln;
|
|
1411
|
+
e->line_start = ls;
|
|
1412
|
+
e->pos = bclose + 1;
|
|
1413
|
+
e_skip_inline_space(e);
|
|
1414
|
+
return 1;
|
|
1415
|
+
}
|
|
1416
|
+
/* rc == 2: long key — fall to pass 1 + pass 2 for the error */
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
/* ---- pass 1: strict-JSON validation via the shared walker ----
|
|
1420
|
+
* (the grammar lives in scan/json.c; the engine drives it) */
|
|
1421
|
+
uint8_t kind0;
|
|
1422
|
+
size_t close;
|
|
1423
|
+
{
|
|
1424
|
+
yep_json_walk w;
|
|
1425
|
+
yep_json_tok t;
|
|
1426
|
+
yep_json_walk_init(&w, p, len, open_pos, e->max_depth);
|
|
1427
|
+
for (;;) {
|
|
1428
|
+
yep_jw_status st = yep_json_walk_next(&w, &t);
|
|
1429
|
+
if (st == YEP_JW_REJECT) {
|
|
1430
|
+
return 0;
|
|
1431
|
+
}
|
|
1432
|
+
if (st == YEP_JW_DONE) {
|
|
1433
|
+
break;
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
close = w.close;
|
|
1437
|
+
kind0 = w.kind[0];
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
/* One memchr decides the dominant shape: a SINGLE-LINE span (one
|
|
1441
|
+
* flow collection per block line — the common config/API dump)
|
|
1442
|
+
* skips the flow_enforce rescan AND the per-event line bookkeeping
|
|
1443
|
+
* below; both are no-ops without a newline, and pass 1 already
|
|
1444
|
+
* walked these bytes (TODO.restructure/45, Phase B slice). */
|
|
1445
|
+
int single_line = (memchr(p + open_pos, '\n', close - open_pos) == NULL);
|
|
1446
|
+
|
|
1447
|
+
if (e_flow_key_after(p, len, close)) {
|
|
1448
|
+
return 0;
|
|
1449
|
+
}
|
|
1450
|
+
if (e->flow_enforce && !single_line && !e_flow_floor_ok(p, open_pos, close, e->flow_floor)) {
|
|
1451
|
+
return 0;
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
/* ---- pass 2: emit events from the validated span ---- */
|
|
1455
|
+
uint32_t cur_line = e->line;
|
|
1456
|
+
size_t cur_ls = e->line_start;
|
|
1457
|
+
size_t cur_scan = e->pos; /* unscanned region starts at the span */
|
|
1458
|
+
{
|
|
1459
|
+
yep_event ev;
|
|
1460
|
+
e_event_init(&ev, kind0 ? YEP_EV_MAP_START : YEP_EV_SEQ_START);
|
|
1461
|
+
ev.flow = 1;
|
|
1462
|
+
ev.anchor = anchor;
|
|
1463
|
+
ev.anchor_id = anchor_id;
|
|
1464
|
+
ev.tag = tag;
|
|
1465
|
+
ev.line = cur_line;
|
|
1466
|
+
ev.col = (uint32_t)(open_pos + 1 - cur_ls) + 1;
|
|
1467
|
+
if (emit_now(e, &ev) != 0) {
|
|
1468
|
+
return -2;
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
uint8_t stk[YEP_JSON_MAX_DEPTH]; /* 0 seq, 1 map; key-pending per frame */
|
|
1472
|
+
uint8_t kpending[YEP_JSON_MAX_DEPTH];
|
|
1473
|
+
int sd = 1;
|
|
1474
|
+
stk[0] = kind0;
|
|
1475
|
+
kpending[0] = 0;
|
|
1476
|
+
size_t i = open_pos + 1;
|
|
1477
|
+
for (;;) {
|
|
1478
|
+
while (i < close && (p[i] == ' ' || p[i] == '\n' || p[i] == '\r')) {
|
|
1479
|
+
i++;
|
|
1480
|
+
}
|
|
1481
|
+
char c = p[i];
|
|
1482
|
+
if (c == ']' || c == '}') {
|
|
1483
|
+
yep_event ev;
|
|
1484
|
+
e_event_init(&ev, stk[sd - 1] ? YEP_EV_MAP_END : YEP_EV_SEQ_END);
|
|
1485
|
+
/* flow=1 only on START events (kernel convention: the style
|
|
1486
|
+
* belongs to the opening bracket, END events stay plain) */
|
|
1487
|
+
if (!single_line)
|
|
1488
|
+
jx_advance_line(e, &cur_scan, i, &cur_line, &cur_ls);
|
|
1489
|
+
ev.line = cur_line;
|
|
1490
|
+
ev.col = (uint32_t)(i - cur_ls) + 1;
|
|
1491
|
+
if (emit_now(e, &ev) != 0) {
|
|
1492
|
+
return -2;
|
|
1493
|
+
}
|
|
1494
|
+
sd--;
|
|
1495
|
+
if (sd == 0) {
|
|
1496
|
+
break;
|
|
1497
|
+
}
|
|
1498
|
+
i++;
|
|
1499
|
+
continue;
|
|
1500
|
+
}
|
|
1501
|
+
if (c == '{' || c == '[') {
|
|
1502
|
+
if (stk[sd - 1] && kpending[sd - 1] == 2) {
|
|
1503
|
+
kpending[sd - 1] = 0; /* the value of the pending pair */
|
|
1504
|
+
}
|
|
1505
|
+
yep_event ev;
|
|
1506
|
+
e_event_init(&ev, c == '[' ? YEP_EV_SEQ_START : YEP_EV_MAP_START);
|
|
1507
|
+
ev.flow = 1;
|
|
1508
|
+
if (!single_line)
|
|
1509
|
+
jx_advance_line(e, &cur_scan, i, &cur_line, &cur_ls);
|
|
1510
|
+
ev.line = cur_line;
|
|
1511
|
+
ev.col = (uint32_t)(i + 1 - cur_ls) + 1;
|
|
1512
|
+
if (emit_now(e, &ev) != 0) {
|
|
1513
|
+
return -2;
|
|
1514
|
+
}
|
|
1515
|
+
stk[sd] = (c == '[') ? 0 : 1;
|
|
1516
|
+
kpending[sd] = 0;
|
|
1517
|
+
sd++;
|
|
1518
|
+
i++;
|
|
1519
|
+
continue;
|
|
1520
|
+
}
|
|
1521
|
+
if (c == ',' || c == ':') {
|
|
1522
|
+
i++;
|
|
1523
|
+
continue;
|
|
1524
|
+
}
|
|
1525
|
+
/* scalar: quoted, number, or literal */
|
|
1526
|
+
yep_event ev;
|
|
1527
|
+
e_event_init(&ev, YEP_EV_SCALAR);
|
|
1528
|
+
if (!single_line)
|
|
1529
|
+
jx_advance_line(e, &cur_scan, i, &cur_line, &cur_ls);
|
|
1530
|
+
ev.line = cur_line;
|
|
1531
|
+
ev.col = (uint32_t)(i - cur_ls) + 1;
|
|
1532
|
+
size_t vstart = i;
|
|
1533
|
+
size_t raw_end = i; /* span end (past the closing quote for strings) */
|
|
1534
|
+
if (c == '"') {
|
|
1535
|
+
int he = 0;
|
|
1536
|
+
size_t vclose;
|
|
1537
|
+
if (!yep_json_string(p, close + 1, &i, &vclose, &he)) {
|
|
1538
|
+
return -1; /* validated: cannot happen */
|
|
1539
|
+
}
|
|
1540
|
+
size_t vend = vclose; /* the closing quote */
|
|
1541
|
+
raw_end = vclose + 1;
|
|
1542
|
+
ev.style = YEP_STYLE_DOUBLE_QUOTED;
|
|
1543
|
+
if (he) {
|
|
1544
|
+
ev.multiline = 0;
|
|
1545
|
+
char* out = yep_finish_double(p, (uint32_t)(vstart + 1), (uint32_t)vend, 0, e->pool,
|
|
1546
|
+
&ev.value.len);
|
|
1547
|
+
if (out == NULL) {
|
|
1548
|
+
return e_fail(e, YEP_ERR_MEMORY, vstart);
|
|
1549
|
+
}
|
|
1550
|
+
ev.value.p = out;
|
|
1551
|
+
ev.borrowed = 0;
|
|
1552
|
+
} else {
|
|
1553
|
+
ev.value.p = p + vstart + 1;
|
|
1554
|
+
ev.value.len = (uint32_t)(vend - vstart - 1);
|
|
1555
|
+
ev.borrowed = 1;
|
|
1556
|
+
}
|
|
1557
|
+
} else if (c == '-' || (c >= '0' && c <= '9')) {
|
|
1558
|
+
i++;
|
|
1559
|
+
while (i < close && ((p[i] >= '0' && p[i] <= '9') || p[i] == '.' || p[i] == 'e' ||
|
|
1560
|
+
p[i] == 'E' || p[i] == '+' || p[i] == '-')) {
|
|
1561
|
+
i++;
|
|
1562
|
+
}
|
|
1563
|
+
ev.value.p = p + vstart;
|
|
1564
|
+
ev.value.len = (uint32_t)(i - vstart);
|
|
1565
|
+
ev.borrowed = 1;
|
|
1566
|
+
ev.style = YEP_STYLE_PLAIN;
|
|
1567
|
+
ev.implicit = 1;
|
|
1568
|
+
} else {
|
|
1569
|
+
/* literals: validated in pass 1; walk their exact length */
|
|
1570
|
+
i += (c == 't') ? 4 : (c == 'f') ? 5 : 4;
|
|
1571
|
+
ev.value.p = p + vstart;
|
|
1572
|
+
ev.value.len = (uint32_t)(i - vstart);
|
|
1573
|
+
ev.borrowed = 1;
|
|
1574
|
+
ev.style = YEP_STYLE_PLAIN;
|
|
1575
|
+
ev.implicit = 1;
|
|
1576
|
+
}
|
|
1577
|
+
/* a map's first scalar of a pair is a KEY: the 1024-character
|
|
1578
|
+
* simple-key limit applies (raw span, quotes included) */
|
|
1579
|
+
if (stk[sd - 1] && kpending[sd - 1] == 0) {
|
|
1580
|
+
size_t kspan = raw_end - vstart;
|
|
1581
|
+
if (kspan > YEP_MAX_SIMPLE_KEY) {
|
|
1582
|
+
return e_fail(e, YEP_ERR_KEY_TOO_LONG, vstart);
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
if (emit_now(e, &ev) != 0) {
|
|
1586
|
+
return -2;
|
|
1587
|
+
}
|
|
1588
|
+
if (stk[sd - 1] && p[i] == ':' && kpending[sd - 1] == 0) {
|
|
1589
|
+
kpending[sd - 1] = 2; /* key seen; the next scalar/collection is the value */
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
/* engine position: past the close; line state reflects the span */
|
|
1594
|
+
jx_advance_line(e, &cur_scan, close + 1, &cur_line, &cur_ls);
|
|
1595
|
+
e->line = cur_line;
|
|
1596
|
+
e->line_start = cur_ls;
|
|
1597
|
+
e->pos = close + 1;
|
|
1598
|
+
e_skip_inline_space(e);
|
|
1599
|
+
return 1;
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
/* Iterative flow kernel; emits events for the whole collection. */
|
|
1603
|
+
/* Initializes one flow frame (SSOT: every field, every push site). */
|
|
1604
|
+
static void e_flow_frame_init(void* stv, int idx, int kind, int pending_key) {
|
|
1605
|
+
typedef struct {
|
|
1606
|
+
uint8_t kind;
|
|
1607
|
+
uint8_t pending_key;
|
|
1608
|
+
uint8_t sep;
|
|
1609
|
+
uint8_t just_closed;
|
|
1610
|
+
uint8_t pair_open;
|
|
1611
|
+
uint8_t pair_value;
|
|
1612
|
+
uint8_t pair_wrap;
|
|
1613
|
+
int32_t buf_from;
|
|
1614
|
+
uint32_t entry_line;
|
|
1615
|
+
uint32_t entries;
|
|
1616
|
+
} e_flow_frame;
|
|
1617
|
+
e_flow_frame* st = (e_flow_frame*)stv;
|
|
1618
|
+
st[idx].kind = (uint8_t)kind;
|
|
1619
|
+
st[idx].pending_key = (uint8_t)pending_key;
|
|
1620
|
+
st[idx].sep = 0;
|
|
1621
|
+
st[idx].just_closed = 0;
|
|
1622
|
+
st[idx].pair_open = 0;
|
|
1623
|
+
st[idx].pair_value = 0;
|
|
1624
|
+
st[idx].pair_wrap = 0;
|
|
1625
|
+
st[idx].buf_from = -1;
|
|
1626
|
+
st[idx].entry_line = 0;
|
|
1627
|
+
st[idx].entries = 0;
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
static int e_flow(yep_engine* e, yep_view anchor, yep_view tag, uint32_t anchor_id) {
|
|
1631
|
+
struct {
|
|
1632
|
+
uint8_t kind; /* 0 seq, 1 map */
|
|
1633
|
+
uint8_t pending_key; /* map: 1 key seen awaiting ':' value; 2 key emitted w/o ':' */
|
|
1634
|
+
uint8_t sep; /* a ',' was consumed and no entry followed yet */
|
|
1635
|
+
uint8_t just_closed; /* a nested collection closed here (key candidate) */
|
|
1636
|
+
uint8_t pair_open; /* seq: a buffered single-pair awaits its MAP_END */
|
|
1637
|
+
uint8_t pair_value; /* seq: the open pair already has its value */
|
|
1638
|
+
uint8_t pair_wrap; /* map frame pushed as a seq single-pair */
|
|
1639
|
+
int32_t buf_from; /* seq: entry events buffered from this index */
|
|
1640
|
+
uint32_t entry_line; /* seq: line where the buffered entry ended */
|
|
1641
|
+
uint32_t entries; /* completed entries (for trailing-comma detection) */
|
|
1642
|
+
} st[YEP_MAX_DEPTH];
|
|
1643
|
+
int n = 0;
|
|
1644
|
+
|
|
1645
|
+
unsigned char open = (unsigned char)e->p[e->pos];
|
|
1646
|
+
e->pos++;
|
|
1647
|
+
yep_event ev;
|
|
1648
|
+
e_event_init(&ev, open == '[' ? YEP_EV_SEQ_START : YEP_EV_MAP_START);
|
|
1649
|
+
ev.flow = 1;
|
|
1650
|
+
ev.anchor = anchor;
|
|
1651
|
+
ev.anchor_id = anchor_id;
|
|
1652
|
+
ev.tag = tag;
|
|
1653
|
+
ev.line = e->line;
|
|
1654
|
+
ev.col = e_col(e, e->pos);
|
|
1655
|
+
if (emit_now(e, &ev) != 0) {
|
|
1656
|
+
return -2;
|
|
1657
|
+
}
|
|
1658
|
+
e_flow_frame_init(st, n, (open == '[') ? 0 : 1, 0);
|
|
1659
|
+
n++;
|
|
1660
|
+
|
|
1661
|
+
for (;;) {
|
|
1662
|
+
e_event_init(&ev, YEP_EV_NONE);
|
|
1663
|
+
if (e->pos == e->line_start && e->pos < e->len) {
|
|
1664
|
+
yep_line_info fl = e_line_info_here(e);
|
|
1665
|
+
if ((fl.flags & YEP_LF_TAB) && !(fl.flags & YEP_LF_BLANK)) {
|
|
1666
|
+
return e_fail(e, YEP_ERR_TAB_IN_INDENT, e->pos); /* Y79Y#4 */
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
if (st[n - 1].kind == 0 && st[n - 1].buf_from < 0 && st[n - 1].pair_open == 0 &&
|
|
1670
|
+
e->pos < e->len && e->p[e->pos] != ',' && e->p[e->pos] != ']' && e->p[e->pos] != '}' &&
|
|
1671
|
+
e->p[e->pos] != ':') {
|
|
1672
|
+
/* a sequence entry may become a single-pair key: defer it */
|
|
1673
|
+
st[n - 1].buf_from = (int32_t)e->ev_buf_n;
|
|
1674
|
+
st[n - 1].entry_line = e->line;
|
|
1675
|
+
e->ev_scopes++;
|
|
1676
|
+
}
|
|
1677
|
+
int keyish = (st[n - 1].kind == 1 && st[n - 1].pending_key == 0);
|
|
1678
|
+
int q_key = 0;
|
|
1679
|
+
int rc;
|
|
1680
|
+
if (e_flow_ws(e) != 0) {
|
|
1681
|
+
return -1;
|
|
1682
|
+
}
|
|
1683
|
+
if (e->pos < e->len && e->pos == e->line_start) {
|
|
1684
|
+
yep_line_info fl = e_line_info_here(e);
|
|
1685
|
+
if (fl.flags & (YEP_LF_DOC_START | YEP_LF_DOC_END | YEP_LF_DIRECTIVE)) {
|
|
1686
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos); /* marker inside flow */
|
|
1687
|
+
}
|
|
1688
|
+
if (fl.flags & YEP_LF_TAB) {
|
|
1689
|
+
return e_fail(e, YEP_ERR_TAB_IN_INDENT, e->pos); /* Y79Y#4 */
|
|
1690
|
+
}
|
|
1691
|
+
if (e->flow_enforce && !(fl.flags & (YEP_LF_BLANK | YEP_LF_COMMENT)) &&
|
|
1692
|
+
fl.indent <= e->flow_floor) {
|
|
1693
|
+
/* a block-level flow's lines out-indent its parent (9C9N) */
|
|
1694
|
+
return e_fail(e, YEP_ERR_BAD_INDENT, e->pos + fl.indent);
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
if (e->pos < e->len && e->p[e->pos] != ',' && e->p[e->pos] != ']' && e->p[e->pos] != '}' &&
|
|
1698
|
+
!(e->p[e->pos] == ':' &&
|
|
1699
|
+
((st[n - 1].kind == 1 && st[n - 1].pending_key == 1) || st[n - 1].just_closed)) &&
|
|
1700
|
+
st[n - 1].entries > 0 && st[n - 1].sep == 0 && !st[n - 1].pair_open &&
|
|
1701
|
+
!(st[n - 1].kind == 1 && st[n - 1].pending_key == 1)) {
|
|
1702
|
+
if (getenv("YEP_TRACE"))
|
|
1703
|
+
fprintf(stderr, "[mc] pos=%c kind=%d ent=%u sep=%u pk=%u jc=%u\n", e->p[e->pos],
|
|
1704
|
+
st[n - 1].kind, st[n - 1].entries, st[n - 1].sep, st[n - 1].pending_key,
|
|
1705
|
+
st[n - 1].just_closed);
|
|
1706
|
+
/* a node without a preceding ',' or ':' (CML9 missing comma);
|
|
1707
|
+
* in a sequence a ':'-led token is a new plain, so it errors */
|
|
1708
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
|
|
1709
|
+
}
|
|
1710
|
+
if (e->pos < e->len && e->p[e->pos] == '?' &&
|
|
1711
|
+
(e->pos + 1 >= e->len || e->p[e->pos + 1] == ' ' || e->p[e->pos + 1] == '\t' ||
|
|
1712
|
+
e->p[e->pos + 1] == '\n' || e->p[e->pos + 1] == '\r' ||
|
|
1713
|
+
yep_ct_is((unsigned char)e->p[e->pos + 1], YEP_CT_FLOW_IND))) {
|
|
1714
|
+
e->pos++;
|
|
1715
|
+
if (e_flow_ws(e) != 0) {
|
|
1716
|
+
return -1;
|
|
1717
|
+
}
|
|
1718
|
+
if (e->pos >= e->len ||
|
|
1719
|
+
(e->p[e->pos] == ':' &&
|
|
1720
|
+
(e->pos + 1 >= e->len || e->p[e->pos + 1] == ' ' || e->p[e->pos + 1] == '\t' ||
|
|
1721
|
+
e->p[e->pos + 1] == '\n' || e->p[e->pos + 1] == '\r' ||
|
|
1722
|
+
yep_ct_is((unsigned char)e->p[e->pos + 1], YEP_CT_FLOW_IND) ||
|
|
1723
|
+
e->p[e->pos + 1] == ',' || e->p[e->pos + 1] == ']' || e->p[e->pos + 1] == '}')) ||
|
|
1724
|
+
e->p[e->pos] == ',' || e->p[e->pos] == ']' || e->p[e->pos] == '}') {
|
|
1725
|
+
/* '? : v' / '?' at the boundary: an EMPTY explicit key */
|
|
1726
|
+
e_event_init(&ev, YEP_EV_SCALAR);
|
|
1727
|
+
ev.implicit = 1;
|
|
1728
|
+
ev.line = e->line;
|
|
1729
|
+
ev.col = e_col(e, e->pos) + 1;
|
|
1730
|
+
rc = 1;
|
|
1731
|
+
q_key = 1;
|
|
1732
|
+
} else {
|
|
1733
|
+
rc = e_flow_node(e, &ev, keyish);
|
|
1734
|
+
q_key = 1;
|
|
1735
|
+
}
|
|
1736
|
+
} else {
|
|
1737
|
+
rc = e_flow_node(e, &ev, keyish);
|
|
1738
|
+
}
|
|
1739
|
+
if (rc == -1) {
|
|
1740
|
+
return -1;
|
|
1741
|
+
}
|
|
1742
|
+
if (rc == 2) {
|
|
1743
|
+
if (n >= e->max_depth) {
|
|
1744
|
+
return e_fail(e, YEP_ERR_DEPTH, e->pos);
|
|
1745
|
+
}
|
|
1746
|
+
st[n - 1].pair_value = 1;
|
|
1747
|
+
unsigned char oc = (unsigned char)e->p[e->pos];
|
|
1748
|
+
e->pos++;
|
|
1749
|
+
yep_view pa = ev.anchor, pt = ev.tag; /* props before the opener */
|
|
1750
|
+
uint32_t paid = ev.anchor_id;
|
|
1751
|
+
e_event_init(&ev, oc == '[' ? YEP_EV_SEQ_START : YEP_EV_MAP_START);
|
|
1752
|
+
ev.flow = 1;
|
|
1753
|
+
ev.anchor = pa;
|
|
1754
|
+
ev.anchor_id = paid;
|
|
1755
|
+
ev.tag = pt;
|
|
1756
|
+
ev.line = e->line;
|
|
1757
|
+
ev.col = e_col(e, e->pos);
|
|
1758
|
+
if (emit_now(e, &ev) != 0) {
|
|
1759
|
+
return -2;
|
|
1760
|
+
}
|
|
1761
|
+
st[n - 1].just_closed = 0;
|
|
1762
|
+
/* the nested collection is an entry of the parent frame; when
|
|
1763
|
+
* it appears in a map's value position it completes the pair */
|
|
1764
|
+
if (st[n - 1].kind == 1 && st[n - 1].pending_key == 1) {
|
|
1765
|
+
st[n - 1].pending_key = 0;
|
|
1766
|
+
}
|
|
1767
|
+
st[n - 1].sep = 0;
|
|
1768
|
+
st[n - 1].entries++;
|
|
1769
|
+
e_flow_frame_init(st, n, (oc == '[') ? 0 : 1, 0);
|
|
1770
|
+
n++;
|
|
1771
|
+
continue;
|
|
1772
|
+
}
|
|
1773
|
+
if (rc == 1) {
|
|
1774
|
+
st[n - 1].sep = 0;
|
|
1775
|
+
st[n - 1].just_closed = 0;
|
|
1776
|
+
st[n - 1].entries++;
|
|
1777
|
+
if (st[n - 1].buf_from >= 0) {
|
|
1778
|
+
st[n - 1].entry_line = e->line; /* node end (pre-colon ws) */
|
|
1779
|
+
}
|
|
1780
|
+
if (st[n - 1].pair_open) {
|
|
1781
|
+
st[n - 1].pair_value = 1; /* the pair's value arrived */
|
|
1782
|
+
}
|
|
1783
|
+
/* key detection: ':' after the node (spaces and line breaks
|
|
1784
|
+
* may precede it: "unquoted : value", "\"foo\"\n: bar").
|
|
1785
|
+
* After a QUOTED node the colon separates JSON-style. */
|
|
1786
|
+
size_t save_pos = e->pos;
|
|
1787
|
+
if (e_flow_ws(e) != 0) {
|
|
1788
|
+
return -1;
|
|
1789
|
+
} /* crosses newlines; restored below when no colon */
|
|
1790
|
+
int quoted = (ev.type == YEP_EV_SCALAR && (ev.style == YEP_STYLE_SINGLE_QUOTED ||
|
|
1791
|
+
ev.style == YEP_STYLE_DOUBLE_QUOTED));
|
|
1792
|
+
int key_pos = (st[n - 1].kind == 1 && st[n - 1].pending_key == 0);
|
|
1793
|
+
int cross_line = 0;
|
|
1794
|
+
for (size_t k = save_pos; k < e->pos; k++) {
|
|
1795
|
+
if (e->p[k] == '\n' || e->p[k] == '\r') {
|
|
1796
|
+
cross_line = 1;
|
|
1797
|
+
break;
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
int colon = (e->pos < e->len && e->p[e->pos] == ':' &&
|
|
1801
|
+
((quoted && !ev.multiline && (!cross_line || st[n - 1].kind == 1)) ||
|
|
1802
|
+
(key_pos && !ev.multiline && !cross_line) || e->pos + 1 >= e->len ||
|
|
1803
|
+
e->p[e->pos + 1] == ' ' || e->p[e->pos + 1] == '\t' ||
|
|
1804
|
+
e->p[e->pos + 1] == '\n' || e->p[e->pos + 1] == '\r' ||
|
|
1805
|
+
yep_ct_is((unsigned char)e->p[e->pos + 1], YEP_CT_FLOW_IND)));
|
|
1806
|
+
if (!colon) {
|
|
1807
|
+
e->pos = save_pos; /* not a key; the caller re-reads */
|
|
1808
|
+
}
|
|
1809
|
+
if (colon && st[n - 1].kind == 1 && st[n - 1].pending_key == 0) {
|
|
1810
|
+
if (!q_key && quoted && ev.value.len + 2 > YEP_MAX_SIMPLE_KEY) {
|
|
1811
|
+
return e_fail(e, YEP_ERR_KEY_TOO_LONG, e->pos);
|
|
1812
|
+
}
|
|
1813
|
+
if (!q_key && !quoted && ev.value.len > YEP_MAX_SIMPLE_KEY) {
|
|
1814
|
+
return e_fail(e, YEP_ERR_KEY_TOO_LONG, e->pos);
|
|
1815
|
+
}
|
|
1816
|
+
e->pos++;
|
|
1817
|
+
st[n - 1].pending_key = 1;
|
|
1818
|
+
if (emit_now(e, &ev) != 0) {
|
|
1819
|
+
return -2;
|
|
1820
|
+
}
|
|
1821
|
+
/* value next; if none before , or } → null */
|
|
1822
|
+
continue;
|
|
1823
|
+
}
|
|
1824
|
+
if (colon && st[n - 1].kind == 0) {
|
|
1825
|
+
if (!q_key && quoted && ev.value.len + 2 > YEP_MAX_SIMPLE_KEY) {
|
|
1826
|
+
return e_fail(e, YEP_ERR_KEY_TOO_LONG, e->pos);
|
|
1827
|
+
}
|
|
1828
|
+
if (!q_key && !quoted && ev.value.len > YEP_MAX_SIMPLE_KEY) {
|
|
1829
|
+
return e_fail(e, YEP_ERR_KEY_TOO_LONG, e->pos);
|
|
1830
|
+
}
|
|
1831
|
+
if (st[n - 1].buf_from >= 0 && !quoted && !q_key &&
|
|
1832
|
+
(save_pos < e->line_start || ev.multiline)) {
|
|
1833
|
+
/* "[a\n: b]": an IMPLICIT key cannot span lines ("? " may) */
|
|
1834
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
|
|
1835
|
+
}
|
|
1836
|
+
/* single-pair mapping inside a sequence: the entry's
|
|
1837
|
+
* events are buffered; wrap them in MAP_START .. MAP_END */
|
|
1838
|
+
e->pos++;
|
|
1839
|
+
{
|
|
1840
|
+
int32_t bf = st[n - 1].buf_from;
|
|
1841
|
+
if (bf >= 0) {
|
|
1842
|
+
st[n - 1].buf_from = -1;
|
|
1843
|
+
e->ev_scopes--;
|
|
1844
|
+
if (e_buf_wrap(e, (uint32_t)bf) != 0) {
|
|
1845
|
+
return -2;
|
|
1846
|
+
}
|
|
1847
|
+
if (e->ev_scopes == 0 && e->ev_buf_n > 0) {
|
|
1848
|
+
int brc = e_buf_send(e, 0);
|
|
1849
|
+
if (brc != 0) {
|
|
1850
|
+
return brc;
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
if (emit_now(e, &ev) != 0) {
|
|
1854
|
+
return -2;
|
|
1855
|
+
}
|
|
1856
|
+
st[n - 1].pair_open = 1;
|
|
1857
|
+
continue;
|
|
1858
|
+
} else {
|
|
1859
|
+
yep_event mk;
|
|
1860
|
+
e_event_init(&mk, YEP_EV_MAP_START);
|
|
1861
|
+
mk.flow = 1;
|
|
1862
|
+
mk.line = ev.line;
|
|
1863
|
+
mk.col = ev.col;
|
|
1864
|
+
if (emit_now(e, &mk) != 0) {
|
|
1865
|
+
return -2;
|
|
1866
|
+
}
|
|
1867
|
+
if (emit_now(e, &ev) != 0) {
|
|
1868
|
+
return -2;
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
st[n - 1].pair_open = 1;
|
|
1872
|
+
}
|
|
1873
|
+
continue; /* the value parses as the next regular node */
|
|
1874
|
+
}
|
|
1875
|
+
if (q_key && st[n - 1].kind == 0) {
|
|
1876
|
+
/* "[? x]": an explicit pair with no ':' — null value */
|
|
1877
|
+
int32_t bf = st[n - 1].buf_from;
|
|
1878
|
+
if (bf >= 0) {
|
|
1879
|
+
st[n - 1].buf_from = -1;
|
|
1880
|
+
e->ev_scopes--;
|
|
1881
|
+
if (e_buf_wrap(e, (uint32_t)bf) != 0) {
|
|
1882
|
+
return -2;
|
|
1883
|
+
}
|
|
1884
|
+
st[n - 1].pair_open = 1;
|
|
1885
|
+
if (e->ev_scopes == 0 && e->ev_buf_n > 0) {
|
|
1886
|
+
int brc = e_buf_send(e, 0);
|
|
1887
|
+
if (brc != 0) {
|
|
1888
|
+
return brc;
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
yep_event nv;
|
|
1892
|
+
e_event_init(&nv, YEP_EV_SCALAR);
|
|
1893
|
+
nv.implicit = 1;
|
|
1894
|
+
if (emit_now(e, &nv) != 0) {
|
|
1895
|
+
return -2;
|
|
1896
|
+
}
|
|
1897
|
+
continue;
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
if (st[n - 1].kind == 1 && st[n - 1].pending_key == 0) {
|
|
1901
|
+
/* bare entry in a flow map: key with null value */
|
|
1902
|
+
st[n - 1].pending_key = 2;
|
|
1903
|
+
if (emit_now(e, &ev) != 0) {
|
|
1904
|
+
return -2;
|
|
1905
|
+
}
|
|
1906
|
+
continue;
|
|
1907
|
+
}
|
|
1908
|
+
if (st[n - 1].kind == 1 && st[n - 1].pending_key == 1) {
|
|
1909
|
+
if (emit_now(e, &ev) != 0) {
|
|
1910
|
+
return -2;
|
|
1911
|
+
}
|
|
1912
|
+
st[n - 1].pending_key = 0;
|
|
1913
|
+
continue;
|
|
1914
|
+
}
|
|
1915
|
+
if (emit_now(e, &ev) != 0) {
|
|
1916
|
+
return -2;
|
|
1917
|
+
}
|
|
1918
|
+
continue;
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
/* rc == 0: separator or close */
|
|
1922
|
+
if (e_flow_ws(e) != 0) {
|
|
1923
|
+
return -1;
|
|
1924
|
+
}
|
|
1925
|
+
if (e->pos >= e->len) {
|
|
1926
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
|
|
1927
|
+
}
|
|
1928
|
+
unsigned char c = (unsigned char)e->p[e->pos];
|
|
1929
|
+
if (c == ',') {
|
|
1930
|
+
e->pos++;
|
|
1931
|
+
if (st[n - 1].pair_open) {
|
|
1932
|
+
if (!st[n - 1].pair_value) {
|
|
1933
|
+
yep_event nv;
|
|
1934
|
+
e_event_init(&nv, YEP_EV_SCALAR);
|
|
1935
|
+
nv.implicit = 1;
|
|
1936
|
+
if (emit_now(e, &nv) != 0) {
|
|
1937
|
+
return -2;
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
yep_event mk;
|
|
1941
|
+
e_event_init(&mk, YEP_EV_MAP_END);
|
|
1942
|
+
if (emit_now(e, &mk) != 0) {
|
|
1943
|
+
return -2;
|
|
1944
|
+
}
|
|
1945
|
+
st[n - 1].pair_open = 0;
|
|
1946
|
+
st[n - 1].pair_value = 0;
|
|
1947
|
+
}
|
|
1948
|
+
if (st[n - 1].buf_from >= 0) {
|
|
1949
|
+
st[n - 1].buf_from = -1;
|
|
1950
|
+
e->ev_scopes--;
|
|
1951
|
+
if (e->ev_scopes == 0 && e->ev_buf_n > 0) {
|
|
1952
|
+
int brc = e_buf_send(e, 0);
|
|
1953
|
+
if (brc != 0) {
|
|
1954
|
+
return brc;
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
if (st[n - 1].sep) {
|
|
1959
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos); /* "{,x}" / "[1,,2]" */
|
|
1960
|
+
}
|
|
1961
|
+
if (st[n - 1].entries == 0 && !st[n - 1].pair_open) {
|
|
1962
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos); /* "[, a]" */
|
|
1963
|
+
}
|
|
1964
|
+
if (st[n - 1].kind == 1 && st[n - 1].pending_key != 0) {
|
|
1965
|
+
/* key without ':' value, or ':' without value: null value */
|
|
1966
|
+
yep_event nv;
|
|
1967
|
+
e_event_init(&nv, YEP_EV_SCALAR);
|
|
1968
|
+
nv.implicit = 1;
|
|
1969
|
+
if (emit_now(e, &nv) != 0) {
|
|
1970
|
+
return -2;
|
|
1971
|
+
}
|
|
1972
|
+
st[n - 1].pending_key = 0;
|
|
1973
|
+
}
|
|
1974
|
+
st[n - 1].sep = 1;
|
|
1975
|
+
st[n - 1].just_closed = 0;
|
|
1976
|
+
continue;
|
|
1977
|
+
}
|
|
1978
|
+
if (c == ']' || c == '}') {
|
|
1979
|
+
while (st[n - 1].pair_wrap) {
|
|
1980
|
+
/* a single-pair wrapper inside a sequence closes on any
|
|
1981
|
+
* bracket ("[ : v ]", CFD4) */
|
|
1982
|
+
if (st[n - 1].pending_key != 0) {
|
|
1983
|
+
yep_event nv;
|
|
1984
|
+
e_event_init(&nv, YEP_EV_SCALAR);
|
|
1985
|
+
nv.implicit = 1;
|
|
1986
|
+
if (emit_now(e, &nv) != 0) {
|
|
1987
|
+
return -2;
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
yep_event mk;
|
|
1991
|
+
e_event_init(&mk, YEP_EV_MAP_END);
|
|
1992
|
+
if (emit_now(e, &mk) != 0) {
|
|
1993
|
+
return -2;
|
|
1994
|
+
}
|
|
1995
|
+
st[n - 1].pair_wrap = 0;
|
|
1996
|
+
n--;
|
|
1997
|
+
if (n == 0) {
|
|
1998
|
+
break; /* wrapper was the last frame: malformed */
|
|
1999
|
+
}
|
|
2000
|
+
st[n - 1].just_closed = 1;
|
|
2001
|
+
st[n - 1].entry_line = e->line;
|
|
2002
|
+
}
|
|
2003
|
+
unsigned char want = st[n - 1].kind ? '}' : ']';
|
|
2004
|
+
if (c != want) {
|
|
2005
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
|
|
2006
|
+
}
|
|
2007
|
+
/* YAML 1.2 allows a trailing separator before the closer
|
|
2008
|
+
* ("[1, ]", "{a: 1, }"); the comma handler already rejected
|
|
2009
|
+
* doubled commas. */
|
|
2010
|
+
if (st[n - 1].kind == 1 && st[n - 1].pending_key != 0) {
|
|
2011
|
+
yep_event nv;
|
|
2012
|
+
e_event_init(&nv, YEP_EV_SCALAR);
|
|
2013
|
+
nv.implicit = 1;
|
|
2014
|
+
if (emit_now(e, &nv) != 0) {
|
|
2015
|
+
return -2;
|
|
2016
|
+
}
|
|
2017
|
+
st[n - 1].pending_key = 0;
|
|
2018
|
+
}
|
|
2019
|
+
if (st[n - 1].pair_open) {
|
|
2020
|
+
if (!st[n - 1].pair_value) {
|
|
2021
|
+
yep_event nv;
|
|
2022
|
+
e_event_init(&nv, YEP_EV_SCALAR);
|
|
2023
|
+
nv.implicit = 1;
|
|
2024
|
+
if (emit_now(e, &nv) != 0) {
|
|
2025
|
+
return -2;
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
yep_event mk;
|
|
2029
|
+
e_event_init(&mk, YEP_EV_MAP_END);
|
|
2030
|
+
if (emit_now(e, &mk) != 0) {
|
|
2031
|
+
return -2;
|
|
2032
|
+
}
|
|
2033
|
+
st[n - 1].pair_open = 0;
|
|
2034
|
+
st[n - 1].pair_value = 0;
|
|
2035
|
+
}
|
|
2036
|
+
if (st[n - 1].buf_from >= 0) {
|
|
2037
|
+
st[n - 1].buf_from = -1;
|
|
2038
|
+
e->ev_scopes--;
|
|
2039
|
+
if (e->ev_scopes == 0 && e->ev_buf_n > 0) {
|
|
2040
|
+
int brc = e_buf_send(e, 0);
|
|
2041
|
+
if (brc != 0) {
|
|
2042
|
+
return brc;
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
e->pos++;
|
|
2047
|
+
e_event_init(&ev, st[n - 1].kind ? YEP_EV_MAP_END : YEP_EV_SEQ_END);
|
|
2048
|
+
if (emit_now(e, &ev) != 0) {
|
|
2049
|
+
return -2;
|
|
2050
|
+
}
|
|
2051
|
+
n--;
|
|
2052
|
+
if (n == 0) {
|
|
2053
|
+
if (e->ev_scopes != 0) {
|
|
2054
|
+
return e_fail(e, YEP_ERR_INTERNAL, e->pos);
|
|
2055
|
+
}
|
|
2056
|
+
if (e->ev_buf_n > 0) {
|
|
2057
|
+
int brc = e_buf_send(e, 0);
|
|
2058
|
+
if (brc != 0) {
|
|
2059
|
+
return brc;
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
e_skip_inline_space(e);
|
|
2063
|
+
return 0;
|
|
2064
|
+
}
|
|
2065
|
+
st[n - 1].just_closed = 1;
|
|
2066
|
+
if (st[n - 1].pair_open) {
|
|
2067
|
+
st[n - 1].pair_value = 1;
|
|
2068
|
+
}
|
|
2069
|
+
if (st[n - 1].buf_from >= 0) {
|
|
2070
|
+
st[n - 1].entry_line = e->line; /* the nested key ended here */
|
|
2071
|
+
}
|
|
2072
|
+
continue;
|
|
2073
|
+
}
|
|
2074
|
+
if (c == ':') {
|
|
2075
|
+
e->pos++;
|
|
2076
|
+
if (st[n - 1].kind == 1 && st[n - 1].pending_key == 2) {
|
|
2077
|
+
/* ':' after a bare key completes it (value next) */
|
|
2078
|
+
st[n - 1].pending_key = 1;
|
|
2079
|
+
continue;
|
|
2080
|
+
}
|
|
2081
|
+
if (st[n - 1].kind == 1 && st[n - 1].just_closed) {
|
|
2082
|
+
/* '[a, b]: v' inside a map — the closed collection is the key */
|
|
2083
|
+
st[n - 1].just_closed = 0;
|
|
2084
|
+
st[n - 1].pending_key = 1;
|
|
2085
|
+
continue;
|
|
2086
|
+
}
|
|
2087
|
+
if (st[n - 1].kind == 1 && st[n - 1].pending_key == 0 &&
|
|
2088
|
+
(st[n - 1].sep || st[n - 1].entries == 0)) {
|
|
2089
|
+
/* '{: x' or '{a, : x' — empty key at a fresh entry */
|
|
2090
|
+
yep_event kv;
|
|
2091
|
+
e_event_init(&kv, YEP_EV_SCALAR);
|
|
2092
|
+
kv.implicit = 1;
|
|
2093
|
+
if (emit_now(e, &kv) != 0) {
|
|
2094
|
+
return -2;
|
|
2095
|
+
}
|
|
2096
|
+
st[n - 1].pending_key = 1;
|
|
2097
|
+
st[n - 1].entries = 1;
|
|
2098
|
+
continue;
|
|
2099
|
+
}
|
|
2100
|
+
if (st[n - 1].kind == 0 && st[n - 1].buf_from >= 0) {
|
|
2101
|
+
if (st[n - 1].entry_line != e->line) {
|
|
2102
|
+
/* the key ended on an earlier line */
|
|
2103
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
|
|
2104
|
+
}
|
|
2105
|
+
/* '[ [a, b]: v' — the buffered collection is the pair key */
|
|
2106
|
+
int32_t bf0 = st[n - 1].buf_from;
|
|
2107
|
+
int was_empty = ((uint32_t)bf0 == e->ev_buf_n);
|
|
2108
|
+
st[n - 1].buf_from = -1;
|
|
2109
|
+
e->ev_scopes--;
|
|
2110
|
+
if (e_buf_wrap(e, (uint32_t)bf0) != 0) {
|
|
2111
|
+
return -2;
|
|
2112
|
+
}
|
|
2113
|
+
if (e->ev_scopes == 0 && e->ev_buf_n > 0) {
|
|
2114
|
+
int brc = e_buf_send(e, 0);
|
|
2115
|
+
if (brc != 0) {
|
|
2116
|
+
return brc;
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
if (was_empty) {
|
|
2120
|
+
yep_event kv; /* "[ : v" — the empty key */
|
|
2121
|
+
e_event_init(&kv, YEP_EV_SCALAR);
|
|
2122
|
+
kv.implicit = 1;
|
|
2123
|
+
if (emit_now(e, &kv) != 0) {
|
|
2124
|
+
return -2;
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
st[n - 1].pair_open = 1;
|
|
2128
|
+
continue; /* value next */
|
|
2129
|
+
}
|
|
2130
|
+
if (st[n - 1].kind == 0 && n >= 1) {
|
|
2131
|
+
/* '[ : v' — single-pair mapping with an empty key */
|
|
2132
|
+
if (n >= e->max_depth) {
|
|
2133
|
+
return e_fail(e, YEP_ERR_DEPTH, e->pos);
|
|
2134
|
+
}
|
|
2135
|
+
yep_event mk;
|
|
2136
|
+
e_event_init(&mk, YEP_EV_MAP_START);
|
|
2137
|
+
mk.flow = 1;
|
|
2138
|
+
if (emit_now(e, &mk) != 0) {
|
|
2139
|
+
return -2;
|
|
2140
|
+
}
|
|
2141
|
+
yep_event kv;
|
|
2142
|
+
e_event_init(&kv, YEP_EV_SCALAR);
|
|
2143
|
+
kv.implicit = 1;
|
|
2144
|
+
if (emit_now(e, &kv) != 0) {
|
|
2145
|
+
return -2;
|
|
2146
|
+
}
|
|
2147
|
+
st[n - 1].sep = 0;
|
|
2148
|
+
st[n - 1].entries++;
|
|
2149
|
+
e_flow_frame_init(st, n, 1, 1);
|
|
2150
|
+
st[n].pair_wrap = 1; /* the seq single-pair wrapper */
|
|
2151
|
+
n++;
|
|
2152
|
+
continue;
|
|
2153
|
+
}
|
|
2154
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
|
|
2155
|
+
}
|
|
2156
|
+
/* any other stray byte */
|
|
2157
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
/* -------------------------------------------------------- block machine */
|
|
2162
|
+
|
|
2163
|
+
/* -------------------------------------------- classified fast arms (49) */
|
|
2164
|
+
/* Dispatch decided once per line from scan's shape facts. Each arm
|
|
2165
|
+
* mirrors the general chain's emission sequence exactly; returns
|
|
2166
|
+
* 1 = node complete, 0 = not applicable, <0 error to propagate. */
|
|
2167
|
+
|
|
2168
|
+
/* Plain (optionally anchored) value: emit, fold continuations, land at
|
|
2169
|
+
* the line end. root_ctx is 0 — every arm has pushed a frame. */
|
|
2170
|
+
static int e_shape_plain_value(yep_engine* e, const yep_line_shape* sh, uint16_t floor,
|
|
2171
|
+
yep_view anchor, uint32_t anchor_id) {
|
|
2172
|
+
yep_event ev;
|
|
2173
|
+
e_event_init(&ev, YEP_EV_SCALAR);
|
|
2174
|
+
ev.anchor = anchor;
|
|
2175
|
+
ev.anchor_id = anchor_id;
|
|
2176
|
+
ev.value.p = e->p + sh->val_span.start;
|
|
2177
|
+
ev.value.len = sh->val_span.end - sh->val_span.start;
|
|
2178
|
+
ev.borrowed = 1;
|
|
2179
|
+
ev.style = YEP_STYLE_PLAIN;
|
|
2180
|
+
ev.implicit = 1;
|
|
2181
|
+
ev.line = e->line;
|
|
2182
|
+
ev.col = e_col(e, sh->val_start) + 1;
|
|
2183
|
+
e->pos = sh->val_span.end;
|
|
2184
|
+
if (e_plain_multiline(e, sh->val_span, floor, &ev, 0) != 0) {
|
|
2185
|
+
return -1;
|
|
2186
|
+
}
|
|
2187
|
+
if (e->fold_n == 1 && sh->val_span.term == YEP_TERM_COMMENT) {
|
|
2188
|
+
e_skip_to_eol(e); /* single line ending in a comment */
|
|
2189
|
+
}
|
|
2190
|
+
/* Dash-item fast path (TODO.restructure/78): the prepared value
|
|
2191
|
+
* is offered whole — the same contract as the pair batch; the
|
|
2192
|
+
* top frame is the sequence e_open_seq just opened/continued. */
|
|
2193
|
+
if (e->sink != NULL && e->sink->on_block_item != NULL) {
|
|
2194
|
+
yep_block_value v;
|
|
2195
|
+
memset(&v, 0, sizeof(v));
|
|
2196
|
+
v.cls = YEP_LVAL_PLAIN;
|
|
2197
|
+
v.borrowed = ev.borrowed;
|
|
2198
|
+
v.value = ev.value;
|
|
2199
|
+
int built = e->sink->on_block_item(e->sink->ctx, &v);
|
|
2200
|
+
if (built < 0) {
|
|
2201
|
+
return -2;
|
|
2202
|
+
}
|
|
2203
|
+
if (built == 1) {
|
|
2204
|
+
return 1;
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
return emit_now(e, &ev) == 0 ? 1 : -2;
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2210
|
+
/* Flow value: the JSON fast path first; anything else restores the
|
|
2211
|
+
* cursor and runs the value chain (which re-derives the flow arms). */
|
|
2212
|
+
static int e_shape_flow_value(yep_engine* e, const yep_line_shape* sh, uint16_t floor,
|
|
2213
|
+
yep_ctx next) {
|
|
2214
|
+
yep_view none = {NULL, 0};
|
|
2215
|
+
e->pos = sh->val_start;
|
|
2216
|
+
e->flow_floor = floor;
|
|
2217
|
+
e->flow_enforce = (e->depth > 0);
|
|
2218
|
+
int fast = e_flow_json(e, none, none, 0);
|
|
2219
|
+
e->flow_enforce = 0;
|
|
2220
|
+
if (fast == -2) {
|
|
2221
|
+
return -2;
|
|
2222
|
+
}
|
|
2223
|
+
if (fast == 1) {
|
|
2224
|
+
if (!e_at_eol(e)) {
|
|
2225
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos); /* "k: [1] x" */
|
|
2226
|
+
}
|
|
2227
|
+
return 1;
|
|
2228
|
+
}
|
|
2229
|
+
e->pos = sh->val_start;
|
|
2230
|
+
int rc = e_parse_value(e, next, floor);
|
|
2231
|
+
return rc == 0 ? 1 : rc;
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
/* Alias value; a ':' after the name is a compact key under a dash and
|
|
2235
|
+
* the error the general path raises under a key. */
|
|
2236
|
+
static int e_shape_alias_value(yep_engine* e, const yep_line_shape* sh, int under_key) {
|
|
2237
|
+
yep_view none = {NULL, 0};
|
|
2238
|
+
e->pos = sh->val_start;
|
|
2239
|
+
yep_event ev;
|
|
2240
|
+
e_event_init(&ev, YEP_EV_SCALAR);
|
|
2241
|
+
ev.line = e->line;
|
|
2242
|
+
ev.col = e_col(e, e->pos) + 1;
|
|
2243
|
+
if (e_alias(e, &ev) != 0) {
|
|
2244
|
+
return -1;
|
|
2245
|
+
}
|
|
2246
|
+
if (e_colon_at(e, e->pos)) {
|
|
2247
|
+
if (under_key || ev.multiline) {
|
|
2248
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
|
|
2249
|
+
}
|
|
2250
|
+
if (e_simple_key_ok(e, sh->val_start, e->pos) != 0) {
|
|
2251
|
+
return -1;
|
|
2252
|
+
}
|
|
2253
|
+
uint16_t key_col = (uint16_t)e_col(e, sh->val_start);
|
|
2254
|
+
int rc = e_open_map(e, key_col, e->line, key_col + 1, none, none, 0, 0);
|
|
2255
|
+
if (rc != 0) {
|
|
2256
|
+
return rc;
|
|
2257
|
+
}
|
|
2258
|
+
if (emit_now(e, &ev) != 0) {
|
|
2259
|
+
return -2;
|
|
2260
|
+
}
|
|
2261
|
+
e->pos++; /* ':' */
|
|
2262
|
+
rc = e_parse_value(e, YEP_CTX_AFTER_COLON, key_col);
|
|
2263
|
+
return rc == 0 ? 1 : rc;
|
|
2264
|
+
}
|
|
2265
|
+
if (!e_at_eol(e)) {
|
|
2266
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
|
|
2267
|
+
}
|
|
2268
|
+
return emit_now(e, &ev) == 0 ? 1 : -2;
|
|
2269
|
+
}
|
|
2270
|
+
|
|
2271
|
+
/* The classified line's key event — constructed ONLY on paths that
|
|
2272
|
+
* emit it: the pair path takes the whole line without events, and
|
|
2273
|
+
* building kv up front discarded ~48 stores per classified line
|
|
2274
|
+
* (TODO.restructure/63 phase 1). */
|
|
2275
|
+
static void e_key_event(const yep_engine* e, const yep_line_shape* sh, uint16_t key_col,
|
|
2276
|
+
uint32_t line, yep_event* kv) {
|
|
2277
|
+
e_event_init(kv, YEP_EV_SCALAR);
|
|
2278
|
+
kv->style = YEP_STYLE_PLAIN;
|
|
2279
|
+
kv->implicit = 1;
|
|
2280
|
+
kv->value.p = e->p + sh->key_start;
|
|
2281
|
+
kv->value.len = sh->key_end - sh->key_start;
|
|
2282
|
+
kv->borrowed = 1;
|
|
2283
|
+
kv->line = line;
|
|
2284
|
+
kv->col = key_col + 1;
|
|
2285
|
+
}
|
|
2286
|
+
|
|
2287
|
+
static int e_classified(yep_engine* e, uint16_t floor_col) {
|
|
2288
|
+
yep_line_info li = e_line_info_here(e);
|
|
2289
|
+
if (e->pos != e->line_start + li.indent) {
|
|
2290
|
+
return 0; /* mid-line entry: the chain keeps its dispatch */
|
|
2291
|
+
}
|
|
2292
|
+
const yep_line_shape* sh = e_shape_here(e);
|
|
2293
|
+
if (sh->val == YEP_LVAL_NONE) {
|
|
2294
|
+
return 0; /* classified line, unclassified value: bail whole */
|
|
2295
|
+
}
|
|
2296
|
+
yep_view none = {NULL, 0};
|
|
2297
|
+
|
|
2298
|
+
if (sh->kind == YEP_LSHAPE_DASH) {
|
|
2299
|
+
switch (sh->val) {
|
|
2300
|
+
case YEP_LVAL_EMPTY:
|
|
2301
|
+
case YEP_LVAL_FLOW:
|
|
2302
|
+
case YEP_LVAL_ALIAS:
|
|
2303
|
+
case YEP_LVAL_PLAIN:
|
|
2304
|
+
break; /* the anchored entry and anything else: general chain */
|
|
2305
|
+
default:
|
|
2306
|
+
return 0;
|
|
2307
|
+
}
|
|
2308
|
+
uint16_t col = (uint16_t)e_col(e, e->pos);
|
|
2309
|
+
int rc = e_open_seq(e, col, e->line, col + 1, none, none, 0);
|
|
2310
|
+
if (rc != 0) {
|
|
2311
|
+
return rc;
|
|
2312
|
+
}
|
|
2313
|
+
switch (sh->val) {
|
|
2314
|
+
case YEP_LVAL_EMPTY:
|
|
2315
|
+
e->pos = sh->dash + 1;
|
|
2316
|
+
rc = e_parse_value(e, YEP_CTX_AFTER_DASH, col);
|
|
2317
|
+
return rc == 0 ? 1 : rc;
|
|
2318
|
+
case YEP_LVAL_FLOW:
|
|
2319
|
+
return e_shape_flow_value(e, sh, col, YEP_CTX_AFTER_DASH);
|
|
2320
|
+
case YEP_LVAL_ALIAS:
|
|
2321
|
+
return e_shape_alias_value(e, sh, 0);
|
|
2322
|
+
default:
|
|
2323
|
+
return e_shape_plain_value(e, sh, col, none, 0);
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2326
|
+
|
|
2327
|
+
/* KEY: plain key + ':' — the mapping workhorse line */
|
|
2328
|
+
if (sh->kind != YEP_LSHAPE_KEY) {
|
|
2329
|
+
return 0;
|
|
2330
|
+
}
|
|
2331
|
+
if (e_simple_key_ok(e, sh->key_start, sh->key_end) != 0) {
|
|
2332
|
+
return -1;
|
|
2333
|
+
}
|
|
2334
|
+
uint16_t key_col = (uint16_t)e_col(e, e->pos);
|
|
2335
|
+
/* `key:` with the value on following lines, opening a FRESH map:
|
|
2336
|
+
* the sink may build map+key in one call (TODO.restructure/57) —
|
|
2337
|
+
* the engine then opens its frame silently. Sibling keys (the map
|
|
2338
|
+
* already continues at this column) keep the key-only event. */
|
|
2339
|
+
if (sh->val == YEP_LVAL_EMPTY && e->sink != NULL && e->sink->on_block_open != NULL &&
|
|
2340
|
+
!(e->depth > 0 && e->frames[e->depth - 1].kind == YEP_FRAME_MAP &&
|
|
2341
|
+
e->frames[e->depth - 1].col == key_col)) {
|
|
2342
|
+
yep_view okey = {e->p + sh->key_start, sh->key_end - sh->key_start};
|
|
2343
|
+
int built = e->sink->on_block_open(e->sink->ctx, &okey, e->line, key_col);
|
|
2344
|
+
if (built < 0) {
|
|
2345
|
+
return -2;
|
|
2346
|
+
}
|
|
2347
|
+
if (built == 1) {
|
|
2348
|
+
int orc = e_open_map(e, key_col, e->line, key_col + 1, none, none, 0, 1);
|
|
2349
|
+
if (orc != 0) {
|
|
2350
|
+
return orc;
|
|
2351
|
+
}
|
|
2352
|
+
e->pos = sh->colon + 1;
|
|
2353
|
+
orc = e_parse_value(e, YEP_CTX_AFTER_COLON, key_col);
|
|
2354
|
+
return orc == 0 ? 1 : orc;
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
int rc = e_open_map(e, key_col, e->line, key_col + 1, none, none, 0, 0);
|
|
2358
|
+
if (rc != 0) {
|
|
2359
|
+
return rc;
|
|
2360
|
+
}
|
|
2361
|
+
/* Whole-line fast path (TODO.restructure/54): the sink may build
|
|
2362
|
+
* the key and its classified value as one unit — no events. The
|
|
2363
|
+
* value EVENT is prepared first (folded content, resolved alias,
|
|
2364
|
+
* defined anchor: exactly what the chain below would emit), so a
|
|
2365
|
+
* rejection emits key + prepared value unchanged. */
|
|
2366
|
+
yep_event vv;
|
|
2367
|
+
e_event_init(&vv, YEP_EV_SCALAR);
|
|
2368
|
+
vv.line = e->line;
|
|
2369
|
+
vv.col = e_col(e, sh->val_start) + 1;
|
|
2370
|
+
/* the pair facts are captured BEFORE the fold: e_plain_multiline
|
|
2371
|
+
* advances line/pos across continuations */
|
|
2372
|
+
uint32_t pair_line = e->line;
|
|
2373
|
+
uint16_t pair_val_col = (uint16_t)e_col(e, sh->val_start);
|
|
2374
|
+
yep_block_value v;
|
|
2375
|
+
memset(&v, 0, sizeof(v));
|
|
2376
|
+
v.cls = (uint8_t)sh->val;
|
|
2377
|
+
int prepared = 0;
|
|
2378
|
+
if (sh->val == YEP_LVAL_ALIAS) {
|
|
2379
|
+
e->pos = sh->val_start;
|
|
2380
|
+
if (e_alias(e, &vv) != 0) {
|
|
2381
|
+
return -1; /* undefined alias: the chain's error */
|
|
2382
|
+
}
|
|
2383
|
+
if (e_colon_at(e, e->pos)) {
|
|
2384
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
|
|
2385
|
+
}
|
|
2386
|
+
if (!e_at_eol(e)) {
|
|
2387
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
|
|
2388
|
+
}
|
|
2389
|
+
v.value = vv.value;
|
|
2390
|
+
v.anchor_id = vv.anchor_id;
|
|
2391
|
+
prepared = 1;
|
|
2392
|
+
} else if (sh->val == YEP_LVAL_PLAIN || sh->val == YEP_LVAL_ANCHOR_PLAIN) {
|
|
2393
|
+
if (sh->val == YEP_LVAL_ANCHOR_PLAIN) {
|
|
2394
|
+
v.anchor.p = e->p + sh->val_start + 1;
|
|
2395
|
+
v.anchor.len = sh->anchor_end - sh->val_start - 1;
|
|
2396
|
+
v.anchor_id = anchor_define(e, v.anchor);
|
|
2397
|
+
vv.anchor = v.anchor;
|
|
2398
|
+
vv.anchor_id = v.anchor_id;
|
|
2399
|
+
}
|
|
2400
|
+
vv.value.p = e->p + sh->val_span.start;
|
|
2401
|
+
vv.value.len = sh->val_span.end - sh->val_span.start;
|
|
2402
|
+
vv.borrowed = 1;
|
|
2403
|
+
vv.style = YEP_STYLE_PLAIN;
|
|
2404
|
+
vv.implicit = 1;
|
|
2405
|
+
e->pos = sh->val_span.end;
|
|
2406
|
+
if (e_plain_multiline(e, sh->val_span, key_col, &vv, 0) != 0) {
|
|
2407
|
+
return -1;
|
|
2408
|
+
}
|
|
2409
|
+
if (e->fold_n == 1 && sh->val_span.term == YEP_TERM_COMMENT) {
|
|
2410
|
+
e_skip_to_eol(e); /* single line ending in a comment */
|
|
2411
|
+
}
|
|
2412
|
+
v.value = vv.value;
|
|
2413
|
+
v.borrowed = vv.borrowed;
|
|
2414
|
+
prepared = 1;
|
|
2415
|
+
}
|
|
2416
|
+
|
|
2417
|
+
if (prepared) {
|
|
2418
|
+
if (e->sink != NULL && e->sink->on_block_pair != NULL) {
|
|
2419
|
+
yep_view key = {e->p + sh->key_start, sh->key_end - sh->key_start};
|
|
2420
|
+
int built =
|
|
2421
|
+
e->sink->on_block_pair(e->sink->ctx, &key, &v, pair_line, key_col, pair_val_col);
|
|
2422
|
+
if (built < 0) {
|
|
2423
|
+
return -2;
|
|
2424
|
+
}
|
|
2425
|
+
if (built == 1) {
|
|
2426
|
+
return 1;
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
yep_event kv;
|
|
2430
|
+
e_key_event(e, sh, key_col, pair_line, &kv);
|
|
2431
|
+
if (emit_now(e, &kv) != 0 || emit_now(e, &vv) != 0) {
|
|
2432
|
+
return -2;
|
|
2433
|
+
}
|
|
2434
|
+
return 1;
|
|
2435
|
+
}
|
|
2436
|
+
|
|
2437
|
+
/* EMPTY / FLOW: no prepared pair — the value is not on this line */
|
|
2438
|
+
switch (sh->val) {
|
|
2439
|
+
case YEP_LVAL_EMPTY: {
|
|
2440
|
+
yep_event kv;
|
|
2441
|
+
e_key_event(e, sh, key_col, pair_line, &kv);
|
|
2442
|
+
if (emit_now(e, &kv) != 0) {
|
|
2443
|
+
return -2;
|
|
2444
|
+
}
|
|
2445
|
+
e->pos = sh->colon + 1;
|
|
2446
|
+
rc = e_parse_value(e, YEP_CTX_AFTER_COLON, key_col);
|
|
2447
|
+
return rc == 0 ? 1 : rc;
|
|
2448
|
+
}
|
|
2449
|
+
case YEP_LVAL_FLOW: {
|
|
2450
|
+
yep_event kv;
|
|
2451
|
+
e_key_event(e, sh, key_col, pair_line, &kv);
|
|
2452
|
+
if (emit_now(e, &kv) != 0) {
|
|
2453
|
+
return -2;
|
|
2454
|
+
}
|
|
2455
|
+
return e_shape_flow_value(e, sh, key_col, YEP_CTX_AFTER_COLON);
|
|
2456
|
+
}
|
|
2457
|
+
default:
|
|
2458
|
+
return 0; /* PLAIN/ALIAS/ANCHOR_PLAIN handled above (unreachable) */
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2462
|
+
/* Parses the node at the cursor (content position). Returns 0 ok,
|
|
2463
|
+
* -1 error, -2 sink abort. Collections may remain open (frames).
|
|
2464
|
+
*
|
|
2465
|
+
* Property semantics: same-line props belong to the node that follows
|
|
2466
|
+
* them (a key scalar when the line turns out to be a mapping entry);
|
|
2467
|
+
* pend props (parsed at the parent's value position on an earlier line)
|
|
2468
|
+
* belong to the VALUE node as a whole — a collection START event when
|
|
2469
|
+
* the value is a collection. */
|
|
2470
|
+
static int e_node(yep_engine* e, yep_ctx ctx, uint16_t floor_col) {
|
|
2471
|
+
size_t node_at = e->pos;
|
|
2472
|
+
yep_view pend_a = e->pend_anchor, pend_t = e->pend_tag;
|
|
2473
|
+
uint32_t pend_aid = e->pend_anchor_id;
|
|
2474
|
+
e->pend_anchor.p = NULL;
|
|
2475
|
+
e->pend_anchor.len = 0;
|
|
2476
|
+
e->pend_anchor_id = 0;
|
|
2477
|
+
e->pend_tag.p = NULL;
|
|
2478
|
+
e->pend_tag.len = 0;
|
|
2479
|
+
yep_view anchor = {0}, tag = {0};
|
|
2480
|
+
e_props(e, &anchor, &tag);
|
|
2481
|
+
if (e->err.code != YEP_ERR_NONE) {
|
|
2482
|
+
return -1; /* e_props failed (unresolved tag handle) */
|
|
2483
|
+
}
|
|
2484
|
+
uint32_t anchor_ordinal = 0;
|
|
2485
|
+
if (!yep_view_is_empty(anchor)) {
|
|
2486
|
+
anchor_ordinal = anchor_define(e, anchor);
|
|
2487
|
+
}
|
|
2488
|
+
/* Node-level props for scalar/alias/flow VALUE events: same-line
|
|
2489
|
+
* wins over pend. */
|
|
2490
|
+
yep_view node_a = yep_view_is_empty(anchor) ? pend_a : anchor;
|
|
2491
|
+
yep_view node_t = yep_view_is_empty(tag) ? pend_t : tag;
|
|
2492
|
+
/* the id rides the SAME precedence as the view: same-line props win
|
|
2493
|
+
* over pend — a props-only line followed by its node ("&a\n- x",
|
|
2494
|
+
* "&a\n[b]") carried the pend VIEW with a zero id and the DOM never
|
|
2495
|
+
* bound the ordinal (found by the self-referencing-structures spec:
|
|
2496
|
+
* "--- &id001\n- *id001" failed with an EMPTY error message) */
|
|
2497
|
+
uint32_t node_aid = anchor_ordinal != 0 ? anchor_ordinal : pend_aid;
|
|
2498
|
+
|
|
2499
|
+
/* One dispatch decision per line (TODO.restructure/49): a fresh
|
|
2500
|
+
* line with no pending props and a classified shape takes the fast
|
|
2501
|
+
* arms; every other entry keeps the chain below unchanged. */
|
|
2502
|
+
if ((ctx == YEP_CTX_FRESH || ctx == YEP_CTX_VALUE_LINE) && yep_view_is_empty(pend_a) &&
|
|
2503
|
+
yep_view_is_empty(pend_t) && pend_aid == 0) {
|
|
2504
|
+
int frc = e_classified(e, floor_col);
|
|
2505
|
+
if (frc != 0) {
|
|
2506
|
+
return frc > 0 ? 0 : frc;
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
|
|
2510
|
+
e_skip_inline_space(e);
|
|
2511
|
+
|
|
2512
|
+
yep_event ev;
|
|
2513
|
+
e_event_init(&ev, YEP_EV_SCALAR);
|
|
2514
|
+
ev.anchor = node_a;
|
|
2515
|
+
ev.anchor_id = node_aid;
|
|
2516
|
+
ev.tag = node_t;
|
|
2517
|
+
ev.line = e->line;
|
|
2518
|
+
ev.col = e_col(e, node_at) + 1;
|
|
2519
|
+
|
|
2520
|
+
if (e_at_eol(e)) {
|
|
2521
|
+
/* properties with the value on following lines */
|
|
2522
|
+
e->pend_anchor = node_a;
|
|
2523
|
+
e->pend_anchor_id = anchor_ordinal;
|
|
2524
|
+
e->pend_tag = node_t;
|
|
2525
|
+
e->doc_inline = 0; /* the node starts on a later line */
|
|
2526
|
+
return e_parse_value(e, ctx, floor_col);
|
|
2527
|
+
/* (ctx stays: AFTER_COLON converts to VALUE_LINE at the break) */
|
|
2528
|
+
}
|
|
2529
|
+
|
|
2530
|
+
unsigned char c = (unsigned char)e->p[e->pos];
|
|
2531
|
+
|
|
2532
|
+
if (c == ',' && (!yep_view_is_empty(anchor) || !yep_view_is_empty(tag) ||
|
|
2533
|
+
!yep_view_is_empty(pend_a) || !yep_view_is_empty(pend_t))) {
|
|
2534
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos); /* "!!str, xxx" (block) */
|
|
2535
|
+
}
|
|
2536
|
+
if (c == '-' && (e->pos + 1 >= e->len || e->p[e->pos + 1] == ' ' || e->p[e->pos + 1] == '\t' ||
|
|
2537
|
+
e->p[e->pos + 1] == '\n' || e->p[e->pos + 1] == '\r')) {
|
|
2538
|
+
if (e->pos > 0 && e->p[e->pos - 1] == '\t') {
|
|
2539
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos); /* "-\t-" (Y79Y) */
|
|
2540
|
+
}
|
|
2541
|
+
if (!yep_view_is_empty(anchor) || !yep_view_is_empty(tag)) {
|
|
2542
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos); /* "&a - x" */
|
|
2543
|
+
}
|
|
2544
|
+
uint16_t col = (uint16_t)e_col(e, e->pos);
|
|
2545
|
+
int rc = e_open_seq(e, col, e->line, col + 1, node_a, node_t, node_aid);
|
|
2546
|
+
if (rc != 0) {
|
|
2547
|
+
return rc;
|
|
2548
|
+
}
|
|
2549
|
+
e->pos++; /* '-' */
|
|
2550
|
+
return e_parse_value(e, YEP_CTX_AFTER_DASH, col);
|
|
2551
|
+
}
|
|
2552
|
+
|
|
2553
|
+
if (c == '?' && (e->pos + 1 >= e->len || e->p[e->pos + 1] == ' ' || e->p[e->pos + 1] == '\t' ||
|
|
2554
|
+
e->p[e->pos + 1] == '\n' || e->p[e->pos + 1] == '\r')) {
|
|
2555
|
+
if (ctx == YEP_CTX_AFTER_COLON) {
|
|
2556
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos); /* "key: ? a" */
|
|
2557
|
+
}
|
|
2558
|
+
if (e->pos > 0 && e->p[e->pos - 1] == '\t') {
|
|
2559
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos); /* "?\t-" (Y79Y) */
|
|
2560
|
+
}
|
|
2561
|
+
uint16_t col = (uint16_t)e_col(e, e->pos);
|
|
2562
|
+
int rc = e_open_map(e, col, e->line, col + 1, pend_a, pend_t, pend_aid, 0);
|
|
2563
|
+
if (rc != 0) {
|
|
2564
|
+
return rc;
|
|
2565
|
+
}
|
|
2566
|
+
e->q_map_depth = e->depth;
|
|
2567
|
+
e->pos++;
|
|
2568
|
+
e_skip_inline_space(e);
|
|
2569
|
+
if (e->pos < e->len && e->p[e->pos] == ':' &&
|
|
2570
|
+
(e->pos + 1 >= e->len || e->p[e->pos + 1] == ' ' || e->p[e->pos + 1] == '\t' ||
|
|
2571
|
+
e->p[e->pos + 1] == '\n' || e->p[e->pos + 1] == '\r')) {
|
|
2572
|
+
size_t after = e->pos + 1;
|
|
2573
|
+
while (after < e->len && (e->p[after] == ' ' || e->p[after] == '\t')) {
|
|
2574
|
+
after++;
|
|
2575
|
+
}
|
|
2576
|
+
if (after >= e->len || e->p[after] == '\n' || e->p[after] == '\r' ||
|
|
2577
|
+
(e->p[after] == '#' && after > e->pos + 1)) {
|
|
2578
|
+
/* '? :' at end of line — empty explicit key */
|
|
2579
|
+
yep_event kv;
|
|
2580
|
+
e_event_init(&kv, YEP_EV_SCALAR);
|
|
2581
|
+
kv.style = YEP_STYLE_PLAIN;
|
|
2582
|
+
kv.implicit = 1;
|
|
2583
|
+
if (emit_now(e, &kv) != 0) {
|
|
2584
|
+
return -2;
|
|
2585
|
+
}
|
|
2586
|
+
e->pos++;
|
|
2587
|
+
return e_parse_value(e, YEP_CTX_AFTER_COLON, col);
|
|
2588
|
+
}
|
|
2589
|
+
/* '? : x' — the ':' starts a nested pair: the KEY is that map */
|
|
2590
|
+
}
|
|
2591
|
+
/* the explicit key is parsed as a value (may be empty → null);
|
|
2592
|
+
* its content may ALIGN with the '?' column */
|
|
2593
|
+
e->q_key_pending = 1;
|
|
2594
|
+
{
|
|
2595
|
+
int qrc = e_parse_value(e, YEP_CTX_AFTER_Q, col);
|
|
2596
|
+
if (qrc != 0) {
|
|
2597
|
+
return qrc;
|
|
2598
|
+
}
|
|
2599
|
+
e->q_value_pending = 1; /* until a ':' line supplies the value */
|
|
2600
|
+
return 0;
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
|
|
2604
|
+
if (c == '[' || c == '{') {
|
|
2605
|
+
/* pre-scan: is this flow collection a mapping KEY? (a key must
|
|
2606
|
+
* begin and end on one line — "[23\n]: 42" is not a key) */
|
|
2607
|
+
{
|
|
2608
|
+
/* JSON fast path first: its validating scan already finds the
|
|
2609
|
+
* close, replacing e_skip_flow's pre-scan when it applies. */
|
|
2610
|
+
e->flow_floor = floor_col;
|
|
2611
|
+
e->flow_enforce = (e->depth > 0);
|
|
2612
|
+
int fast = e_flow_json(e, node_a, node_t, anchor_ordinal);
|
|
2613
|
+
e->flow_enforce = 0;
|
|
2614
|
+
if (fast == 1) {
|
|
2615
|
+
if (!e_at_eol(e)) {
|
|
2616
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos); /* "[ a ] ]" */
|
|
2617
|
+
}
|
|
2618
|
+
if (e->depth == 0) {
|
|
2619
|
+
e->last_root_flow = 1; /* "[23\n]: 42" is not a key */
|
|
2620
|
+
}
|
|
2621
|
+
return 0;
|
|
2622
|
+
}
|
|
2623
|
+
if (fast == -2) {
|
|
2624
|
+
return -2;
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
size_t save = e->pos;
|
|
2628
|
+
uint32_t line0 = e->line;
|
|
2629
|
+
if (e_skip_flow(e) != 0) {
|
|
2630
|
+
return -1;
|
|
2631
|
+
}
|
|
2632
|
+
e_skip_inline_space(e);
|
|
2633
|
+
int is_key = e_colon_at(e, e->pos) && e->line == line0;
|
|
2634
|
+
e->pos = save;
|
|
2635
|
+
if (is_key && ctx == YEP_CTX_AFTER_COLON) {
|
|
2636
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
|
|
2637
|
+
}
|
|
2638
|
+
if (is_key) {
|
|
2639
|
+
if (e_simple_key_ok(e, node_at, e->pos) != 0) {
|
|
2640
|
+
return -1;
|
|
2641
|
+
}
|
|
2642
|
+
uint16_t key_col = (uint16_t)e_col(e, node_at);
|
|
2643
|
+
int rc = e_open_map(e, key_col, e->line, key_col + 1, pend_a, pend_t, pend_aid, 0);
|
|
2644
|
+
if (rc != 0) {
|
|
2645
|
+
return rc;
|
|
2646
|
+
}
|
|
2647
|
+
rc = e_flow(e, node_a, node_t, node_aid);
|
|
2648
|
+
if (rc != 0) {
|
|
2649
|
+
return rc;
|
|
2650
|
+
}
|
|
2651
|
+
e->pos++; /* ':' */
|
|
2652
|
+
return e_parse_value(e, YEP_CTX_AFTER_COLON, key_col);
|
|
2653
|
+
}
|
|
2654
|
+
{
|
|
2655
|
+
e->flow_floor = floor_col;
|
|
2656
|
+
e->flow_enforce = (e->depth > 0);
|
|
2657
|
+
int rc = e_flow(e, node_a, node_t, node_aid);
|
|
2658
|
+
e->flow_enforce = 0;
|
|
2659
|
+
if (rc != 0) {
|
|
2660
|
+
return rc;
|
|
2661
|
+
}
|
|
2662
|
+
if (!e_at_eol(e)) {
|
|
2663
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos); /* "[ a ] ]" */
|
|
2664
|
+
}
|
|
2665
|
+
if (e->depth == 0) {
|
|
2666
|
+
e->last_root_flow = 1; /* "[23\n]: 42" is not a key */
|
|
2667
|
+
}
|
|
2668
|
+
return 0;
|
|
2669
|
+
}
|
|
2670
|
+
}
|
|
2671
|
+
|
|
2672
|
+
if (c == '"' || c == '\'') {
|
|
2673
|
+
if (e_quoted_floor(e, &ev, floor_col, e->depth > 0) != 0) {
|
|
2674
|
+
return -1;
|
|
2675
|
+
}
|
|
2676
|
+
if (e_colon_at(e, e->pos)) {
|
|
2677
|
+
if (ctx == YEP_CTX_AFTER_COLON || ev.multiline) {
|
|
2678
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
|
|
2679
|
+
}
|
|
2680
|
+
if (e_simple_key_ok(e, node_at, e->pos) != 0) {
|
|
2681
|
+
return -1;
|
|
2682
|
+
}
|
|
2683
|
+
uint16_t key_col = (uint16_t)e_col(e, node_at);
|
|
2684
|
+
int rc = e_open_map(e, key_col, e->line, key_col + 1, pend_a, pend_t, pend_aid, 0);
|
|
2685
|
+
if (rc != 0) {
|
|
2686
|
+
return rc;
|
|
2687
|
+
}
|
|
2688
|
+
if (emit_now(e, &ev) != 0) {
|
|
2689
|
+
return -2;
|
|
2690
|
+
}
|
|
2691
|
+
e->pos++; /* ':' */
|
|
2692
|
+
return e_parse_value(e, YEP_CTX_AFTER_COLON, key_col);
|
|
2693
|
+
}
|
|
2694
|
+
if (!e_at_eol(e)) {
|
|
2695
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
|
|
2696
|
+
}
|
|
2697
|
+
return emit_now(e, &ev) == 0 ? 0 : -2;
|
|
2698
|
+
}
|
|
2699
|
+
|
|
2700
|
+
if (c == '*') {
|
|
2701
|
+
if (!yep_view_is_empty(anchor) || !yep_view_is_empty(tag)) {
|
|
2702
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos); /* "&a *b" */
|
|
2703
|
+
}
|
|
2704
|
+
if (e_alias(e, &ev) != 0) {
|
|
2705
|
+
return -1;
|
|
2706
|
+
}
|
|
2707
|
+
if (e_colon_at(e, e->pos)) {
|
|
2708
|
+
if (ctx == YEP_CTX_AFTER_COLON || ev.multiline) {
|
|
2709
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
|
|
2710
|
+
}
|
|
2711
|
+
if (e_simple_key_ok(e, node_at, e->pos) != 0) {
|
|
2712
|
+
return -1;
|
|
2713
|
+
}
|
|
2714
|
+
uint16_t key_col = (uint16_t)e_col(e, node_at);
|
|
2715
|
+
int rc = e_open_map(e, key_col, e->line, key_col + 1, pend_a, pend_t, pend_aid, 0);
|
|
2716
|
+
if (rc != 0) {
|
|
2717
|
+
return rc;
|
|
2718
|
+
}
|
|
2719
|
+
if (emit_now(e, &ev) != 0) {
|
|
2720
|
+
return -2;
|
|
2721
|
+
}
|
|
2722
|
+
e->pos++;
|
|
2723
|
+
return e_parse_value(e, YEP_CTX_AFTER_COLON, key_col);
|
|
2724
|
+
}
|
|
2725
|
+
if (!e_at_eol(e)) {
|
|
2726
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
|
|
2727
|
+
}
|
|
2728
|
+
return emit_now(e, &ev) == 0 ? 0 : -2;
|
|
2729
|
+
}
|
|
2730
|
+
|
|
2731
|
+
if (c == '|' || c == '>') {
|
|
2732
|
+
int parent_col = (e->depth == 0) ? -1 : (int)floor_col;
|
|
2733
|
+
if (e_block_scalar(e, &ev, parent_col) != 0) {
|
|
2734
|
+
return -1;
|
|
2735
|
+
}
|
|
2736
|
+
return emit_now(e, &ev) == 0 ? 0 : -2;
|
|
2737
|
+
}
|
|
2738
|
+
|
|
2739
|
+
/* plain scalar */
|
|
2740
|
+
if (!yep_plain_first_ok((unsigned char)e->p[e->pos])) {
|
|
2741
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos); /* lone "]" / "}" / "," */
|
|
2742
|
+
}
|
|
2743
|
+
yep_span s = yep_scan_plain(e->p, e->len, e->pos, 0);
|
|
2744
|
+
if (s.term != YEP_TERM_COLON && !yep_view_is_empty(pend_a) && !yep_view_is_empty(anchor)) {
|
|
2745
|
+
/* two anchors on ONE scalar ("&a
|
|
2746
|
+
&b v"); a colon means the
|
|
2747
|
+
* inline anchor belongs to the key and the pend one elsewhere */
|
|
2748
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
|
|
2749
|
+
}
|
|
2750
|
+
if (s.term != YEP_TERM_COLON && ctx == YEP_CTX_FRESH && e->depth > 0 &&
|
|
2751
|
+
e_col(e, node_at) == e->frames[e->depth - 1].col) {
|
|
2752
|
+
/* a plain scalar at an open container's column with no ':' —
|
|
2753
|
+
* neither a mapping key nor a sequence entry */
|
|
2754
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, s.end);
|
|
2755
|
+
}
|
|
2756
|
+
if (s.term == YEP_TERM_COLON) {
|
|
2757
|
+
if (ctx == YEP_CTX_AFTER_COLON) {
|
|
2758
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, s.end);
|
|
2759
|
+
}
|
|
2760
|
+
if (node_at > 0 && e->p[node_at - 1] == '\t') {
|
|
2761
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, node_at); /* ":\tkey:" */
|
|
2762
|
+
}
|
|
2763
|
+
if (e_simple_key_ok(e, s.start, s.end) != 0) {
|
|
2764
|
+
return -1;
|
|
2765
|
+
}
|
|
2766
|
+
if (e->doc_inline && (!yep_view_is_empty(anchor) || !yep_view_is_empty(tag))) {
|
|
2767
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos); /* "--- &a k: v" */
|
|
2768
|
+
}
|
|
2769
|
+
uint16_t key_col = (uint16_t)e_col(e, node_at);
|
|
2770
|
+
int rc = e_open_map(e, key_col, e->line, key_col + 1, pend_a, pend_t, pend_aid, 0);
|
|
2771
|
+
if (rc != 0) {
|
|
2772
|
+
return rc;
|
|
2773
|
+
}
|
|
2774
|
+
yep_event kv;
|
|
2775
|
+
e_event_init(&kv, YEP_EV_SCALAR);
|
|
2776
|
+
kv.anchor = anchor;
|
|
2777
|
+
kv.anchor_id =
|
|
2778
|
+
anchor_ordinal; /* the KEY binds its anchor; the
|
|
2779
|
+
* DOM keys on the ordinal (a key-anchored scalar's aliases
|
|
2780
|
+
* resolved to garbage without it — found by flow-direct-diff) */
|
|
2781
|
+
kv.tag = tag;
|
|
2782
|
+
kv.style = YEP_STYLE_PLAIN;
|
|
2783
|
+
kv.implicit = 1;
|
|
2784
|
+
kv.value.p = e->p + s.start;
|
|
2785
|
+
kv.value.len = s.end - s.start;
|
|
2786
|
+
kv.borrowed = 1;
|
|
2787
|
+
kv.line = e->line;
|
|
2788
|
+
kv.col = key_col + 1;
|
|
2789
|
+
if (emit_now(e, &kv) != 0) {
|
|
2790
|
+
return -2;
|
|
2791
|
+
}
|
|
2792
|
+
/* advance from the TRIMMED span end (not the stale pre-scan
|
|
2793
|
+
* position — the first ':' in the buffer may be INSIDE the key);
|
|
2794
|
+
* spaces may still precede the terminating colon */
|
|
2795
|
+
e->pos = s.end;
|
|
2796
|
+
while (e->pos < e->len && e->p[e->pos] != ':') {
|
|
2797
|
+
e->pos++;
|
|
2798
|
+
}
|
|
2799
|
+
e->pos++; /* past ':' */
|
|
2800
|
+
return e_parse_value(e, YEP_CTX_AFTER_COLON, key_col);
|
|
2801
|
+
}
|
|
2802
|
+
|
|
2803
|
+
ev.value.p = e->p + s.start;
|
|
2804
|
+
ev.value.len = s.end - s.start;
|
|
2805
|
+
ev.borrowed = 1;
|
|
2806
|
+
ev.style = YEP_STYLE_PLAIN;
|
|
2807
|
+
ev.implicit = 1;
|
|
2808
|
+
e->pos = s.end;
|
|
2809
|
+
if (e_plain_multiline(e, s, floor_col, &ev, e->depth == 0) != 0) {
|
|
2810
|
+
return -1;
|
|
2811
|
+
}
|
|
2812
|
+
if (e->fold_n == 1 && s.term == YEP_TERM_COMMENT) {
|
|
2813
|
+
e_skip_to_eol(e); /* single line ending in a comment */
|
|
2814
|
+
}
|
|
2815
|
+
return emit_now(e, &ev) == 0 ? 0 : -2;
|
|
2816
|
+
}
|
|
2817
|
+
|
|
2818
|
+
/* Value after "key:" / "-" / "?": on this line, or on following lines. */
|
|
2819
|
+
static int e_parse_value(yep_engine* e, yep_ctx ctx, uint16_t floor_col) {
|
|
2820
|
+
e_skip_inline_space(e);
|
|
2821
|
+
if (!e_at_eol(e)) {
|
|
2822
|
+
if (ctx == YEP_CTX_AFTER_COLON && e->p[e->pos] == '-' &&
|
|
2823
|
+
(e->pos + 1 >= e->len || e->p[e->pos + 1] == ' ' || e->p[e->pos + 1] == '\t' ||
|
|
2824
|
+
e->p[e->pos + 1] == '\n' || e->p[e->pos + 1] == '\r')) {
|
|
2825
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos); /* "key: - x" */
|
|
2826
|
+
}
|
|
2827
|
+
if (ctx == YEP_CTX_AFTER_DASH && e->p[e->pos] == ':' &&
|
|
2828
|
+
(e->pos + 1 >= e->len || e->p[e->pos + 1] == ' ' || e->p[e->pos + 1] == '\t' ||
|
|
2829
|
+
e->p[e->pos + 1] == '\n' || e->p[e->pos + 1] == '\r')) {
|
|
2830
|
+
/* "- :" — a compact pair with an empty key */
|
|
2831
|
+
uint16_t col = (uint16_t)e_col(e, e->pos);
|
|
2832
|
+
int rc = e_open_map(e, col, e->line, col + 1, e->pend_anchor, e->pend_tag,
|
|
2833
|
+
e->pend_anchor_id, 0);
|
|
2834
|
+
if (rc != 0) {
|
|
2835
|
+
return rc;
|
|
2836
|
+
}
|
|
2837
|
+
e->pend_anchor.p = NULL;
|
|
2838
|
+
e->pend_anchor.len = 0;
|
|
2839
|
+
e->pend_anchor_id = 0;
|
|
2840
|
+
e->pend_tag.p = NULL;
|
|
2841
|
+
e->pend_tag.len = 0;
|
|
2842
|
+
yep_event kv;
|
|
2843
|
+
e_event_init(&kv, YEP_EV_SCALAR);
|
|
2844
|
+
kv.implicit = 1;
|
|
2845
|
+
if (emit_now(e, &kv) != 0) {
|
|
2846
|
+
return -2;
|
|
2847
|
+
}
|
|
2848
|
+
e->pos++;
|
|
2849
|
+
return e_parse_value(e, YEP_CTX_AFTER_COLON, col);
|
|
2850
|
+
}
|
|
2851
|
+
int rc = e_node(e, ctx, floor_col);
|
|
2852
|
+
if (rc != 0) {
|
|
2853
|
+
return rc;
|
|
2854
|
+
}
|
|
2855
|
+
/* A plain value with continuations legitimately consumed through
|
|
2856
|
+
* the line break — pos sits at the next line's START; the main
|
|
2857
|
+
* loop must see the indentation, so do not skip it. */
|
|
2858
|
+
if (e->pos == e->line_start) {
|
|
2859
|
+
return 0;
|
|
2860
|
+
}
|
|
2861
|
+
e_skip_inline_space(e);
|
|
2862
|
+
if (!e_at_eol(e)) {
|
|
2863
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
|
|
2864
|
+
}
|
|
2865
|
+
return 0;
|
|
2866
|
+
}
|
|
2867
|
+
|
|
2868
|
+
/* following lines */
|
|
2869
|
+
for (;;) {
|
|
2870
|
+
if (e->pos >= e->len) {
|
|
2871
|
+
goto empty_value;
|
|
2872
|
+
}
|
|
2873
|
+
yep_line_info li;
|
|
2874
|
+
if (e->pos == e->line_start) {
|
|
2875
|
+
/* seed the memo: the fast arms re-read this line's facts —
|
|
2876
|
+
* a direct scan made every following line double-scanned */
|
|
2877
|
+
li = e_line_info_here(e);
|
|
2878
|
+
} else {
|
|
2879
|
+
li = yep_scan_line(e->p, e->len, e->pos);
|
|
2880
|
+
}
|
|
2881
|
+
if (li.flags & YEP_LF_BLANK) {
|
|
2882
|
+
e_line_done(e, li.end);
|
|
2883
|
+
continue;
|
|
2884
|
+
}
|
|
2885
|
+
if (li.flags & YEP_LF_COMMENT) {
|
|
2886
|
+
e_line_done(e, li.end);
|
|
2887
|
+
continue;
|
|
2888
|
+
}
|
|
2889
|
+
if (li.flags & (YEP_LF_DOC_START | YEP_LF_DOC_END)) {
|
|
2890
|
+
/* a document boundary ends the pending node: the props-only
|
|
2891
|
+
* document ("---\n!\n---\n…") emits its empty scalar here,
|
|
2892
|
+
* never swallowing the next document as its value */
|
|
2893
|
+
goto empty_value;
|
|
2894
|
+
}
|
|
2895
|
+
if (li.indent == floor_col && e->depth > 0 &&
|
|
2896
|
+
(ctx == YEP_CTX_AFTER_COLON || ctx == YEP_CTX_VALUE_LINE) &&
|
|
2897
|
+
(li.first == '&' || li.first == '!')) {
|
|
2898
|
+
/* Only PROPS-ONLY lines are new nodes, not values
|
|
2899
|
+
* ("key: &x\n!!map\na: b"); "&anchor c: 3" is a keyed pair */
|
|
2900
|
+
size_t t = li.offset + li.indent;
|
|
2901
|
+
int only_props = 1;
|
|
2902
|
+
while (t < li.end) {
|
|
2903
|
+
char pc = e->p[t];
|
|
2904
|
+
if (pc == ' ' || pc == '\t') {
|
|
2905
|
+
t++;
|
|
2906
|
+
continue;
|
|
2907
|
+
}
|
|
2908
|
+
if (pc == '&' || pc == '!' || pc == '<' || pc == ',' || pc == '>' ||
|
|
2909
|
+
(pc != '\n' && pc != '\r' && pc != '#' &&
|
|
2910
|
+
!yep_ct_is((unsigned char)pc, YEP_CT_FLOW_IND) && pc != ':' && pc != ' ')) {
|
|
2911
|
+
t++;
|
|
2912
|
+
continue;
|
|
2913
|
+
}
|
|
2914
|
+
only_props = (pc == '\n' || pc == '\r' || pc == '#');
|
|
2915
|
+
break;
|
|
2916
|
+
}
|
|
2917
|
+
if (only_props) {
|
|
2918
|
+
return e_fail(e, YEP_ERR_UNEXPECTED, e->pos + li.indent);
|
|
2919
|
+
}
|
|
2920
|
+
}
|
|
2921
|
+
yep_ctx vctx = (ctx == YEP_CTX_AFTER_COLON) ? YEP_CTX_VALUE_LINE : ctx;
|
|
2922
|
+
/* At document root there is no parent block: a following line
|
|
2923
|
+
* at any indent is the value ("&a\n- x"). Explicit-key content
|
|
2924
|
+
* may align with the '?' column. Flow nodes and property runs
|
|
2925
|
+
* continue the value even at the parent column ("k:\n!!seq\n[a]"). */
|
|
2926
|
+
if (li.indent > floor_col || e->depth == 0 ||
|
|
2927
|
+
(ctx == YEP_CTX_AFTER_Q && li.indent == floor_col && li.first != ':') ||
|
|
2928
|
+
((li.first == '[' || li.first == '{') && li.indent == floor_col)) {
|
|
2929
|
+
e->pos += li.indent;
|
|
2930
|
+
return e_node(e, vctx, floor_col);
|
|
2931
|
+
}
|
|
2932
|
+
/* indentless sequence as a mapping value; under a sequence frame
|
|
2933
|
+
* a dash at the same column is a SIBLING, not a continuation */
|
|
2934
|
+
if (li.indent == floor_col && li.first == '-' &&
|
|
2935
|
+
((ctx == YEP_CTX_AFTER_DASH && e->depth > 0 &&
|
|
2936
|
+
e->frames[e->depth - 1].kind == YEP_FRAME_MAP &&
|
|
2937
|
+
e->frames[e->depth - 1].col == floor_col) ||
|
|
2938
|
+
vctx == YEP_CTX_VALUE_LINE) &&
|
|
2939
|
+
(li.offset + li.indent + 1 >= e->len || e->p[li.offset + li.indent + 1] == ' ' ||
|
|
2940
|
+
e->p[li.offset + li.indent + 1] == '\t' || e->p[li.offset + li.indent + 1] == '\n' ||
|
|
2941
|
+
e->p[li.offset + li.indent + 1] == '\r')) {
|
|
2942
|
+
e->pos += li.indent;
|
|
2943
|
+
return e_node(e, ctx == YEP_CTX_AFTER_DASH ? ctx : vctx, floor_col);
|
|
2944
|
+
}
|
|
2945
|
+
goto empty_value;
|
|
2946
|
+
}
|
|
2947
|
+
|
|
2948
|
+
empty_value: {
|
|
2949
|
+
yep_event ev;
|
|
2950
|
+
e_event_init(&ev, YEP_EV_SCALAR);
|
|
2951
|
+
ev.style = YEP_STYLE_PLAIN;
|
|
2952
|
+
ev.implicit = 1;
|
|
2953
|
+
ev.anchor = e->pend_anchor;
|
|
2954
|
+
ev.tag = e->pend_tag;
|
|
2955
|
+
ev.anchor_id = e->pend_anchor_id;
|
|
2956
|
+
e->pend_anchor.p = NULL;
|
|
2957
|
+
e->pend_anchor.len = 0;
|
|
2958
|
+
e->pend_anchor_id = 0;
|
|
2959
|
+
e->pend_tag.p = NULL;
|
|
2960
|
+
e->pend_tag.len = 0;
|
|
2961
|
+
return emit_now(e, &ev) == 0 ? 0 : -2;
|
|
2962
|
+
}
|
|
2963
|
+
}
|
|
2964
|
+
|
|
2965
|
+
/* ------------------------------------------------------------ lifecycle */
|
|
2966
|
+
|
|
2967
|
+
void yep_engine_set_resolver(yep_engine* e, const yep_resolver* r) {
|
|
2968
|
+
if (e != NULL) {
|
|
2969
|
+
e->resolver = r != NULL ? r : yep_resolver_core12();
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
|
|
2973
|
+
void yep_engine_set_max_depth(yep_engine* e, int depth) {
|
|
2974
|
+
if (e != NULL && depth > 0) {
|
|
2975
|
+
e->max_depth = depth > YEP_MAX_DEPTH ? YEP_MAX_DEPTH : depth;
|
|
2976
|
+
}
|
|
2977
|
+
}
|
|
2978
|
+
|
|
2979
|
+
yep_pool* yep_engine_detach_pool(yep_engine* e) {
|
|
2980
|
+
if (e == NULL || e->pool == NULL) {
|
|
2981
|
+
return NULL;
|
|
2982
|
+
}
|
|
2983
|
+
yep_pool* p = e->pool;
|
|
2984
|
+
e->pool = yep_pool_create(e->sys, 8192); /* fresh: engine stays usable */
|
|
2985
|
+
return p;
|
|
2986
|
+
}
|
|
2987
|
+
|
|
2988
|
+
void yep_engine_prepare(yep_engine* e, const yep_text_stats* st) {
|
|
2989
|
+
if (e == NULL || st == NULL) {
|
|
2990
|
+
return;
|
|
2991
|
+
}
|
|
2992
|
+
if (st->amp > 0) {
|
|
2993
|
+
yep_nametab_reserve(&e->anchors, (uint32_t)st->amp);
|
|
2994
|
+
}
|
|
2995
|
+
}
|
|
2996
|
+
|
|
2997
|
+
yep_engine* yep_engine_create(const yep_allocator* sys) {
|
|
2998
|
+
if (sys == NULL) {
|
|
2999
|
+
return NULL;
|
|
3000
|
+
}
|
|
3001
|
+
yep_pool* pool = yep_pool_create(sys, 8192);
|
|
3002
|
+
if (pool == NULL) {
|
|
3003
|
+
return NULL;
|
|
3004
|
+
}
|
|
3005
|
+
yep_engine* e = yep_alloc(sys, sizeof(yep_engine));
|
|
3006
|
+
if (e == NULL) {
|
|
3007
|
+
yep_pool_destroy(pool);
|
|
3008
|
+
return NULL;
|
|
3009
|
+
}
|
|
3010
|
+
memset(e, 0, sizeof(*e));
|
|
3011
|
+
e->sys = sys;
|
|
3012
|
+
e->pool = pool;
|
|
3013
|
+
e->max_depth = YEP_MAX_DEPTH;
|
|
3014
|
+
if (!yep_nametab_init(&e->anchors, sys)) {
|
|
3015
|
+
yep_free(e->sys, e);
|
|
3016
|
+
yep_pool_destroy(pool);
|
|
3017
|
+
return NULL;
|
|
3018
|
+
}
|
|
3019
|
+
yep_error_clear(&e->err);
|
|
3020
|
+
return e;
|
|
3021
|
+
}
|
|
3022
|
+
|
|
3023
|
+
/* YAML 1.2 line breaks beyond \n/\r: NEL, LS, PS. When present, the
|
|
3024
|
+
* input is copied with each normalized to a single '\n' (libyaml reader
|
|
3025
|
+
* behavior); inputs without them keep the zero-copy path. */
|
|
3026
|
+
static const char* e_normalize_breaks(yep_engine* e, const char* p, size_t len) {
|
|
3027
|
+
const unsigned char* q = (const unsigned char*)p;
|
|
3028
|
+
size_t i = 0;
|
|
3029
|
+
int found = 0;
|
|
3030
|
+
while (i < len) {
|
|
3031
|
+
if (q[i] == 0xC2 && i + 1 < len && q[i + 1] == 0x85) {
|
|
3032
|
+
found = 1;
|
|
3033
|
+
break;
|
|
3034
|
+
}
|
|
3035
|
+
if (q[i] == 0xE2 && i + 2 < len && q[i + 1] == 0x80 &&
|
|
3036
|
+
(q[i + 2] == 0xA8 || q[i + 2] == 0xA9)) {
|
|
3037
|
+
found = 1;
|
|
3038
|
+
break;
|
|
3039
|
+
}
|
|
3040
|
+
i++;
|
|
3041
|
+
}
|
|
3042
|
+
if (!found) {
|
|
3043
|
+
return p;
|
|
3044
|
+
}
|
|
3045
|
+
char* out = yep_alloc(e->sys, len + 1);
|
|
3046
|
+
if (out == NULL) {
|
|
3047
|
+
return p; /* fall back: parse as-is */
|
|
3048
|
+
}
|
|
3049
|
+
size_t o = 0;
|
|
3050
|
+
for (size_t j = 0; j < len;) {
|
|
3051
|
+
if (q[j] == 0xC2 && j + 1 < len && q[j + 1] == 0x85) {
|
|
3052
|
+
out[o++] = '\n';
|
|
3053
|
+
j += 2;
|
|
3054
|
+
} else if (q[j] == 0xE2 && j + 2 < len && q[j + 1] == 0x80 &&
|
|
3055
|
+
(q[j + 2] == 0xA8 || q[j + 2] == 0xA9)) {
|
|
3056
|
+
out[o++] = '\n';
|
|
3057
|
+
j += 3;
|
|
3058
|
+
} else {
|
|
3059
|
+
out[o++] = p[j++];
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
3062
|
+
out[o] = '\0';
|
|
3063
|
+
e->norm_buf = out;
|
|
3064
|
+
return out;
|
|
3065
|
+
}
|
|
3066
|
+
|
|
3067
|
+
typedef struct yep_stepstate {
|
|
3068
|
+
char* buf;
|
|
3069
|
+
size_t len, cap;
|
|
3070
|
+
size_t scanned; /* bytes folded into quote/flow/comment state */
|
|
3071
|
+
int in_flow; /* open [ { depth */
|
|
3072
|
+
int quote; /* 0 none, else the opening quote char */
|
|
3073
|
+
int in_comment; /* inside a # comment (until the line break) */
|
|
3074
|
+
int saw_directive; /* a % line awaits its --- document */
|
|
3075
|
+
int started; /* STREAM_START emitted */
|
|
3076
|
+
uint32_t line_base; /* whole-stream lines consumed by earlier runs */
|
|
3077
|
+
} yep_stepstate;
|
|
3078
|
+
|
|
3079
|
+
static int engine_run_impl(yep_engine* e, const char* buf, size_t len, const yep_sink* sink,
|
|
3080
|
+
int emit_start, int emit_end);
|
|
3081
|
+
|
|
3082
|
+
int yep_engine_run(yep_engine* e, const char* buf, size_t len, const yep_sink* sink) {
|
|
3083
|
+
return engine_run_impl(e, buf, len, sink, 1, 1);
|
|
3084
|
+
}
|
|
3085
|
+
|
|
3086
|
+
void yep_engine_destroy(yep_engine* e) {
|
|
3087
|
+
if (e == NULL) {
|
|
3088
|
+
return;
|
|
3089
|
+
}
|
|
3090
|
+
if (e->step != NULL) {
|
|
3091
|
+
yep_stepstate* st = e->step;
|
|
3092
|
+
free(st->buf);
|
|
3093
|
+
free(st);
|
|
3094
|
+
e->step = NULL;
|
|
3095
|
+
}
|
|
3096
|
+
yep_nametab_free(&e->anchors);
|
|
3097
|
+
yep_free(e->sys, e->norm_buf);
|
|
3098
|
+
yep_pool_destroy(e->pool);
|
|
3099
|
+
yep_free(e->sys, e);
|
|
3100
|
+
}
|
|
3101
|
+
|
|
3102
|
+
/* ---- resumable stepping (TODO.impl/07) --------------------------------
|
|
3103
|
+
*
|
|
3104
|
+
* The pending buffer holds raw bytes; each step parses the complete-
|
|
3105
|
+
* document prefix and keeps the tail. A cut is safe only at the start
|
|
3106
|
+
* of a column-0 document marker (--- / ...) while no quoted scalar,
|
|
3107
|
+
* comment, or flow collection spans it — tracked incrementally as
|
|
3108
|
+
* chunks arrive. Anything ambiguous (lone-\r line breaks, an
|
|
3109
|
+
* unterminated marker at the buffer edge, quote pairs split by the
|
|
3110
|
+
* chunk boundary) merely delays the cut: buffering, never a wrong
|
|
3111
|
+
* split. Cuts land only at --- lines (never ...: that would orphan
|
|
3112
|
+
* an explicit DOCUMENT_END) and only when no % directive is pending:
|
|
3113
|
+
* directives travel with their document. Line numbers stay
|
|
3114
|
+
* stream-absolute: each run starts at the base derived from the
|
|
3115
|
+
* engine's own line counter, so stepped events are byte-identical to
|
|
3116
|
+
* whole-buffer ones. */
|
|
3117
|
+
|
|
3118
|
+
/* A quote opens a scalar only where a value can begin; anywhere else
|
|
3119
|
+
* it is literal plain text (the apostrophe in "don't"). This is exact
|
|
3120
|
+
* on valid YAML: a quote at a value position always opens. */
|
|
3121
|
+
static int quote_opens(char prev) {
|
|
3122
|
+
return prev == '\n' || prev == ' ' || prev == '\t' || prev == '-' || prev == ':' ||
|
|
3123
|
+
prev == '?' || prev == ',' || prev == '[' || prev == '{' || prev == '&' || prev == '*' ||
|
|
3124
|
+
prev == '!';
|
|
3125
|
+
}
|
|
3126
|
+
|
|
3127
|
+
/* Advances quote/flow/comment state over buf[scanned, len) and returns
|
|
3128
|
+
* the offset of the LAST safe cut (0 = none). When the final bytes'
|
|
3129
|
+
* meaning depends on bytes not yet fed (a quote that may be doubled,
|
|
3130
|
+
* a marker missing its terminator), scanned is held back so the next
|
|
3131
|
+
* scan re-reads them with context. */
|
|
3132
|
+
static size_t step_scan(yep_stepstate* st) {
|
|
3133
|
+
const char* p = st->buf;
|
|
3134
|
+
size_t last_cut = 0;
|
|
3135
|
+
size_t hold = st->len;
|
|
3136
|
+
size_t i = st->scanned;
|
|
3137
|
+
for (; i < st->len; i++) {
|
|
3138
|
+
char c = p[i];
|
|
3139
|
+
if (st->in_comment) {
|
|
3140
|
+
if (c == '\n') {
|
|
3141
|
+
st->in_comment = 0;
|
|
3142
|
+
}
|
|
3143
|
+
continue;
|
|
3144
|
+
}
|
|
3145
|
+
if (st->quote != 0) {
|
|
3146
|
+
if (st->quote == '\'') {
|
|
3147
|
+
if (c == '\'') {
|
|
3148
|
+
if (i + 1 >= st->len) {
|
|
3149
|
+
hold = i; /* '' or close? wait for the next byte */
|
|
3150
|
+
break;
|
|
3151
|
+
}
|
|
3152
|
+
if (p[i + 1] == '\'') {
|
|
3153
|
+
i++; /* escaped quote: still inside */
|
|
3154
|
+
continue;
|
|
3155
|
+
}
|
|
3156
|
+
st->quote = 0;
|
|
3157
|
+
}
|
|
3158
|
+
continue;
|
|
3159
|
+
}
|
|
3160
|
+
if (c == '\\') {
|
|
3161
|
+
if (i + 1 >= st->len) {
|
|
3162
|
+
hold = i; /* the escaped byte is unknown */
|
|
3163
|
+
break;
|
|
3164
|
+
}
|
|
3165
|
+
i++;
|
|
3166
|
+
continue;
|
|
3167
|
+
}
|
|
3168
|
+
if (c == '"') {
|
|
3169
|
+
st->quote = 0;
|
|
3170
|
+
}
|
|
3171
|
+
continue;
|
|
3172
|
+
}
|
|
3173
|
+
if (c == '#') {
|
|
3174
|
+
char prev = i > 0 ? p[i - 1] : '\n';
|
|
3175
|
+
if (prev == '\n' || prev == ' ' || prev == '\t') {
|
|
3176
|
+
st->in_comment = 1;
|
|
3177
|
+
continue;
|
|
3178
|
+
}
|
|
3179
|
+
}
|
|
3180
|
+
if ((c == '\'' || c == '"') && quote_opens(i > 0 ? p[i - 1] : '\n')) {
|
|
3181
|
+
st->quote = c;
|
|
3182
|
+
continue;
|
|
3183
|
+
}
|
|
3184
|
+
if (c == '[' || c == '{') {
|
|
3185
|
+
st->in_flow++;
|
|
3186
|
+
continue;
|
|
3187
|
+
}
|
|
3188
|
+
if (c == ']' || c == '}') {
|
|
3189
|
+
if (st->in_flow > 0) {
|
|
3190
|
+
st->in_flow--;
|
|
3191
|
+
}
|
|
3192
|
+
continue;
|
|
3193
|
+
}
|
|
3194
|
+
if (st->in_flow == 0) {
|
|
3195
|
+
char prev = i > 0 ? p[i - 1] : '\n';
|
|
3196
|
+
if (prev != '\n') {
|
|
3197
|
+
continue;
|
|
3198
|
+
}
|
|
3199
|
+
if (c == '%') {
|
|
3200
|
+
st->saw_directive = 1; /* must stay with its --- line */
|
|
3201
|
+
continue;
|
|
3202
|
+
}
|
|
3203
|
+
if (c == '-') {
|
|
3204
|
+
if (i + 3 >= st->len) {
|
|
3205
|
+
hold = i; /* --- possibly truncated by the chunk */
|
|
3206
|
+
break;
|
|
3207
|
+
}
|
|
3208
|
+
if (p[i + 1] == '-' && p[i + 2] == '-') {
|
|
3209
|
+
char after = p[i + 3];
|
|
3210
|
+
if (after == '\n' || after == ' ' || after == '\t' || after == '\r') {
|
|
3211
|
+
if (st->saw_directive == 0 && i > 0) {
|
|
3212
|
+
last_cut = i; /* interior document boundary */
|
|
3213
|
+
}
|
|
3214
|
+
st->saw_directive = 0; /* this --- owns the directives */
|
|
3215
|
+
}
|
|
3216
|
+
}
|
|
3217
|
+
}
|
|
3218
|
+
}
|
|
3219
|
+
}
|
|
3220
|
+
st->scanned = hold < st->len ? hold : st->len;
|
|
3221
|
+
return last_cut;
|
|
3222
|
+
}
|
|
3223
|
+
|
|
3224
|
+
int yep_engine_step(yep_engine* e, const char* chunk, size_t len, int final, const yep_sink* sink) {
|
|
3225
|
+
if (e == NULL || (chunk == NULL && len != 0)) {
|
|
3226
|
+
return -1;
|
|
3227
|
+
}
|
|
3228
|
+
if (e->step == NULL) {
|
|
3229
|
+
e->step = calloc(1, sizeof(yep_stepstate));
|
|
3230
|
+
if (e->step == NULL) {
|
|
3231
|
+
return -1;
|
|
3232
|
+
}
|
|
3233
|
+
}
|
|
3234
|
+
yep_stepstate* st = e->step;
|
|
3235
|
+
if (len > 0) {
|
|
3236
|
+
if (st->len + len > st->cap) {
|
|
3237
|
+
size_t cap = st->cap ? st->cap : 4096;
|
|
3238
|
+
while (cap < st->len + len) {
|
|
3239
|
+
cap *= 2;
|
|
3240
|
+
}
|
|
3241
|
+
char* nb = realloc(st->buf, cap);
|
|
3242
|
+
if (nb == NULL) {
|
|
3243
|
+
return -1;
|
|
3244
|
+
}
|
|
3245
|
+
st->buf = nb;
|
|
3246
|
+
st->cap = cap;
|
|
3247
|
+
}
|
|
3248
|
+
memcpy(st->buf + st->len, chunk, len);
|
|
3249
|
+
st->len += len;
|
|
3250
|
+
}
|
|
3251
|
+
if (!final) {
|
|
3252
|
+
size_t cut = step_scan(st);
|
|
3253
|
+
if (cut == 0 || cut >= st->len) {
|
|
3254
|
+
return 0; /* no complete document yet */
|
|
3255
|
+
}
|
|
3256
|
+
e->line_base = st->line_base;
|
|
3257
|
+
int rc = engine_run_impl(e, st->buf, cut, sink, !st->started, 0);
|
|
3258
|
+
if (rc != 0) {
|
|
3259
|
+
return rc;
|
|
3260
|
+
}
|
|
3261
|
+
st->started = 1;
|
|
3262
|
+
st->line_base = e->line - 1; /* the engine consumed to here */
|
|
3263
|
+
memmove(st->buf, st->buf + cut, st->len - cut);
|
|
3264
|
+
st->len -= cut;
|
|
3265
|
+
st->scanned = 0;
|
|
3266
|
+
/* the tail began at a --- line in a clean state */
|
|
3267
|
+
st->in_flow = 0;
|
|
3268
|
+
st->quote = 0;
|
|
3269
|
+
st->in_comment = 0;
|
|
3270
|
+
st->saw_directive = 0;
|
|
3271
|
+
return 0;
|
|
3272
|
+
}
|
|
3273
|
+
/* final: parse everything that remains and close the stream */
|
|
3274
|
+
e->line_base = st->line_base;
|
|
3275
|
+
int rc = engine_run_impl(e, st->buf, st->len, sink, !st->started, 1);
|
|
3276
|
+
free(st->buf);
|
|
3277
|
+
free(st);
|
|
3278
|
+
e->step = NULL;
|
|
3279
|
+
e->line_base = 0;
|
|
3280
|
+
return rc;
|
|
3281
|
+
}
|
|
3282
|
+
|
|
3283
|
+
static int engine_run_impl(yep_engine* e, const char* buf, size_t len, const yep_sink* sink,
|
|
3284
|
+
int emit_start, int emit_end) {
|
|
3285
|
+
if (e == NULL || (buf == NULL && len != 0)) {
|
|
3286
|
+
return -1;
|
|
3287
|
+
}
|
|
3288
|
+
yep_free(e->sys, e->norm_buf);
|
|
3289
|
+
e->norm_buf = NULL;
|
|
3290
|
+
e->p = e_normalize_breaks(e, buf, len);
|
|
3291
|
+
e->len = (e->norm_buf != NULL) ? strlen(e->norm_buf) : len;
|
|
3292
|
+
e->pos = 0;
|
|
3293
|
+
e->line = 1 + e->line_base;
|
|
3294
|
+
e->line_base = 0;
|
|
3295
|
+
e->line_start = 0;
|
|
3296
|
+
e->li_cache_line = 0;
|
|
3297
|
+
e->shape_cache_line = 0;
|
|
3298
|
+
e->facts_cache_line = 0;
|
|
3299
|
+
e->depth = 0;
|
|
3300
|
+
yep_nametab_clear(&e->anchors);
|
|
3301
|
+
e->tagmap_n = 0;
|
|
3302
|
+
e->saw_yaml = 0;
|
|
3303
|
+
e->doc_content = 0;
|
|
3304
|
+
e->q_key_pending = 0;
|
|
3305
|
+
e->q_value_pending = 0;
|
|
3306
|
+
e->sink = sink;
|
|
3307
|
+
yep_error_clear(&e->err);
|
|
3308
|
+
if (e->resolver == NULL) {
|
|
3309
|
+
e->resolver = yep_resolver_core12();
|
|
3310
|
+
}
|
|
3311
|
+
|
|
3312
|
+
yep_event ev;
|
|
3313
|
+
if (emit_start) {
|
|
3314
|
+
e_event_init(&ev, YEP_EV_STREAM_START);
|
|
3315
|
+
if (emit_now(e, &ev) != 0) {
|
|
3316
|
+
return -2;
|
|
3317
|
+
}
|
|
3318
|
+
}
|
|
3319
|
+
|
|
3320
|
+
int doc_open = 0;
|
|
3321
|
+
|
|
3322
|
+
while (e->pos < e->len) {
|
|
3323
|
+
/* pos may sit MID-line here (e.g. at a comment left by the
|
|
3324
|
+
* "--- # c" inline path, whose e_line_done was a no-op): the
|
|
3325
|
+
* memo scans line_start and would rewind — scan from pos, the
|
|
3326
|
+
* pre-memo semantics */
|
|
3327
|
+
yep_line_info li =
|
|
3328
|
+
e->pos == e->line_start ? e_line_info_here(e) : yep_scan_line(e->p, e->len, e->pos);
|
|
3329
|
+
if ((li.flags & YEP_LF_TAB) && !(li.flags & YEP_LF_BLANK)) {
|
|
3330
|
+
size_t t = li.offset + li.indent;
|
|
3331
|
+
while (t < li.end && (e->p[t] == ' ' || e->p[t] == '\t')) {
|
|
3332
|
+
t++;
|
|
3333
|
+
}
|
|
3334
|
+
if (t >= li.end || (e->p[t] != '[' && e->p[t] != '{')) {
|
|
3335
|
+
e_fail(e, YEP_ERR_TAB_IN_INDENT, e->pos + li.indent);
|
|
3336
|
+
goto fail;
|
|
3337
|
+
}
|
|
3338
|
+
li.indent = (uint16_t)(t - li.offset);
|
|
3339
|
+
}
|
|
3340
|
+
if (li.flags & YEP_LF_DIRECTIVE) {
|
|
3341
|
+
if (doc_open) {
|
|
3342
|
+
e_fail(e, YEP_ERR_BAD_DIRECTIVE, e->pos);
|
|
3343
|
+
goto fail;
|
|
3344
|
+
}
|
|
3345
|
+
if (e->saw_yaml) {
|
|
3346
|
+
e_fail(e, YEP_ERR_BAD_DIRECTIVE, e->pos); /* repeated %YAML */
|
|
3347
|
+
goto fail;
|
|
3348
|
+
}
|
|
3349
|
+
{
|
|
3350
|
+
/* %YAML takes exactly one version argument */
|
|
3351
|
+
size_t chk = li.offset + li.indent + 1;
|
|
3352
|
+
if (li.end - chk >= 4 && memcmp(e->p + chk, "YAML", 4) == 0) {
|
|
3353
|
+
e->saw_yaml = 1;
|
|
3354
|
+
size_t a0 = chk + 4;
|
|
3355
|
+
while (a0 < li.end && e->p[a0] == ' ') {
|
|
3356
|
+
a0++;
|
|
3357
|
+
}
|
|
3358
|
+
size_t a1 = a0;
|
|
3359
|
+
int dots = 0;
|
|
3360
|
+
while (a1 < li.end &&
|
|
3361
|
+
((e->p[a1] >= '0' && e->p[a1] <= '9') || e->p[a1] == '.')) {
|
|
3362
|
+
dots += (e->p[a1] == '.');
|
|
3363
|
+
a1++;
|
|
3364
|
+
}
|
|
3365
|
+
size_t rest = a1;
|
|
3366
|
+
while (rest < li.end && e->p[rest] == ' ') {
|
|
3367
|
+
rest++;
|
|
3368
|
+
}
|
|
3369
|
+
if (rest < li.end && e->p[rest] == '#' && rest > a1) {
|
|
3370
|
+
rest = li.end; /* trailing comment (blank before #) */
|
|
3371
|
+
}
|
|
3372
|
+
if (a1 == a0 || dots != 1 || rest < li.end || e->p[a0] == '.' ||
|
|
3373
|
+
e->p[a1 - 1] == '.') {
|
|
3374
|
+
e_fail(e, YEP_ERR_BAD_DIRECTIVE, chk);
|
|
3375
|
+
goto fail;
|
|
3376
|
+
}
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
3379
|
+
/* %TAG <handle> <prefix> — registered for the next document */
|
|
3380
|
+
size_t d = li.offset + li.indent + 1;
|
|
3381
|
+
while (d < li.end && e->p[d] == ' ') {
|
|
3382
|
+
d++;
|
|
3383
|
+
}
|
|
3384
|
+
if (li.end - d >= 3 && memcmp(e->p + d, "TAG", 3) == 0 && e->tagmap_n < 8) {
|
|
3385
|
+
size_t h0 = d + 3;
|
|
3386
|
+
while (h0 < li.end && e->p[h0] == ' ') {
|
|
3387
|
+
h0++;
|
|
3388
|
+
}
|
|
3389
|
+
size_t h1 = h0;
|
|
3390
|
+
while (h1 < li.end && e->p[h1] != ' ') {
|
|
3391
|
+
h1++;
|
|
3392
|
+
}
|
|
3393
|
+
size_t p0 = h1;
|
|
3394
|
+
while (p0 < li.end && e->p[p0] == ' ') {
|
|
3395
|
+
p0++;
|
|
3396
|
+
}
|
|
3397
|
+
size_t p1 = li.end;
|
|
3398
|
+
while (p1 > p0 && (e->p[p1 - 1] == ' ' || e->p[p1 - 1] == '\r')) {
|
|
3399
|
+
p1--;
|
|
3400
|
+
}
|
|
3401
|
+
if (h1 > h0 && p1 > p0 && e->p[h0] == '!') {
|
|
3402
|
+
e->tagmap[e->tagmap_n].handle.p = e->p + h0;
|
|
3403
|
+
e->tagmap[e->tagmap_n].handle.len = (uint32_t)(h1 - h0);
|
|
3404
|
+
e->tagmap[e->tagmap_n].prefix.p = e->p + p0;
|
|
3405
|
+
e->tagmap[e->tagmap_n].prefix.len = (uint32_t)(p1 - p0);
|
|
3406
|
+
e->tagmap_n++;
|
|
3407
|
+
}
|
|
3408
|
+
}
|
|
3409
|
+
e_line_done(e, li.end);
|
|
3410
|
+
continue;
|
|
3411
|
+
}
|
|
3412
|
+
if (li.flags & (YEP_LF_BLANK | YEP_LF_COMMENT)) {
|
|
3413
|
+
e_line_done(e, li.end);
|
|
3414
|
+
continue;
|
|
3415
|
+
}
|
|
3416
|
+
if (li.flags & YEP_LF_DOC_START) {
|
|
3417
|
+
int rc = e_close_to(e, 0);
|
|
3418
|
+
if (rc != 0) {
|
|
3419
|
+
return rc;
|
|
3420
|
+
}
|
|
3421
|
+
if (doc_open) {
|
|
3422
|
+
if (!e->doc_content) {
|
|
3423
|
+
e_event_init(&ev, YEP_EV_SCALAR);
|
|
3424
|
+
ev.implicit = 1;
|
|
3425
|
+
if (emit_now(e, &ev) != 0) {
|
|
3426
|
+
return -2;
|
|
3427
|
+
}
|
|
3428
|
+
}
|
|
3429
|
+
e->doc_content = 0;
|
|
3430
|
+
e_event_init(&ev, YEP_EV_DOCUMENT_END);
|
|
3431
|
+
if (emit_now(e, &ev) != 0) {
|
|
3432
|
+
return -2;
|
|
3433
|
+
}
|
|
3434
|
+
doc_open = 0;
|
|
3435
|
+
e->tagmap_n = 0; /* directives are per-document */
|
|
3436
|
+
yep_nametab_clear(&e->anchors); /* so are anchors */
|
|
3437
|
+
}
|
|
3438
|
+
e_event_init(&ev, YEP_EV_DOCUMENT_START);
|
|
3439
|
+
ev.style = 1; /* explicit marker */
|
|
3440
|
+
if (emit_now(e, &ev) != 0) {
|
|
3441
|
+
return -2;
|
|
3442
|
+
}
|
|
3443
|
+
doc_open = 1;
|
|
3444
|
+
e->saw_yaml = 0;
|
|
3445
|
+
e->last_root_flow = 0; /* a new document may carry its own directives */
|
|
3446
|
+
e->pos = li.offset + li.indent + 3;
|
|
3447
|
+
e_skip_inline_space(e);
|
|
3448
|
+
if (!e_at_eol(e)) {
|
|
3449
|
+
e->doc_inline = 1;
|
|
3450
|
+
rc = e_node(e, YEP_CTX_FRESH, li.indent);
|
|
3451
|
+
e->doc_inline = 0;
|
|
3452
|
+
if (rc != 0) {
|
|
3453
|
+
if (rc == -2) {
|
|
3454
|
+
return -2;
|
|
3455
|
+
}
|
|
3456
|
+
goto fail;
|
|
3457
|
+
}
|
|
3458
|
+
e->doc_content = 1;
|
|
3459
|
+
}
|
|
3460
|
+
e_line_done(e, e->pos);
|
|
3461
|
+
continue;
|
|
3462
|
+
}
|
|
3463
|
+
if (li.flags & YEP_LF_DOC_END) {
|
|
3464
|
+
{
|
|
3465
|
+
size_t rest = li.offset + li.indent + 3;
|
|
3466
|
+
while (rest < e->len &&
|
|
3467
|
+
(e->p[rest] == ' ' || e->p[rest] == '\t' || e->p[rest] == '\r')) {
|
|
3468
|
+
rest++;
|
|
3469
|
+
}
|
|
3470
|
+
if (rest < e->len && e->p[rest] != '\n' && e->p[rest] != '#') {
|
|
3471
|
+
e_fail(e, YEP_ERR_UNEXPECTED, rest);
|
|
3472
|
+
goto fail;
|
|
3473
|
+
}
|
|
3474
|
+
}
|
|
3475
|
+
int rc = e_close_to(e, 0);
|
|
3476
|
+
if (rc != 0) {
|
|
3477
|
+
return rc;
|
|
3478
|
+
}
|
|
3479
|
+
if (doc_open) {
|
|
3480
|
+
if (!e->doc_content) {
|
|
3481
|
+
e_event_init(&ev, YEP_EV_SCALAR);
|
|
3482
|
+
ev.implicit = 1;
|
|
3483
|
+
if (emit_now(e, &ev) != 0) {
|
|
3484
|
+
return -2;
|
|
3485
|
+
}
|
|
3486
|
+
}
|
|
3487
|
+
e_event_init(&ev, YEP_EV_DOCUMENT_END);
|
|
3488
|
+
ev.style = 1; /* explicit "..." marker */
|
|
3489
|
+
e->tagmap_n = 0; /* directives are per-document */
|
|
3490
|
+
yep_nametab_clear(&e->anchors);
|
|
3491
|
+
if (emit_now(e, &ev) != 0) {
|
|
3492
|
+
return -2;
|
|
3493
|
+
}
|
|
3494
|
+
doc_open = 0;
|
|
3495
|
+
e->doc_content = 0;
|
|
3496
|
+
e->saw_yaml = 0;
|
|
3497
|
+
}
|
|
3498
|
+
e_line_done(e, li.end);
|
|
3499
|
+
continue;
|
|
3500
|
+
}
|
|
3501
|
+
|
|
3502
|
+
if (!doc_open) {
|
|
3503
|
+
e_event_init(&ev, YEP_EV_DOCUMENT_START);
|
|
3504
|
+
if (emit_now(e, &ev) != 0) {
|
|
3505
|
+
return -2;
|
|
3506
|
+
}
|
|
3507
|
+
doc_open = 1;
|
|
3508
|
+
}
|
|
3509
|
+
|
|
3510
|
+
/* unwind frames that cannot continue at this column. The
|
|
3511
|
+
* dash-blank test is computed once per line (TODO.restructure/75
|
|
3512
|
+
* — it was spelled out twice below); the top frame loads once
|
|
3513
|
+
* per iteration. */
|
|
3514
|
+
uint16_t c = li.indent;
|
|
3515
|
+
const int dash_blank =
|
|
3516
|
+
li.first == '-' &&
|
|
3517
|
+
(li.offset + li.indent + 1 >= e->len || e->p[li.offset + li.indent + 1] == ' ' ||
|
|
3518
|
+
e->p[li.offset + li.indent + 1] == '\t' || e->p[li.offset + li.indent + 1] == '\n' ||
|
|
3519
|
+
e->p[li.offset + li.indent + 1] == '\r');
|
|
3520
|
+
while (e->depth > 0) {
|
|
3521
|
+
const yep_frame* fr = &e->frames[e->depth - 1];
|
|
3522
|
+
if (fr->col > c) {
|
|
3523
|
+
int rc = e_close_to(e, e->depth - 1);
|
|
3524
|
+
if (rc != 0) {
|
|
3525
|
+
return rc;
|
|
3526
|
+
}
|
|
3527
|
+
continue;
|
|
3528
|
+
}
|
|
3529
|
+
if (fr->col == c) {
|
|
3530
|
+
int continues;
|
|
3531
|
+
if (fr->kind == YEP_FRAME_SEQ) {
|
|
3532
|
+
continues = dash_blank;
|
|
3533
|
+
} else if (fr->inline_doc) {
|
|
3534
|
+
continues = 0; /* "--- k: v" maps take deeper keys only */
|
|
3535
|
+
} else {
|
|
3536
|
+
continues = yep_scan_is_key_start(li.first) || li.first == ':' ||
|
|
3537
|
+
li.first == '?' || (li.first == '-' && !dash_blank);
|
|
3538
|
+
}
|
|
3539
|
+
if (!continues) {
|
|
3540
|
+
int rc = e_close_to(e, e->depth - 1);
|
|
3541
|
+
if (rc != 0) {
|
|
3542
|
+
return rc;
|
|
3543
|
+
}
|
|
3544
|
+
continue;
|
|
3545
|
+
}
|
|
3546
|
+
}
|
|
3547
|
+
break;
|
|
3548
|
+
}
|
|
3549
|
+
|
|
3550
|
+
if (e->depth == 0 && e->doc_content) {
|
|
3551
|
+
/* a completed root scalar followed by more content: a second
|
|
3552
|
+
* root node in the same document ("word1 # c\nword2") */
|
|
3553
|
+
e_fail(e, YEP_ERR_UNEXPECTED, e->pos + li.indent);
|
|
3554
|
+
goto fail;
|
|
3555
|
+
}
|
|
3556
|
+
if (e->depth > 0 && e->frames[e->depth - 1].col < c) {
|
|
3557
|
+
/* deeper than any open container at main dispatch: nested
|
|
3558
|
+
* values are consumed inside e_parse_value, so this line is
|
|
3559
|
+
* an orphan continuation ("key: word1\n# c\n word2") */
|
|
3560
|
+
e_fail(e, YEP_ERR_BAD_INDENT, e->pos + li.indent);
|
|
3561
|
+
goto fail;
|
|
3562
|
+
}
|
|
3563
|
+
e->pos += li.indent;
|
|
3564
|
+
unsigned char first = (unsigned char)e->p[e->pos];
|
|
3565
|
+
if (first == ':' &&
|
|
3566
|
+
(e->pos + 1 >= e->len || e->p[e->pos + 1] == ' ' || e->p[e->pos + 1] == '\t' ||
|
|
3567
|
+
e->p[e->pos + 1] == '\n' || e->p[e->pos + 1] == '\r')) {
|
|
3568
|
+
/* explicit-key value line, or a bare ':' pair (empty key) */
|
|
3569
|
+
int rc;
|
|
3570
|
+
if (e->last_root_flow) {
|
|
3571
|
+
e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
|
|
3572
|
+
goto fail; /* a root flow node cannot become a key */
|
|
3573
|
+
}
|
|
3574
|
+
if (e->depth == 0 || e->frames[e->depth - 1].kind != YEP_FRAME_MAP) {
|
|
3575
|
+
rc = e_open_map(e, c, e->line, c + 1, e->pend_anchor, e->pend_tag,
|
|
3576
|
+
e->pend_anchor_id, 0);
|
|
3577
|
+
if (rc != 0) {
|
|
3578
|
+
if (rc == -2) {
|
|
3579
|
+
return -2;
|
|
3580
|
+
}
|
|
3581
|
+
goto fail;
|
|
3582
|
+
}
|
|
3583
|
+
}
|
|
3584
|
+
if (!e->q_key_pending) {
|
|
3585
|
+
yep_event kv;
|
|
3586
|
+
e_event_init(&kv, YEP_EV_SCALAR);
|
|
3587
|
+
kv.style = YEP_STYLE_PLAIN;
|
|
3588
|
+
kv.implicit = 1;
|
|
3589
|
+
if (emit_now(e, &kv) != 0) {
|
|
3590
|
+
return -2;
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
3593
|
+
e->q_key_pending = 0;
|
|
3594
|
+
e->q_value_pending = 0;
|
|
3595
|
+
e->pos++;
|
|
3596
|
+
rc = e_parse_value(e, YEP_CTX_AFTER_DASH, e->frames[e->depth - 1].col);
|
|
3597
|
+
if (rc != 0) {
|
|
3598
|
+
if (rc == -2) {
|
|
3599
|
+
return -2;
|
|
3600
|
+
}
|
|
3601
|
+
goto fail;
|
|
3602
|
+
}
|
|
3603
|
+
e->doc_content = 1;
|
|
3604
|
+
e_line_done(e, e->pos);
|
|
3605
|
+
continue;
|
|
3606
|
+
}
|
|
3607
|
+
|
|
3608
|
+
/* The cycle fast path (TODO.restructure/79 carve 1): a line
|
|
3609
|
+
* that CONTINUES the top frame (a map key at its column, a
|
|
3610
|
+
* seq dash) with no pending state is exactly e_classified's
|
|
3611
|
+
* contract — call it straight from the line loop and skip
|
|
3612
|
+
* e_node's prologue (pend save/clear, e_props, the gate) and
|
|
3613
|
+
* the dispatch tail. Non-classified shapes return 0 and take
|
|
3614
|
+
* the chain below unchanged. */
|
|
3615
|
+
if (e->depth > 0 && e->pend_anchor.p == NULL && e->pend_tag.p == NULL &&
|
|
3616
|
+
e->pend_anchor_id == 0 && !e->q_key_pending && !e->q_value_pending &&
|
|
3617
|
+
((e->frames[e->depth - 1].kind == YEP_FRAME_MAP && e->frames[e->depth - 1].col == c &&
|
|
3618
|
+
!e->frames[e->depth - 1].inline_doc) ||
|
|
3619
|
+
(e->frames[e->depth - 1].kind == YEP_FRAME_SEQ && e->frames[e->depth - 1].col == c &&
|
|
3620
|
+
dash_blank))) {
|
|
3621
|
+
int frc = e_classified(e, e->frames[e->depth - 1].col);
|
|
3622
|
+
if (frc != 0) {
|
|
3623
|
+
if (frc < 0) {
|
|
3624
|
+
if (frc == -2) {
|
|
3625
|
+
return -2;
|
|
3626
|
+
}
|
|
3627
|
+
goto fail;
|
|
3628
|
+
}
|
|
3629
|
+
e->doc_content = 1;
|
|
3630
|
+
e_line_done(e, e->pos);
|
|
3631
|
+
continue;
|
|
3632
|
+
}
|
|
3633
|
+
}
|
|
3634
|
+
|
|
3635
|
+
{
|
|
3636
|
+
e->last_root_flow = 0;
|
|
3637
|
+
int rc = e_flush_q_value(e); /* a key without its ':' line */
|
|
3638
|
+
if (rc != 0) {
|
|
3639
|
+
return -2;
|
|
3640
|
+
}
|
|
3641
|
+
rc = e_node(e, YEP_CTX_FRESH, e->depth > 0 ? e->frames[e->depth - 1].col : 0);
|
|
3642
|
+
if (rc != 0) {
|
|
3643
|
+
if (rc == -2) {
|
|
3644
|
+
return -2;
|
|
3645
|
+
}
|
|
3646
|
+
goto fail;
|
|
3647
|
+
}
|
|
3648
|
+
e->doc_content = 1;
|
|
3649
|
+
}
|
|
3650
|
+
e_line_done(e, e->pos);
|
|
3651
|
+
}
|
|
3652
|
+
|
|
3653
|
+
if ((e->tagmap_n > 0 || e->saw_yaml) && !doc_open) {
|
|
3654
|
+
/* a directive was never followed by "---" */
|
|
3655
|
+
e_fail(e, YEP_ERR_BAD_DIRECTIVE, e->len);
|
|
3656
|
+
goto fail;
|
|
3657
|
+
}
|
|
3658
|
+
{
|
|
3659
|
+
int rc = e_close_to(e, 0);
|
|
3660
|
+
if (rc != 0) {
|
|
3661
|
+
return rc;
|
|
3662
|
+
}
|
|
3663
|
+
}
|
|
3664
|
+
if (doc_open) {
|
|
3665
|
+
if (!e->doc_content) {
|
|
3666
|
+
e_event_init(&ev, YEP_EV_SCALAR);
|
|
3667
|
+
ev.implicit = 1;
|
|
3668
|
+
if (emit_now(e, &ev) != 0) {
|
|
3669
|
+
return -2;
|
|
3670
|
+
}
|
|
3671
|
+
}
|
|
3672
|
+
e_event_init(&ev, YEP_EV_DOCUMENT_END);
|
|
3673
|
+
if (emit_now(e, &ev) != 0) {
|
|
3674
|
+
return -2;
|
|
3675
|
+
}
|
|
3676
|
+
e->tagmap_n = 0;
|
|
3677
|
+
yep_nametab_clear(&e->anchors);
|
|
3678
|
+
}
|
|
3679
|
+
if (emit_end) {
|
|
3680
|
+
e_event_init(&ev, YEP_EV_STREAM_END);
|
|
3681
|
+
if (emit_now(e, &ev) != 0) {
|
|
3682
|
+
return -2;
|
|
3683
|
+
}
|
|
3684
|
+
}
|
|
3685
|
+
return 0;
|
|
3686
|
+
|
|
3687
|
+
fail:
|
|
3688
|
+
*yep_error_tls() = e->err;
|
|
3689
|
+
return -1;
|
|
3690
|
+
}
|
|
3691
|
+
|
|
3692
|
+
size_t yep_engine_pos(const yep_engine* e) {
|
|
3693
|
+
return e ? e->pos : 0;
|
|
3694
|
+
}
|
|
3695
|
+
|
|
3696
|
+
const yep_error* yep_engine_error(const yep_engine* e) {
|
|
3697
|
+
return e ? &e->err : NULL;
|
|
3698
|
+
}
|