teptris 0.2.37-aarch64-linux → 0.2.39-aarch64-linux
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/teptris_ext/engine/LICENSE.md +21 -0
- data/ext/teptris_ext/engine/src/include/teptris/plan.h +123 -0
- data/ext/teptris_ext/engine/src/include/teptris/teptris.h +208 -0
- data/ext/teptris_ext/engine/src/include/teptris/version.h +9 -0
- data/ext/teptris_ext/engine/src/teptris/api.c +362 -0
- data/ext/teptris_ext/engine/src/teptris/build.c +355 -0
- data/ext/teptris_ext/engine/src/teptris/common/chartype.c +70 -0
- data/ext/teptris_ext/engine/src/teptris/common/chartype.h +41 -0
- data/ext/teptris_ext/engine/src/teptris/common/ryu/common.h +114 -0
- data/ext/teptris_ext/engine/src/teptris/common/ryu/d2s.c +496 -0
- data/ext/teptris_ext/engine/src/teptris/common/ryu/d2s_full_table.h +367 -0
- data/ext/teptris_ext/engine/src/teptris/common/ryu/d2s_intrinsics.h +357 -0
- data/ext/teptris_ext/engine/src/teptris/common/ryu/digit_table.h +35 -0
- data/ext/teptris_ext/engine/src/teptris/common/ryu/ryu_d2s.h +39 -0
- data/ext/teptris_ext/engine/src/teptris/common/ryu/ryu_parse.h +45 -0
- data/ext/teptris_ext/engine/src/teptris/common/ryu/s2d.c +259 -0
- data/ext/teptris_ext/engine/src/teptris/common/simd.h +119 -0
- data/ext/teptris_ext/engine/src/teptris/dom/dom.c +192 -0
- data/ext/teptris_ext/engine/src/teptris/dom/dom.h +124 -0
- data/ext/teptris_ext/engine/src/teptris/emit/emitter.c +718 -0
- data/ext/teptris_ext/engine/src/teptris/emit/emitter.h +11 -0
- data/ext/teptris_ext/engine/src/teptris/memory/arena.c +104 -0
- data/ext/teptris_ext/engine/src/teptris/memory/arena.h +53 -0
- data/ext/teptris_ext/engine/src/teptris/parse/datetime.c +218 -0
- data/ext/teptris_ext/engine/src/teptris/parse/keys.c +121 -0
- data/ext/teptris_ext/engine/src/teptris/parse/parse.h +76 -0
- data/ext/teptris_ext/engine/src/teptris/parse/parser.c +931 -0
- data/ext/teptris_ext/engine/src/teptris/parse/scalars.c +940 -0
- data/ext/teptris_ext/engine/src/teptris/plan/plan.c +472 -0
- data/ext/teptris_ext/engine/src/teptris/version.c +6 -0
- data/ext/teptris_ext/extconf.rb +61 -0
- data/ext/teptris_ext/materialize.c +608 -0
- data/lib/{teptris_ext.so → teptris/3.3/teptris_ext.so} +0 -0
- data/lib/teptris/3.4/teptris_ext.so +0 -0
- data/lib/teptris/4.0/teptris_ext.so +0 -0
- data/lib/teptris/version.rb +1 -1
- data/lib/teptris.rb +19 -11
- metadata +37 -3
|
@@ -0,0 +1,931 @@
|
|
|
1
|
+
#include "teptris/parse/parse.h"
|
|
2
|
+
|
|
3
|
+
#include <stdarg.h>
|
|
4
|
+
#include <stdio.h>
|
|
5
|
+
#include <string.h>
|
|
6
|
+
|
|
7
|
+
#include "teptris/common/chartype.h"
|
|
8
|
+
#include "teptris/memory/arena.h"
|
|
9
|
+
|
|
10
|
+
teptris_status tep_fail_at(teptris_parser *ps, const char *at, teptris_status code,
|
|
11
|
+
const char *fmt, ...)
|
|
12
|
+
{
|
|
13
|
+
teptris_document *doc = ps->doc;
|
|
14
|
+
va_list ap;
|
|
15
|
+
va_start(ap, fmt);
|
|
16
|
+
vsnprintf(doc->err_msg, sizeof(doc->err_msg), fmt, ap);
|
|
17
|
+
va_end(ap);
|
|
18
|
+
doc->err.status = code;
|
|
19
|
+
doc->err.message = doc->err_msg;
|
|
20
|
+
|
|
21
|
+
/* Position state is lazy: hot paths never maintain line/bol, so
|
|
22
|
+
* every position is computed by one scan src -> pos (error paths
|
|
23
|
+
* already abort the parse; this is the only line/column source). */
|
|
24
|
+
const char *pos = (at != NULL) ? at : ps->p;
|
|
25
|
+
size_t line = 1;
|
|
26
|
+
const char *bol = ps->src;
|
|
27
|
+
for (const char *q = ps->src; q < pos; q++) {
|
|
28
|
+
if (*q == '\n') {
|
|
29
|
+
line++;
|
|
30
|
+
bol = q + 1;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
doc->err.line = line;
|
|
34
|
+
doc->err.column = (size_t)(pos - bol) + 1;
|
|
35
|
+
return code;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
void tep_adv(teptris_parser *ps, size_t n)
|
|
39
|
+
{
|
|
40
|
+
size_t avail = (size_t)(ps->end - ps->p);
|
|
41
|
+
if (n > avail) {
|
|
42
|
+
n = avail;
|
|
43
|
+
}
|
|
44
|
+
ps->p += n;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
teptris_status tep_skip_ws(teptris_parser *ps)
|
|
48
|
+
{
|
|
49
|
+
while (ps->p < ps->end && tep_is_ws((unsigned char)*ps->p)) {
|
|
50
|
+
ps->p++;
|
|
51
|
+
}
|
|
52
|
+
return TEPTRIS_OK;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static teptris_status skip_comment(teptris_parser *ps)
|
|
56
|
+
{
|
|
57
|
+
tep_adv(ps, 1); /* '#' */
|
|
58
|
+
while (ps->p < ps->end) {
|
|
59
|
+
unsigned char c = (unsigned char)*ps->p;
|
|
60
|
+
if (c == '\n') {
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
if (c == '\r') {
|
|
64
|
+
if (ps->p + 1 < ps->end && ps->p[1] == '\n') {
|
|
65
|
+
break; /* EOL handled by the caller */
|
|
66
|
+
}
|
|
67
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
68
|
+
"lone carriage return in comment");
|
|
69
|
+
}
|
|
70
|
+
if ((c < 0x20 && c != '\t') || c == 0x7F) {
|
|
71
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
72
|
+
"control character in comment");
|
|
73
|
+
}
|
|
74
|
+
ps->p++;
|
|
75
|
+
}
|
|
76
|
+
return TEPTRIS_OK;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
teptris_status tep_skip_ws_nl(teptris_parser *ps)
|
|
80
|
+
{
|
|
81
|
+
for (;;) {
|
|
82
|
+
if (ps->p >= ps->end) {
|
|
83
|
+
return TEPTRIS_OK;
|
|
84
|
+
}
|
|
85
|
+
unsigned char c = (unsigned char)*ps->p;
|
|
86
|
+
if (tep_is_ws(c)) {
|
|
87
|
+
tep_adv(ps, 1);
|
|
88
|
+
} else if (c == '\n') {
|
|
89
|
+
tep_adv(ps, 1);
|
|
90
|
+
} else if (c == '\r' && ps->p + 1 < ps->end && ps->p[1] == '\n') {
|
|
91
|
+
tep_adv(ps, 2);
|
|
92
|
+
} else if (c == '#') {
|
|
93
|
+
teptris_status st = skip_comment(ps);
|
|
94
|
+
if (st != TEPTRIS_OK) {
|
|
95
|
+
return st;
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
return TEPTRIS_OK;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
bool tep_at_eol(const teptris_parser *ps)
|
|
104
|
+
{
|
|
105
|
+
if (ps->p >= ps->end) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
if (*ps->p == '\n') {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
return *ps->p == '\r' && ps->p + 1 < ps->end && ps->p[1] == '\n';
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
teptris_status tep_finish_line(teptris_parser *ps)
|
|
115
|
+
{
|
|
116
|
+
teptris_status st = tep_skip_ws(ps);
|
|
117
|
+
if (st != TEPTRIS_OK) {
|
|
118
|
+
return st;
|
|
119
|
+
}
|
|
120
|
+
if (ps->p < ps->end && *ps->p == '#') {
|
|
121
|
+
st = skip_comment(ps);
|
|
122
|
+
if (st != TEPTRIS_OK) {
|
|
123
|
+
return st;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (ps->p >= ps->end) {
|
|
127
|
+
return TEPTRIS_OK;
|
|
128
|
+
}
|
|
129
|
+
if (*ps->p == '\n') {
|
|
130
|
+
tep_adv(ps, 1);
|
|
131
|
+
return TEPTRIS_OK;
|
|
132
|
+
}
|
|
133
|
+
if (*ps->p == '\r' && ps->p + 1 < ps->end && ps->p[1] == '\n') {
|
|
134
|
+
tep_adv(ps, 2);
|
|
135
|
+
return TEPTRIS_OK;
|
|
136
|
+
}
|
|
137
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
138
|
+
"expected newline after value");
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* ---------------------------------------------------------------- UTF-8 -- */
|
|
142
|
+
|
|
143
|
+
static size_t utf8_invalid_at(const char *data, size_t len)
|
|
144
|
+
{
|
|
145
|
+
static const uint32_t mins[3] = {0x80, 0x800, 0x10000};
|
|
146
|
+
size_t i = 0;
|
|
147
|
+
while (i < len) {
|
|
148
|
+
/* SWAR ASCII skip: 8 bytes per iteration while every high bit
|
|
149
|
+
* is clear (all-ASCII corpora never pay the byte loop). A
|
|
150
|
+
* word with a high bit falls through; the multibyte walker
|
|
151
|
+
* resumes this loop after each sequence. */
|
|
152
|
+
while (i + 8 <= len) {
|
|
153
|
+
uint64_t w;
|
|
154
|
+
memcpy(&w, data + i, 8);
|
|
155
|
+
if ((w & 0x8080808080808080ULL) != 0) {
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
i += 8;
|
|
159
|
+
}
|
|
160
|
+
if (i >= len) {
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
unsigned char c = (unsigned char)data[i];
|
|
164
|
+
if (c < 0x80) {
|
|
165
|
+
i++;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
size_t need;
|
|
169
|
+
uint32_t cp;
|
|
170
|
+
if ((c & 0xE0) == 0xC0) {
|
|
171
|
+
need = 1;
|
|
172
|
+
cp = c & 0x1F;
|
|
173
|
+
} else if ((c & 0xF0) == 0xE0) {
|
|
174
|
+
need = 2;
|
|
175
|
+
cp = c & 0x0F;
|
|
176
|
+
} else if ((c & 0xF8) == 0xF0) {
|
|
177
|
+
need = 3;
|
|
178
|
+
cp = c & 0x07;
|
|
179
|
+
} else {
|
|
180
|
+
return i;
|
|
181
|
+
}
|
|
182
|
+
if (i + need >= len) {
|
|
183
|
+
return i; /* truncated */
|
|
184
|
+
}
|
|
185
|
+
for (size_t k = 1; k <= need; k++) {
|
|
186
|
+
unsigned char cc = (unsigned char)data[i + k];
|
|
187
|
+
if ((cc & 0xC0) != 0x80) {
|
|
188
|
+
return i;
|
|
189
|
+
}
|
|
190
|
+
cp = (cp << 6) | (cc & 0x3F);
|
|
191
|
+
}
|
|
192
|
+
if (cp < mins[need - 1] || (cp >= 0xD800 && cp <= 0xDFFF) ||
|
|
193
|
+
cp > 0x10FFFF) {
|
|
194
|
+
return i;
|
|
195
|
+
}
|
|
196
|
+
i += need + 1;
|
|
197
|
+
}
|
|
198
|
+
return len;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
static teptris_status validate_utf8(teptris_document *doc, const char *data,
|
|
202
|
+
size_t len)
|
|
203
|
+
{
|
|
204
|
+
size_t bad = utf8_invalid_at(data, len);
|
|
205
|
+
if (bad == len) {
|
|
206
|
+
return TEPTRIS_OK;
|
|
207
|
+
}
|
|
208
|
+
size_t line = 1;
|
|
209
|
+
const char *bol = data;
|
|
210
|
+
for (size_t i = 0; i < bad; i++) {
|
|
211
|
+
if (data[i] == '\n') {
|
|
212
|
+
line++;
|
|
213
|
+
bol = data + i + 1;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
doc->err.status = TEPTRIS_ERR_ENCODING;
|
|
217
|
+
doc->err.line = line;
|
|
218
|
+
doc->err.column = bad - (size_t)(bol - data) + 1;
|
|
219
|
+
snprintf(doc->err_msg, sizeof(doc->err_msg), "invalid UTF-8 sequence");
|
|
220
|
+
doc->err.message = doc->err_msg;
|
|
221
|
+
return TEPTRIS_ERR_ENCODING;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/* -------------------------------------------------------- key insertion -- */
|
|
225
|
+
|
|
226
|
+
static teptris_status insert_dotted(teptris_parser *ps, teptris_node *table,
|
|
227
|
+
const teptris_view *parts, size_t count,
|
|
228
|
+
teptris_node *value)
|
|
229
|
+
{
|
|
230
|
+
teptris_document *doc = ps->doc;
|
|
231
|
+
teptris_node *t = table;
|
|
232
|
+
|
|
233
|
+
for (size_t i = 0; i + 1 < count; i++) {
|
|
234
|
+
teptris_view part = parts[i];
|
|
235
|
+
uint64_t h = teptris_dom_key_hash(part.ptr, part.len);
|
|
236
|
+
size_t slot;
|
|
237
|
+
teptris_entry *e =
|
|
238
|
+
teptris_dom_table_probe_slot(t, h, part.ptr, part.len, &slot);
|
|
239
|
+
if (e == NULL) {
|
|
240
|
+
teptris_node *child = teptris_dom_new_table(doc, TBL_DOTTED);
|
|
241
|
+
if (child == NULL) {
|
|
242
|
+
return TEPTRIS_ERR_ALLOC;
|
|
243
|
+
}
|
|
244
|
+
teptris_status st =
|
|
245
|
+
teptris_dom_table_insert_slot(doc, t, part, h, child, slot);
|
|
246
|
+
if (st != TEPTRIS_OK) {
|
|
247
|
+
return st;
|
|
248
|
+
}
|
|
249
|
+
t = child;
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
teptris_node *v = e->value;
|
|
253
|
+
if (v->kind != TEPTRIS_TABLE) {
|
|
254
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SEMANTIC,
|
|
255
|
+
"key '%.*s' is not a table", (int)part.len,
|
|
256
|
+
part.ptr);
|
|
257
|
+
}
|
|
258
|
+
if (v->flags & TBL_INLINE) {
|
|
259
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SEMANTIC,
|
|
260
|
+
"cannot extend inline table '%.*s'", (int)part.len,
|
|
261
|
+
part.ptr);
|
|
262
|
+
}
|
|
263
|
+
if (v->flags & (TBL_IMPLICIT | TBL_EXPLICIT)) {
|
|
264
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SEMANTIC,
|
|
265
|
+
"cannot extend table '%.*s' defined by a header",
|
|
266
|
+
(int)part.len, part.ptr);
|
|
267
|
+
}
|
|
268
|
+
t = v;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
teptris_view last = parts[count - 1];
|
|
272
|
+
uint64_t hlast = teptris_dom_key_hash(last.ptr, last.len);
|
|
273
|
+
size_t last_slot;
|
|
274
|
+
if (teptris_dom_table_probe_slot(t, hlast, last.ptr, last.len,
|
|
275
|
+
&last_slot) != NULL) {
|
|
276
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SEMANTIC, "duplicate key '%.*s'",
|
|
277
|
+
(int)last.len, last.ptr);
|
|
278
|
+
}
|
|
279
|
+
return teptris_dom_table_insert_slot(doc, t, last, hlast, value, last_slot);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/* ------------------------------------------------------------- headers -- */
|
|
283
|
+
|
|
284
|
+
static teptris_status resolve_header(teptris_parser *ps, const teptris_view *parts,
|
|
285
|
+
size_t count, bool aot, teptris_node **out)
|
|
286
|
+
{
|
|
287
|
+
teptris_document *doc = ps->doc;
|
|
288
|
+
teptris_node *t = doc->root;
|
|
289
|
+
|
|
290
|
+
for (size_t i = 0; i + 1 < count; i++) {
|
|
291
|
+
teptris_view part = parts[i];
|
|
292
|
+
uint64_t h = teptris_dom_key_hash(part.ptr, part.len);
|
|
293
|
+
size_t slot;
|
|
294
|
+
teptris_entry *e =
|
|
295
|
+
teptris_dom_table_probe_slot(t, h, part.ptr, part.len, &slot);
|
|
296
|
+
if (e == NULL) {
|
|
297
|
+
teptris_node *child = teptris_dom_new_table(doc, TBL_IMPLICIT);
|
|
298
|
+
if (child == NULL) {
|
|
299
|
+
return TEPTRIS_ERR_ALLOC;
|
|
300
|
+
}
|
|
301
|
+
teptris_status st =
|
|
302
|
+
teptris_dom_table_insert_slot(doc, t, part, h, child, slot);
|
|
303
|
+
if (st != TEPTRIS_OK) {
|
|
304
|
+
return st;
|
|
305
|
+
}
|
|
306
|
+
t = child;
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
teptris_node *v = e->value;
|
|
310
|
+
if (v->kind == TEPTRIS_ARRAY) {
|
|
311
|
+
if (!(v->flags & ARR_AOT)) {
|
|
312
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SEMANTIC,
|
|
313
|
+
"cannot extend plain array '%.*s'",
|
|
314
|
+
(int)part.len, part.ptr);
|
|
315
|
+
}
|
|
316
|
+
teptris_node *last =
|
|
317
|
+
v->as.array.items[v->as.array.len - 1];
|
|
318
|
+
if (last->kind != TEPTRIS_TABLE) {
|
|
319
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_STATE,
|
|
320
|
+
"corrupt array of tables");
|
|
321
|
+
}
|
|
322
|
+
t = last;
|
|
323
|
+
} else if (v->kind == TEPTRIS_TABLE) {
|
|
324
|
+
if (v->flags & TBL_INLINE) {
|
|
325
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SEMANTIC,
|
|
326
|
+
"cannot extend inline table '%.*s'",
|
|
327
|
+
(int)part.len, part.ptr);
|
|
328
|
+
}
|
|
329
|
+
/* TBL_DOTTED intermediates are walkable: the spec allows
|
|
330
|
+
* [table] headers to define sub-tables within dotted-key
|
|
331
|
+
* tables ([fruit.apple.texture] after apple.color = ...).
|
|
332
|
+
* Exact redefinition is still rejected at the final part. */
|
|
333
|
+
t = v;
|
|
334
|
+
} else {
|
|
335
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SEMANTIC,
|
|
336
|
+
"key '%.*s' is not a table", (int)part.len,
|
|
337
|
+
part.ptr);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
teptris_view last = parts[count - 1];
|
|
342
|
+
uint64_t hlast = teptris_dom_key_hash(last.ptr, last.len);
|
|
343
|
+
size_t last_slot;
|
|
344
|
+
teptris_entry *e = teptris_dom_table_probe_slot(t, hlast, last.ptr,
|
|
345
|
+
last.len, &last_slot);
|
|
346
|
+
|
|
347
|
+
if (aot) {
|
|
348
|
+
teptris_node *arr;
|
|
349
|
+
if (e == NULL) {
|
|
350
|
+
arr = teptris_dom_new_array(doc, ARR_AOT);
|
|
351
|
+
if (arr == NULL) {
|
|
352
|
+
return TEPTRIS_ERR_ALLOC;
|
|
353
|
+
}
|
|
354
|
+
teptris_status st = teptris_dom_table_insert_slot(doc, t, last,
|
|
355
|
+
hlast, arr,
|
|
356
|
+
last_slot);
|
|
357
|
+
if (st != TEPTRIS_OK) {
|
|
358
|
+
return st;
|
|
359
|
+
}
|
|
360
|
+
} else {
|
|
361
|
+
arr = e->value;
|
|
362
|
+
if (arr->kind != TEPTRIS_ARRAY || !(arr->flags & ARR_AOT)) {
|
|
363
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SEMANTIC,
|
|
364
|
+
"cannot redefine '%.*s' as an array of "
|
|
365
|
+
"tables",
|
|
366
|
+
(int)last.len, last.ptr);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
teptris_node *elem = teptris_dom_new_table(doc, TBL_EXPLICIT);
|
|
370
|
+
if (elem == NULL) {
|
|
371
|
+
return TEPTRIS_ERR_ALLOC;
|
|
372
|
+
}
|
|
373
|
+
teptris_status st = teptris_dom_array_push(doc, arr, elem);
|
|
374
|
+
if (st != TEPTRIS_OK) {
|
|
375
|
+
return st;
|
|
376
|
+
}
|
|
377
|
+
*out = elem;
|
|
378
|
+
return TEPTRIS_OK;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (e == NULL) {
|
|
382
|
+
teptris_node *tbl = teptris_dom_new_table(doc, TBL_EXPLICIT);
|
|
383
|
+
if (tbl == NULL) {
|
|
384
|
+
return TEPTRIS_ERR_ALLOC;
|
|
385
|
+
}
|
|
386
|
+
teptris_status st = teptris_dom_table_insert_slot(doc, t, last, hlast,
|
|
387
|
+
tbl, last_slot);
|
|
388
|
+
if (st != TEPTRIS_OK) {
|
|
389
|
+
return st;
|
|
390
|
+
}
|
|
391
|
+
*out = tbl;
|
|
392
|
+
return TEPTRIS_OK;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
teptris_node *v = e->value;
|
|
396
|
+
if (v->kind == TEPTRIS_TABLE && (v->flags & TBL_IMPLICIT)) {
|
|
397
|
+
v->flags = (uint8_t)((v->flags & ~TBL_IMPLICIT) | TBL_EXPLICIT);
|
|
398
|
+
*out = v;
|
|
399
|
+
return TEPTRIS_OK;
|
|
400
|
+
}
|
|
401
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SEMANTIC,
|
|
402
|
+
"table '%.*s' defined more than once", (int)last.len,
|
|
403
|
+
last.ptr);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
static teptris_status parse_header(teptris_parser *ps)
|
|
407
|
+
{
|
|
408
|
+
tep_adv(ps, 1); /* '[' */
|
|
409
|
+
bool aot = false;
|
|
410
|
+
if (ps->p < ps->end && *ps->p == '[') {
|
|
411
|
+
aot = true;
|
|
412
|
+
tep_adv(ps, 1);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
teptris_status st = tep_skip_ws(ps);
|
|
416
|
+
if (st != TEPTRIS_OK) {
|
|
417
|
+
return st;
|
|
418
|
+
}
|
|
419
|
+
teptris_view kbuf[8];
|
|
420
|
+
teptris_view *parts;
|
|
421
|
+
size_t count;
|
|
422
|
+
st = teptris_parse_key_path(ps, kbuf, 8, &parts, &count);
|
|
423
|
+
if (st != TEPTRIS_OK) {
|
|
424
|
+
return st;
|
|
425
|
+
}
|
|
426
|
+
st = tep_skip_ws(ps);
|
|
427
|
+
if (st != TEPTRIS_OK) {
|
|
428
|
+
return st;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
if (aot) {
|
|
432
|
+
if (ps->end - ps->p < 2 || ps->p[0] != ']' || ps->p[1] != ']') {
|
|
433
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX, "expected ']]'");
|
|
434
|
+
}
|
|
435
|
+
tep_adv(ps, 2);
|
|
436
|
+
} else {
|
|
437
|
+
if (ps->p >= ps->end || *ps->p != ']') {
|
|
438
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX, "expected ']'");
|
|
439
|
+
}
|
|
440
|
+
tep_adv(ps, 1);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
teptris_node *target;
|
|
444
|
+
teptris_status rst = resolve_header(ps, parts, count, aot, &target);
|
|
445
|
+
if (rst != TEPTRIS_OK) {
|
|
446
|
+
return rst;
|
|
447
|
+
}
|
|
448
|
+
ps->cur = target;
|
|
449
|
+
return TEPTRIS_OK;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/* Fast path for the dominant header shape: [bare.bare.bare] with no
|
|
453
|
+
* ws, no quoted segments, no [[. The dotted path is scanned inline
|
|
454
|
+
* (FNV fused into the scan, like try_keyval_fast) and resolved via
|
|
455
|
+
* the fused insert path. Returns false BEFORE any mutation for every
|
|
456
|
+
* other shape; when it returns true, *st_out carries the verdict and
|
|
457
|
+
* *out the target table (errors tear down the document, so implicit
|
|
458
|
+
* tables created before an error are harmless). */
|
|
459
|
+
#if defined(_MSC_VER)
|
|
460
|
+
#define TEP_NOINLINE __declspec(noinline)
|
|
461
|
+
#else
|
|
462
|
+
#define TEP_NOINLINE __attribute__((noinline))
|
|
463
|
+
#endif
|
|
464
|
+
/* noinline: single-caller statics inline into the main loop by
|
|
465
|
+
* default, and the loop is per-LINE code — the 8th-lever lesson */
|
|
466
|
+
TEP_NOINLINE static bool try_header_fast(teptris_parser *ps,
|
|
467
|
+
teptris_status *st_out,
|
|
468
|
+
teptris_node **out)
|
|
469
|
+
{
|
|
470
|
+
const char *start = ps->p;
|
|
471
|
+
if (start >= ps->end || *start != '[') {
|
|
472
|
+
return false;
|
|
473
|
+
}
|
|
474
|
+
const char *q = start + 1;
|
|
475
|
+
if (q >= ps->end || *q == '[') {
|
|
476
|
+
return false; /* array-of-tables: general path */
|
|
477
|
+
}
|
|
478
|
+
teptris_view parts[8];
|
|
479
|
+
uint64_t hs[8];
|
|
480
|
+
size_t count = 0;
|
|
481
|
+
for (;;) {
|
|
482
|
+
if (q >= ps->end || !tep_is_barekey((unsigned char)*q)) {
|
|
483
|
+
return false; /* empty/quoted segment or ws: general path */
|
|
484
|
+
}
|
|
485
|
+
if (count == 8) {
|
|
486
|
+
return false;
|
|
487
|
+
}
|
|
488
|
+
uint64_t h = 1469598103934665603ULL;
|
|
489
|
+
const char *seg = q;
|
|
490
|
+
while (q < ps->end && tep_is_barekey((unsigned char)*q)) {
|
|
491
|
+
h ^= (unsigned char)*q;
|
|
492
|
+
h *= 1099511628211ULL;
|
|
493
|
+
q++;
|
|
494
|
+
}
|
|
495
|
+
parts[count].ptr = seg;
|
|
496
|
+
parts[count].len = (size_t)(q - seg);
|
|
497
|
+
hs[count] = h;
|
|
498
|
+
count++;
|
|
499
|
+
if (q < ps->end && *q == '.') {
|
|
500
|
+
q++;
|
|
501
|
+
continue;
|
|
502
|
+
}
|
|
503
|
+
break;
|
|
504
|
+
}
|
|
505
|
+
if (q >= ps->end || *q != ']') {
|
|
506
|
+
return false;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/* shape confirmed — commit the resolution */
|
|
510
|
+
teptris_document *doc = ps->doc;
|
|
511
|
+
teptris_node *t = doc->root;
|
|
512
|
+
for (size_t i = 0; i + 1 < count; i++) {
|
|
513
|
+
size_t slot;
|
|
514
|
+
teptris_entry *e =
|
|
515
|
+
teptris_dom_table_probe_slot(t, hs[i], parts[i].ptr,
|
|
516
|
+
parts[i].len, &slot);
|
|
517
|
+
if (e == NULL) {
|
|
518
|
+
teptris_node *child = teptris_dom_new_table(doc, TBL_IMPLICIT);
|
|
519
|
+
if (child == NULL) {
|
|
520
|
+
*st_out = TEPTRIS_ERR_ALLOC;
|
|
521
|
+
return true;
|
|
522
|
+
}
|
|
523
|
+
teptris_status st = teptris_dom_table_insert_slot(
|
|
524
|
+
doc, t, parts[i], hs[i], child, slot);
|
|
525
|
+
if (st != TEPTRIS_OK) {
|
|
526
|
+
*st_out = st;
|
|
527
|
+
return true;
|
|
528
|
+
}
|
|
529
|
+
t = child;
|
|
530
|
+
continue;
|
|
531
|
+
}
|
|
532
|
+
teptris_node *v = e->value;
|
|
533
|
+
if (v->kind == TEPTRIS_ARRAY) {
|
|
534
|
+
if (!(v->flags & ARR_AOT)) {
|
|
535
|
+
ps->p = q + 1; /* error position matches parse_header */
|
|
536
|
+
*st_out = tep_fail_at(
|
|
537
|
+
ps, NULL, TEPTRIS_ERR_SEMANTIC,
|
|
538
|
+
"cannot extend plain array '%.*s'", (int)parts[i].len,
|
|
539
|
+
parts[i].ptr);
|
|
540
|
+
return true;
|
|
541
|
+
}
|
|
542
|
+
t = v->as.array.items[v->as.array.len - 1];
|
|
543
|
+
} else if (v->kind == TEPTRIS_TABLE) {
|
|
544
|
+
if (v->flags & TBL_INLINE) {
|
|
545
|
+
ps->p = q + 1; /* error position matches parse_header */
|
|
546
|
+
*st_out = tep_fail_at(
|
|
547
|
+
ps, NULL, TEPTRIS_ERR_SEMANTIC,
|
|
548
|
+
"cannot extend inline table '%.*s'", (int)parts[i].len,
|
|
549
|
+
parts[i].ptr);
|
|
550
|
+
return true;
|
|
551
|
+
}
|
|
552
|
+
t = v;
|
|
553
|
+
} else {
|
|
554
|
+
ps->p = q + 1; /* error position matches parse_header */
|
|
555
|
+
*st_out = tep_fail_at(ps, NULL, TEPTRIS_ERR_SEMANTIC,
|
|
556
|
+
"key '%.*s' is not a table",
|
|
557
|
+
(int)parts[i].len, parts[i].ptr);
|
|
558
|
+
return true;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
size_t last_slot;
|
|
562
|
+
teptris_view last = parts[count - 1];
|
|
563
|
+
if (teptris_dom_table_probe_slot(t, hs[count - 1], last.ptr, last.len,
|
|
564
|
+
&last_slot) != NULL) {
|
|
565
|
+
return false; /* exact-redefinition / implicit-convert: general */
|
|
566
|
+
}
|
|
567
|
+
teptris_node *tbl = teptris_dom_new_table(doc, TBL_EXPLICIT);
|
|
568
|
+
if (tbl == NULL) {
|
|
569
|
+
*st_out = TEPTRIS_ERR_ALLOC;
|
|
570
|
+
return true;
|
|
571
|
+
}
|
|
572
|
+
teptris_status st = teptris_dom_table_insert_slot(doc, t, last,
|
|
573
|
+
hs[count - 1], tbl,
|
|
574
|
+
last_slot);
|
|
575
|
+
if (st != TEPTRIS_OK) {
|
|
576
|
+
*st_out = st;
|
|
577
|
+
return true;
|
|
578
|
+
}
|
|
579
|
+
ps->p = q + 1;
|
|
580
|
+
*st_out = TEPTRIS_OK;
|
|
581
|
+
*out = tbl;
|
|
582
|
+
return true;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/* Fused fast path for the dominant line shape: bare key, optional
|
|
586
|
+
* ws, '=', optional ws, value — no dots, no quoted key. Scans the key
|
|
587
|
+
* inline, parses the value, and does a single find+insert; avoids the
|
|
588
|
+
* parts-array arena allocation and the general dotted-key machinery.
|
|
589
|
+
* Rewinds and returns false for anything else. */
|
|
590
|
+
static bool try_keyval_fast(teptris_parser *ps, teptris_node *tbl,
|
|
591
|
+
teptris_status *st_out)
|
|
592
|
+
{
|
|
593
|
+
const char *start = ps->p;
|
|
594
|
+
if (start >= ps->end || !tep_is_barekey((unsigned char)*start)) {
|
|
595
|
+
return false;
|
|
596
|
+
}
|
|
597
|
+
/* FNV-1a fused into the scan: the bytes are already loaded here,
|
|
598
|
+
* so the hash comes free instead of a second pass in find */
|
|
599
|
+
uint64_t h = 1469598103934665603ULL;
|
|
600
|
+
const char *kend = ps->p;
|
|
601
|
+
while (kend < ps->end && tep_is_barekey((unsigned char)*kend)) {
|
|
602
|
+
h ^= (unsigned char)*kend;
|
|
603
|
+
h *= 1099511628211ULL;
|
|
604
|
+
kend++;
|
|
605
|
+
}
|
|
606
|
+
ps->p = kend;
|
|
607
|
+
/* a '.' or quote means dotted/quoted: general path (rewind) */
|
|
608
|
+
while (ps->p < ps->end && (*ps->p == ' ' || *ps->p == '\t')) {
|
|
609
|
+
ps->p++;
|
|
610
|
+
}
|
|
611
|
+
if (ps->p >= ps->end || *ps->p != '=') {
|
|
612
|
+
ps->p = start;
|
|
613
|
+
return false;
|
|
614
|
+
}
|
|
615
|
+
ps->p++; /* '=' */
|
|
616
|
+
while (ps->p < ps->end && (*ps->p == ' ' || *ps->p == '\t')) {
|
|
617
|
+
ps->p++;
|
|
618
|
+
}
|
|
619
|
+
teptris_view key = {start, (size_t)(kend - start)};
|
|
620
|
+
|
|
621
|
+
teptris_node *value;
|
|
622
|
+
teptris_status st = teptris_parse_value_fast(ps, &value);
|
|
623
|
+
if (st != TEPTRIS_OK) {
|
|
624
|
+
*st_out = st;
|
|
625
|
+
return true;
|
|
626
|
+
}
|
|
627
|
+
size_t slot;
|
|
628
|
+
if (teptris_dom_table_probe_slot(tbl, h, key.ptr, key.len, &slot) != NULL) {
|
|
629
|
+
*st_out = tep_fail_at(ps, NULL, TEPTRIS_ERR_SEMANTIC,
|
|
630
|
+
"duplicate key '%.*s'", (int)key.len, key.ptr);
|
|
631
|
+
return true;
|
|
632
|
+
}
|
|
633
|
+
*st_out = teptris_dom_table_insert_slot(ps->doc, tbl, key, h, value, slot);
|
|
634
|
+
return true;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
static teptris_status parse_keyval(teptris_parser *ps)
|
|
638
|
+
{
|
|
639
|
+
teptris_status fast;
|
|
640
|
+
if (try_keyval_fast(ps, ps->cur, &fast)) {
|
|
641
|
+
return fast;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
teptris_view kbuf[8];
|
|
645
|
+
teptris_view *parts;
|
|
646
|
+
size_t count;
|
|
647
|
+
teptris_status st = teptris_parse_key_path(ps, kbuf, 8, &parts, &count);
|
|
648
|
+
if (st != TEPTRIS_OK) {
|
|
649
|
+
return st;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
st = tep_skip_ws(ps);
|
|
653
|
+
if (st != TEPTRIS_OK) {
|
|
654
|
+
return st;
|
|
655
|
+
}
|
|
656
|
+
if (ps->p >= ps->end || *ps->p != '=') {
|
|
657
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX, "expected '=' after key");
|
|
658
|
+
}
|
|
659
|
+
tep_adv(ps, 1);
|
|
660
|
+
st = tep_skip_ws(ps);
|
|
661
|
+
if (st != TEPTRIS_OK) {
|
|
662
|
+
return st;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
teptris_node *value;
|
|
666
|
+
st = teptris_parse_value(ps, &value);
|
|
667
|
+
if (st != TEPTRIS_OK) {
|
|
668
|
+
return st;
|
|
669
|
+
}
|
|
670
|
+
return insert_dotted(ps, ps->cur, parts, count, value);
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/* -------------------------------------------------- arrays, inline tbls -- */
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
/* Array separator fast path: the overwhelmingly common shapes are
|
|
677
|
+
* ", v" and ",\n v". Consume those with inline loops; only a '#'
|
|
678
|
+
* comment or CR forces the full skipper (which validates them). */
|
|
679
|
+
static teptris_status skip_array_gap(teptris_parser *ps)
|
|
680
|
+
{
|
|
681
|
+
for (;;) {
|
|
682
|
+
while (ps->p < ps->end && (*ps->p == ' ' || *ps->p == '\t')) {
|
|
683
|
+
ps->p++;
|
|
684
|
+
}
|
|
685
|
+
if (ps->p >= ps->end) {
|
|
686
|
+
return TEPTRIS_OK;
|
|
687
|
+
}
|
|
688
|
+
if (*ps->p == '\n') {
|
|
689
|
+
ps->p++;
|
|
690
|
+
continue;
|
|
691
|
+
}
|
|
692
|
+
if (*ps->p == '#') {
|
|
693
|
+
return tep_skip_ws_nl(ps);
|
|
694
|
+
}
|
|
695
|
+
if (*ps->p == '\r') {
|
|
696
|
+
return tep_skip_ws_nl(ps);
|
|
697
|
+
}
|
|
698
|
+
return TEPTRIS_OK;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
teptris_status teptris_parse_array(teptris_parser *ps, teptris_node **out)
|
|
703
|
+
{
|
|
704
|
+
teptris_document *doc = ps->doc;
|
|
705
|
+
teptris_node *arr = teptris_dom_new_array(doc, 0);
|
|
706
|
+
if (arr == NULL) {
|
|
707
|
+
return TEPTRIS_ERR_ALLOC;
|
|
708
|
+
}
|
|
709
|
+
tep_adv(ps, 1); /* '[' */
|
|
710
|
+
ps->depth++;
|
|
711
|
+
if (ps->depth > ps->max_depth) {
|
|
712
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_DEPTH, "nesting too deep");
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
for (;;) {
|
|
716
|
+
teptris_status st = skip_array_gap(ps);
|
|
717
|
+
if (st != TEPTRIS_OK) {
|
|
718
|
+
return st;
|
|
719
|
+
}
|
|
720
|
+
if (ps->p >= ps->end) {
|
|
721
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
722
|
+
"unterminated array");
|
|
723
|
+
}
|
|
724
|
+
if (*ps->p == ']') {
|
|
725
|
+
tep_adv(ps, 1);
|
|
726
|
+
break;
|
|
727
|
+
}
|
|
728
|
+
teptris_node *v;
|
|
729
|
+
st = teptris_parse_value_fast(ps, &v);
|
|
730
|
+
if (st != TEPTRIS_OK) {
|
|
731
|
+
return st;
|
|
732
|
+
}
|
|
733
|
+
if (arr->as.array.len < arr->as.array.cap) {
|
|
734
|
+
arr->as.array.items[arr->as.array.len++] = v;
|
|
735
|
+
} else {
|
|
736
|
+
st = teptris_dom_array_push(doc, arr, v);
|
|
737
|
+
if (st != TEPTRIS_OK) {
|
|
738
|
+
return st;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
st = skip_array_gap(ps);
|
|
742
|
+
if (st != TEPTRIS_OK) {
|
|
743
|
+
return st;
|
|
744
|
+
}
|
|
745
|
+
if (ps->p < ps->end && *ps->p == ',') {
|
|
746
|
+
tep_adv(ps, 1);
|
|
747
|
+
continue;
|
|
748
|
+
}
|
|
749
|
+
if (ps->p < ps->end && *ps->p == ']') {
|
|
750
|
+
tep_adv(ps, 1);
|
|
751
|
+
break;
|
|
752
|
+
}
|
|
753
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX, "expected ',' or ']'");
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
ps->depth--;
|
|
757
|
+
*out = arr;
|
|
758
|
+
return TEPTRIS_OK;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
teptris_status teptris_parse_inline(teptris_parser *ps, teptris_node **out)
|
|
762
|
+
{
|
|
763
|
+
teptris_document *doc = ps->doc;
|
|
764
|
+
teptris_node *tbl = teptris_dom_new_table(doc, TBL_INLINE);
|
|
765
|
+
if (tbl == NULL) {
|
|
766
|
+
return TEPTRIS_ERR_ALLOC;
|
|
767
|
+
}
|
|
768
|
+
tep_adv(ps, 1); /* '{' */
|
|
769
|
+
ps->depth++;
|
|
770
|
+
if (ps->depth > ps->max_depth) {
|
|
771
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_DEPTH, "nesting too deep");
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
/* TOML 1.1 grammar: newlines (with comments) between members and
|
|
775
|
+
* a trailing comma before '}' are accepted; the 1.0-strict
|
|
776
|
+
* versions of those documents live in the versioned corpus's
|
|
777
|
+
* invalid/ tree for parsers targeting 1.0 only. */
|
|
778
|
+
for (;;) {
|
|
779
|
+
teptris_status st = skip_array_gap(ps);
|
|
780
|
+
if (st != TEPTRIS_OK) {
|
|
781
|
+
return st;
|
|
782
|
+
}
|
|
783
|
+
if (ps->p >= ps->end) {
|
|
784
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
785
|
+
"unterminated inline table");
|
|
786
|
+
}
|
|
787
|
+
if (*ps->p == '}') {
|
|
788
|
+
tep_adv(ps, 1);
|
|
789
|
+
break;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
teptris_status fast;
|
|
793
|
+
if (try_keyval_fast(ps, tbl, &fast)) {
|
|
794
|
+
if (fast != TEPTRIS_OK) {
|
|
795
|
+
return fast;
|
|
796
|
+
}
|
|
797
|
+
} else {
|
|
798
|
+
teptris_view kbuf[8];
|
|
799
|
+
teptris_view *parts;
|
|
800
|
+
size_t count;
|
|
801
|
+
st = teptris_parse_key_path(ps, kbuf, 8, &parts, &count);
|
|
802
|
+
if (st != TEPTRIS_OK) {
|
|
803
|
+
return st;
|
|
804
|
+
}
|
|
805
|
+
st = tep_skip_ws(ps);
|
|
806
|
+
if (st != TEPTRIS_OK) {
|
|
807
|
+
return st;
|
|
808
|
+
}
|
|
809
|
+
if (ps->p >= ps->end || *ps->p != '=') {
|
|
810
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
811
|
+
"expected '=' after key");
|
|
812
|
+
}
|
|
813
|
+
tep_adv(ps, 1);
|
|
814
|
+
st = tep_skip_ws(ps);
|
|
815
|
+
if (st != TEPTRIS_OK) {
|
|
816
|
+
return st;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
teptris_node *value;
|
|
820
|
+
st = teptris_parse_value(ps, &value);
|
|
821
|
+
if (st != TEPTRIS_OK) {
|
|
822
|
+
return st;
|
|
823
|
+
}
|
|
824
|
+
st = insert_dotted(ps, tbl, parts, count, value);
|
|
825
|
+
if (st != TEPTRIS_OK) {
|
|
826
|
+
return st;
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
st = skip_array_gap(ps);
|
|
831
|
+
if (st != TEPTRIS_OK) {
|
|
832
|
+
return st;
|
|
833
|
+
}
|
|
834
|
+
if (ps->p < ps->end && *ps->p == ',') {
|
|
835
|
+
tep_adv(ps, 1);
|
|
836
|
+
continue;
|
|
837
|
+
}
|
|
838
|
+
if (ps->p < ps->end && *ps->p == '}') {
|
|
839
|
+
tep_adv(ps, 1);
|
|
840
|
+
break;
|
|
841
|
+
}
|
|
842
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX, "expected ',' or '}'");
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
ps->depth--;
|
|
846
|
+
*out = tbl;
|
|
847
|
+
return TEPTRIS_OK;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
/* ------------------------------------------------------------ main loop -- */
|
|
851
|
+
|
|
852
|
+
teptris_status teptris_parser_run(teptris_document *doc, const char *data,
|
|
853
|
+
size_t len)
|
|
854
|
+
{
|
|
855
|
+
teptris_status st = validate_utf8(doc, data, len);
|
|
856
|
+
if (st != TEPTRIS_OK) {
|
|
857
|
+
return st;
|
|
858
|
+
}
|
|
859
|
+
size_t off = 0;
|
|
860
|
+
if (len >= 3 && (unsigned char)data[0] == 0xEF && (unsigned char)data[1] == 0xBB &&
|
|
861
|
+
(unsigned char)data[2] == 0xBF) {
|
|
862
|
+
off = 3;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
teptris_parser ps;
|
|
866
|
+
memset(&ps, 0, sizeof(ps));
|
|
867
|
+
ps.doc = doc;
|
|
868
|
+
ps.src = data;
|
|
869
|
+
ps.p = data + off;
|
|
870
|
+
ps.end = data + len;
|
|
871
|
+
ps.max_depth = doc->max_depth ? doc->max_depth : 512;
|
|
872
|
+
|
|
873
|
+
doc->root = teptris_dom_new_table(doc, TBL_EXPLICIT);
|
|
874
|
+
if (doc->root == NULL) {
|
|
875
|
+
doc->err.status = TEPTRIS_ERR_ALLOC;
|
|
876
|
+
snprintf(doc->err_msg, sizeof(doc->err_msg), "out of memory");
|
|
877
|
+
doc->err.message = doc->err_msg;
|
|
878
|
+
return TEPTRIS_ERR_ALLOC;
|
|
879
|
+
}
|
|
880
|
+
ps.cur = doc->root;
|
|
881
|
+
|
|
882
|
+
for (;;) {
|
|
883
|
+
st = tep_skip_ws(&ps);
|
|
884
|
+
if (st != TEPTRIS_OK) {
|
|
885
|
+
return st;
|
|
886
|
+
}
|
|
887
|
+
if (ps.p >= ps.end) {
|
|
888
|
+
break;
|
|
889
|
+
}
|
|
890
|
+
unsigned char c = (unsigned char)*ps.p;
|
|
891
|
+
if (c == '\n') {
|
|
892
|
+
tep_adv(&ps, 1);
|
|
893
|
+
continue;
|
|
894
|
+
}
|
|
895
|
+
if (c == '\r') {
|
|
896
|
+
if (ps.p + 1 < ps.end && ps.p[1] == '\n') {
|
|
897
|
+
tep_adv(&ps, 2);
|
|
898
|
+
continue;
|
|
899
|
+
}
|
|
900
|
+
return tep_fail_at(&ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
901
|
+
"lone carriage return");
|
|
902
|
+
}
|
|
903
|
+
if (c == '#') {
|
|
904
|
+
st = skip_comment(&ps);
|
|
905
|
+
if (st != TEPTRIS_OK) {
|
|
906
|
+
return st;
|
|
907
|
+
}
|
|
908
|
+
continue;
|
|
909
|
+
}
|
|
910
|
+
if (c == '[') {
|
|
911
|
+
teptris_node *target = NULL;
|
|
912
|
+
if (try_header_fast(&ps, &st, &target)) {
|
|
913
|
+
if (st == TEPTRIS_OK) {
|
|
914
|
+
ps.cur = target;
|
|
915
|
+
}
|
|
916
|
+
} else {
|
|
917
|
+
st = parse_header(&ps);
|
|
918
|
+
}
|
|
919
|
+
} else {
|
|
920
|
+
st = parse_keyval(&ps);
|
|
921
|
+
}
|
|
922
|
+
if (st != TEPTRIS_OK) {
|
|
923
|
+
return st;
|
|
924
|
+
}
|
|
925
|
+
st = tep_finish_line(&ps);
|
|
926
|
+
if (st != TEPTRIS_OK) {
|
|
927
|
+
return st;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
return TEPTRIS_OK;
|
|
931
|
+
}
|