teptris 0.2.37-powerpc64le-linux → 0.2.39-powerpc64le-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/version.rb +1 -1
- data/lib/teptris.rb +19 -11
- metadata +35 -3
- /data/lib/{teptris_ext.so → teptris/3.2/teptris_ext.so} +0 -0
|
@@ -0,0 +1,940 @@
|
|
|
1
|
+
#include <errno.h>
|
|
2
|
+
#include <inttypes.h>
|
|
3
|
+
#include <math.h>
|
|
4
|
+
#include <stdio.h>
|
|
5
|
+
#include <stdlib.h>
|
|
6
|
+
#include <string.h>
|
|
7
|
+
|
|
8
|
+
#include "teptris/common/chartype.h"
|
|
9
|
+
#include "teptris/common/simd.h"
|
|
10
|
+
#include "teptris/common/ryu/ryu_parse.h"
|
|
11
|
+
#include "teptris/memory/arena.h"
|
|
12
|
+
#include "teptris/parse/parse.h"
|
|
13
|
+
|
|
14
|
+
#define PUTCH(out, n, ch) \
|
|
15
|
+
do { \
|
|
16
|
+
if (out != NULL) { \
|
|
17
|
+
out[n] = (char)(ch); \
|
|
18
|
+
} \
|
|
19
|
+
(void)(n += 1); \
|
|
20
|
+
} while (0)
|
|
21
|
+
|
|
22
|
+
static void utf8_put(char *out, size_t *n, uint32_t cp)
|
|
23
|
+
{
|
|
24
|
+
if (cp < 0x80) {
|
|
25
|
+
PUTCH(out, *n, cp);
|
|
26
|
+
} else if (cp < 0x800) {
|
|
27
|
+
PUTCH(out, *n, 0xC0 | (cp >> 6));
|
|
28
|
+
PUTCH(out, *n, 0x80 | (cp & 0x3F));
|
|
29
|
+
} else if (cp < 0x10000) {
|
|
30
|
+
PUTCH(out, *n, 0xE0 | (cp >> 12));
|
|
31
|
+
PUTCH(out, *n, 0x80 | ((cp >> 6) & 0x3F));
|
|
32
|
+
PUTCH(out, *n, 0x80 | (cp & 0x3F));
|
|
33
|
+
} else {
|
|
34
|
+
PUTCH(out, *n, 0xF0 | (cp >> 18));
|
|
35
|
+
PUTCH(out, *n, 0x80 | ((cp >> 12) & 0x3F));
|
|
36
|
+
PUTCH(out, *n, 0x80 | ((cp >> 6) & 0x3F));
|
|
37
|
+
PUTCH(out, *n, 0x80 | (cp & 0x3F));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static teptris_status read_hex(teptris_parser *ps, int digits, uint32_t *cp)
|
|
42
|
+
{
|
|
43
|
+
uint32_t v = 0;
|
|
44
|
+
for (int i = 0; i < digits; i++) {
|
|
45
|
+
if (ps->p >= ps->end || !tep_is_hex((unsigned char)*ps->p)) {
|
|
46
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
47
|
+
"invalid unicode escape");
|
|
48
|
+
}
|
|
49
|
+
char c = *ps->p;
|
|
50
|
+
uint32_t d = (c <= '9') ? (uint32_t)(c - '0')
|
|
51
|
+
: (uint32_t)((c | 0x20) - 'a' + 10);
|
|
52
|
+
v = v * 16 + d;
|
|
53
|
+
tep_adv(ps, 1);
|
|
54
|
+
}
|
|
55
|
+
*cp = v;
|
|
56
|
+
return TEPTRIS_OK;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
teptris_status tep_scan_basic(teptris_parser *ps, bool ml, char *out,
|
|
60
|
+
size_t *out_len)
|
|
61
|
+
{
|
|
62
|
+
size_t n = 0;
|
|
63
|
+
for (;;) {
|
|
64
|
+
if (ps->p >= ps->end) {
|
|
65
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
66
|
+
"unterminated string");
|
|
67
|
+
}
|
|
68
|
+
unsigned char c = (unsigned char)*ps->p;
|
|
69
|
+
|
|
70
|
+
if (c == '"') {
|
|
71
|
+
if (!ml) {
|
|
72
|
+
tep_adv(ps, 1);
|
|
73
|
+
*out_len = n;
|
|
74
|
+
return TEPTRIS_OK;
|
|
75
|
+
}
|
|
76
|
+
size_t run = 0;
|
|
77
|
+
const char *q = ps->p;
|
|
78
|
+
while (q < ps->end && *q == '"') {
|
|
79
|
+
run++;
|
|
80
|
+
q++;
|
|
81
|
+
}
|
|
82
|
+
if (run >= 6) {
|
|
83
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
84
|
+
"too many consecutive quotes");
|
|
85
|
+
}
|
|
86
|
+
size_t content = (run >= 3) ? run - 3 : run;
|
|
87
|
+
for (size_t k = 0; k < content; k++) {
|
|
88
|
+
PUTCH(out, n, '"');
|
|
89
|
+
}
|
|
90
|
+
tep_adv(ps, run);
|
|
91
|
+
if (run <= 2) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
*out_len = n;
|
|
95
|
+
return TEPTRIS_OK;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (c == '\\') {
|
|
99
|
+
tep_adv(ps, 1);
|
|
100
|
+
if (ps->p >= ps->end) {
|
|
101
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
102
|
+
"unterminated escape");
|
|
103
|
+
}
|
|
104
|
+
unsigned char e = (unsigned char)*ps->p;
|
|
105
|
+
if (ml && (e == ' ' || e == '\t' || e == '\n' || e == '\r')) {
|
|
106
|
+
const char *q = ps->p;
|
|
107
|
+
while (q < ps->end && (*q == ' ' || *q == '\t')) {
|
|
108
|
+
q++;
|
|
109
|
+
}
|
|
110
|
+
if (q >= ps->end ||
|
|
111
|
+
(*q != '\n' && !(*q == '\r' && q + 1 < ps->end && q[1] == '\n'))) {
|
|
112
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
113
|
+
"invalid escape");
|
|
114
|
+
}
|
|
115
|
+
while (ps->p < ps->end) {
|
|
116
|
+
char w = *ps->p;
|
|
117
|
+
if (w == ' ' || w == '\t' || w == '\n') {
|
|
118
|
+
tep_adv(ps, 1);
|
|
119
|
+
} else if (w == '\r' && ps->p + 1 < ps->end &&
|
|
120
|
+
ps->p[1] == '\n') {
|
|
121
|
+
tep_adv(ps, 2);
|
|
122
|
+
} else {
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
switch (e) {
|
|
129
|
+
case 'b':
|
|
130
|
+
PUTCH(out, n, '\b');
|
|
131
|
+
tep_adv(ps, 1);
|
|
132
|
+
break;
|
|
133
|
+
case 't':
|
|
134
|
+
PUTCH(out, n, '\t');
|
|
135
|
+
tep_adv(ps, 1);
|
|
136
|
+
break;
|
|
137
|
+
case 'n':
|
|
138
|
+
PUTCH(out, n, '\n');
|
|
139
|
+
tep_adv(ps, 1);
|
|
140
|
+
break;
|
|
141
|
+
case 'f':
|
|
142
|
+
PUTCH(out, n, '\f');
|
|
143
|
+
tep_adv(ps, 1);
|
|
144
|
+
break;
|
|
145
|
+
case 'r':
|
|
146
|
+
PUTCH(out, n, '\r');
|
|
147
|
+
tep_adv(ps, 1);
|
|
148
|
+
break;
|
|
149
|
+
case '"':
|
|
150
|
+
PUTCH(out, n, '"');
|
|
151
|
+
tep_adv(ps, 1);
|
|
152
|
+
break;
|
|
153
|
+
case '\\':
|
|
154
|
+
PUTCH(out, n, '\\');
|
|
155
|
+
tep_adv(ps, 1);
|
|
156
|
+
break;
|
|
157
|
+
case 'e': /* TOML 1.1: ESC */
|
|
158
|
+
PUTCH(out, n, 0x1B);
|
|
159
|
+
tep_adv(ps, 1);
|
|
160
|
+
break;
|
|
161
|
+
case 'x': { /* TOML 1.1: \xNN, first 256 codepoints */
|
|
162
|
+
tep_adv(ps, 1);
|
|
163
|
+
uint32_t cp;
|
|
164
|
+
teptris_status st2 = read_hex(ps, 2, &cp);
|
|
165
|
+
if (st2 != TEPTRIS_OK) {
|
|
166
|
+
return st2;
|
|
167
|
+
}
|
|
168
|
+
utf8_put(out, &n, cp);
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
case 'u':
|
|
172
|
+
case 'U': {
|
|
173
|
+
tep_adv(ps, 1);
|
|
174
|
+
uint32_t cp;
|
|
175
|
+
teptris_status st = read_hex(ps, (e == 'u') ? 4 : 8, &cp);
|
|
176
|
+
if (st != TEPTRIS_OK) {
|
|
177
|
+
return st;
|
|
178
|
+
}
|
|
179
|
+
if ((cp >= 0xD800 && cp <= 0xDFFF) || cp > 0x10FFFF) {
|
|
180
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
181
|
+
"invalid unicode scalar value");
|
|
182
|
+
}
|
|
183
|
+
utf8_put(out, &n, cp);
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
default:
|
|
187
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
188
|
+
"invalid escape");
|
|
189
|
+
}
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (c == '\n') {
|
|
194
|
+
if (!ml) {
|
|
195
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
196
|
+
"unterminated string");
|
|
197
|
+
}
|
|
198
|
+
PUTCH(out, n, '\n');
|
|
199
|
+
tep_adv(ps, 1);
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (c == '\r') {
|
|
203
|
+
if (ml && ps->p + 1 < ps->end && ps->p[1] == '\n') {
|
|
204
|
+
PUTCH(out, n, '\r');
|
|
205
|
+
tep_adv(ps, 2);
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
209
|
+
"control character in string");
|
|
210
|
+
}
|
|
211
|
+
if ((c < 0x20 && c != '\t') || c == 0x7F) {
|
|
212
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
213
|
+
"control character in string");
|
|
214
|
+
}
|
|
215
|
+
PUTCH(out, n, c);
|
|
216
|
+
tep_adv(ps, 1);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
teptris_status tep_scan_literal(teptris_parser *ps, bool ml, char *out,
|
|
221
|
+
size_t *out_len)
|
|
222
|
+
{
|
|
223
|
+
size_t n = 0;
|
|
224
|
+
for (;;) {
|
|
225
|
+
if (ps->p >= ps->end) {
|
|
226
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
227
|
+
"unterminated string");
|
|
228
|
+
}
|
|
229
|
+
unsigned char c = (unsigned char)*ps->p;
|
|
230
|
+
|
|
231
|
+
if (c == '\'') {
|
|
232
|
+
if (!ml) {
|
|
233
|
+
tep_adv(ps, 1);
|
|
234
|
+
*out_len = n;
|
|
235
|
+
return TEPTRIS_OK;
|
|
236
|
+
}
|
|
237
|
+
size_t run = 0;
|
|
238
|
+
const char *q = ps->p;
|
|
239
|
+
while (q < ps->end && *q == '\'') {
|
|
240
|
+
run++;
|
|
241
|
+
q++;
|
|
242
|
+
}
|
|
243
|
+
if (run >= 6) {
|
|
244
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
245
|
+
"too many consecutive quotes");
|
|
246
|
+
}
|
|
247
|
+
size_t content = (run >= 3) ? run - 3 : run;
|
|
248
|
+
for (size_t k = 0; k < content; k++) {
|
|
249
|
+
PUTCH(out, n, '\'');
|
|
250
|
+
}
|
|
251
|
+
tep_adv(ps, run);
|
|
252
|
+
if (run <= 2) {
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
*out_len = n;
|
|
256
|
+
return TEPTRIS_OK;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (c == '\n') {
|
|
260
|
+
if (!ml) {
|
|
261
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
262
|
+
"unterminated string");
|
|
263
|
+
}
|
|
264
|
+
PUTCH(out, n, '\n');
|
|
265
|
+
tep_adv(ps, 1);
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
if (c == '\r') {
|
|
269
|
+
if (ml && ps->p + 1 < ps->end && ps->p[1] == '\n') {
|
|
270
|
+
PUTCH(out, n, '\r');
|
|
271
|
+
tep_adv(ps, 2);
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
275
|
+
"control character in string");
|
|
276
|
+
}
|
|
277
|
+
if ((c < 0x20 && c != '\t') || c == 0x7F) {
|
|
278
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
279
|
+
"control character in string");
|
|
280
|
+
}
|
|
281
|
+
PUTCH(out, n, c);
|
|
282
|
+
tep_adv(ps, 1);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/* -------------------------------------------------------------- values -- */
|
|
287
|
+
|
|
288
|
+
bool teptris_try_plain_body(teptris_parser *ps, char close, const char **content,
|
|
289
|
+
size_t *len)
|
|
290
|
+
{
|
|
291
|
+
/* Find the closing quote with memchr (platform SIMD), then reject
|
|
292
|
+
* the span if it contains an escape or control. Two tight passes
|
|
293
|
+
* beat a fused per-byte loop once the body is long enough that
|
|
294
|
+
* memchr's vector path engages; short bodies keep the one-pass
|
|
295
|
+
* loop so we never pay setup on tiny keys. */
|
|
296
|
+
const char *p = ps->p;
|
|
297
|
+
size_t avail = (size_t)(ps->end - p);
|
|
298
|
+
if (avail >= 32) {
|
|
299
|
+
const void *hit = memchr(p, (unsigned char)close, avail);
|
|
300
|
+
if (hit == NULL) {
|
|
301
|
+
return false; /* unterminated */
|
|
302
|
+
}
|
|
303
|
+
size_t off = (size_t)((const char *)hit - p);
|
|
304
|
+
/* reject if escape/control appears before the closer.
|
|
305
|
+
* esc=0 for literals (no escapes); '"' strings forbid '\'. */
|
|
306
|
+
char esc = (close == '"') ? '\\' : 0;
|
|
307
|
+
size_t bad = tep_simd_str_stop(p, off, esc, 0);
|
|
308
|
+
/* str_stop with q=esc also stops on controls; any stop before
|
|
309
|
+
* off means the plain path cannot claim this body. */
|
|
310
|
+
if (bad < off) {
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
*content = p;
|
|
314
|
+
*len = off;
|
|
315
|
+
return true;
|
|
316
|
+
}
|
|
317
|
+
const char *end = ps->end;
|
|
318
|
+
while (p < end) {
|
|
319
|
+
unsigned char c = (unsigned char)*p;
|
|
320
|
+
if (c == (unsigned char)close) {
|
|
321
|
+
*content = ps->p;
|
|
322
|
+
*len = (size_t)(p - ps->p);
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
if (c == '\\' || c == 0x7F || (c < 0x20 && c != '\t')) {
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
p++;
|
|
329
|
+
}
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
static teptris_status parse_string(teptris_parser *ps, teptris_node **out)
|
|
334
|
+
{
|
|
335
|
+
bool basic = *ps->p == '"';
|
|
336
|
+
const char *d = ps->p;
|
|
337
|
+
bool ml = (size_t)(ps->end - ps->p) >= 3 && d[1] == d[0] && d[2] == d[0];
|
|
338
|
+
|
|
339
|
+
tep_adv(ps, ml ? 3 : 1);
|
|
340
|
+
if (ml) {
|
|
341
|
+
if (ps->p + 1 < ps->end && *ps->p == '\r' && ps->p[1] == '\n') {
|
|
342
|
+
tep_adv(ps, 2);
|
|
343
|
+
} else if (ps->p < ps->end && *ps->p == '\n') {
|
|
344
|
+
tep_adv(ps, 1);
|
|
345
|
+
}
|
|
346
|
+
} else {
|
|
347
|
+
const char *content;
|
|
348
|
+
size_t len;
|
|
349
|
+
if (teptris_try_plain_body(ps, basic ? '"' : '\'', &content, &len)) {
|
|
350
|
+
teptris_node *n = teptris_dom_new_node(ps->doc, TEPTRIS_STRING);
|
|
351
|
+
if (n == NULL) {
|
|
352
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_ALLOC, "out of memory");
|
|
353
|
+
}
|
|
354
|
+
char *buf = teptris_arena_alloc(&ps->doc->arena, len + 1);
|
|
355
|
+
if (buf == NULL) {
|
|
356
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_ALLOC, "out of memory");
|
|
357
|
+
}
|
|
358
|
+
memcpy(buf, content, len);
|
|
359
|
+
buf[len] = '\0';
|
|
360
|
+
n->as.str.ptr = buf;
|
|
361
|
+
n->as.str.len = len;
|
|
362
|
+
ps->p += len + 1; /* verified control-free: no newline tracking */
|
|
363
|
+
*out = n;
|
|
364
|
+
return TEPTRIS_OK;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
teptris_parser save = *ps;
|
|
369
|
+
size_t len = 0;
|
|
370
|
+
teptris_status st = basic ? tep_scan_basic(ps, ml, NULL, &len)
|
|
371
|
+
: tep_scan_literal(ps, ml, NULL, &len);
|
|
372
|
+
if (st != TEPTRIS_OK) {
|
|
373
|
+
return st;
|
|
374
|
+
}
|
|
375
|
+
char *buf = teptris_arena_alloc(&ps->doc->arena, len + 1);
|
|
376
|
+
if (buf == NULL) {
|
|
377
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_ALLOC, "out of memory");
|
|
378
|
+
}
|
|
379
|
+
*ps = save;
|
|
380
|
+
st = basic ? tep_scan_basic(ps, ml, buf, &len)
|
|
381
|
+
: tep_scan_literal(ps, ml, buf, &len);
|
|
382
|
+
if (st != TEPTRIS_OK) {
|
|
383
|
+
return st;
|
|
384
|
+
}
|
|
385
|
+
buf[len] = '\0';
|
|
386
|
+
|
|
387
|
+
teptris_node *n = teptris_dom_new_node(ps->doc, TEPTRIS_STRING);
|
|
388
|
+
if (n == NULL) {
|
|
389
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_ALLOC, "out of memory");
|
|
390
|
+
}
|
|
391
|
+
n->as.str.ptr = buf;
|
|
392
|
+
n->as.str.len = len;
|
|
393
|
+
*out = n;
|
|
394
|
+
return TEPTRIS_OK;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
static teptris_status expect_word(teptris_parser *ps, const char *w)
|
|
398
|
+
{
|
|
399
|
+
size_t n = strlen(w);
|
|
400
|
+
if ((size_t)(ps->end - ps->p) < n || memcmp(ps->p, w, n) != 0) {
|
|
401
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX, "invalid value");
|
|
402
|
+
}
|
|
403
|
+
tep_adv(ps, n);
|
|
404
|
+
return TEPTRIS_OK;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
static bool digit_for(unsigned char c, int radix)
|
|
408
|
+
{
|
|
409
|
+
switch (radix) {
|
|
410
|
+
case 10:
|
|
411
|
+
return tep_is_dec(c);
|
|
412
|
+
case 16:
|
|
413
|
+
return tep_is_hex(c);
|
|
414
|
+
case 8:
|
|
415
|
+
return c >= '0' && c <= '7';
|
|
416
|
+
case 2:
|
|
417
|
+
return c == '0' || c == '1';
|
|
418
|
+
default:
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
static unsigned hexval(char c)
|
|
424
|
+
{
|
|
425
|
+
return (c <= '9') ? (unsigned)(c - '0')
|
|
426
|
+
: (unsigned)((c | 0x20) - 'a' + 10);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
#define TEP_NUM_MAX 512
|
|
430
|
+
|
|
431
|
+
static teptris_status scan_run(teptris_parser *ps, int radix, char *buf,
|
|
432
|
+
size_t *blen)
|
|
433
|
+
{
|
|
434
|
+
size_t n = 0;
|
|
435
|
+
bool prev_digit = false;
|
|
436
|
+
while (ps->p < ps->end) {
|
|
437
|
+
unsigned char c = (unsigned char)*ps->p;
|
|
438
|
+
if (digit_for(c, radix)) {
|
|
439
|
+
if (n == TEP_NUM_MAX - 1) {
|
|
440
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
441
|
+
"number too long");
|
|
442
|
+
}
|
|
443
|
+
buf[n++] = (char)c;
|
|
444
|
+
prev_digit = true;
|
|
445
|
+
tep_adv(ps, 1);
|
|
446
|
+
} else if (c == '_') {
|
|
447
|
+
if (!prev_digit) {
|
|
448
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
449
|
+
"underscore must be between digits");
|
|
450
|
+
}
|
|
451
|
+
prev_digit = false;
|
|
452
|
+
tep_adv(ps, 1);
|
|
453
|
+
} else {
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
buf[n] = '\0';
|
|
458
|
+
if (n == 0) {
|
|
459
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX, "expected digits");
|
|
460
|
+
}
|
|
461
|
+
if (!prev_digit) {
|
|
462
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
463
|
+
"trailing underscore in number");
|
|
464
|
+
}
|
|
465
|
+
*blen = n;
|
|
466
|
+
return TEPTRIS_OK;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
static bool mag_from(const char *s, size_t n, int radix, uint64_t *out)
|
|
470
|
+
{
|
|
471
|
+
uint64_t v = 0;
|
|
472
|
+
for (size_t i = 0; i < n; i++) {
|
|
473
|
+
unsigned d = hexval(s[i]);
|
|
474
|
+
if (v > (UINT64_MAX - d) / (unsigned)radix) {
|
|
475
|
+
return false;
|
|
476
|
+
}
|
|
477
|
+
v = v * (unsigned)radix + d;
|
|
478
|
+
}
|
|
479
|
+
*out = v;
|
|
480
|
+
return true;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
teptris_status teptris_parse_number(teptris_parser *ps, teptris_node **out,
|
|
484
|
+
bool signed_input)
|
|
485
|
+
{
|
|
486
|
+
bool neg = false;
|
|
487
|
+
if (signed_input) {
|
|
488
|
+
neg = (*ps->p == '-');
|
|
489
|
+
tep_adv(ps, 1);
|
|
490
|
+
}
|
|
491
|
+
if (ps->p >= ps->end) {
|
|
492
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX, "invalid number");
|
|
493
|
+
}
|
|
494
|
+
unsigned char c = (unsigned char)*ps->p;
|
|
495
|
+
|
|
496
|
+
if (c == 'i') {
|
|
497
|
+
teptris_status st = expect_word(ps, "inf");
|
|
498
|
+
if (st != TEPTRIS_OK) {
|
|
499
|
+
return st;
|
|
500
|
+
}
|
|
501
|
+
teptris_node *n = teptris_dom_new_node(ps->doc, TEPTRIS_FLOAT);
|
|
502
|
+
if (n == NULL) {
|
|
503
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_ALLOC, "out of memory");
|
|
504
|
+
}
|
|
505
|
+
n->as.f = neg ? -INFINITY : INFINITY;
|
|
506
|
+
*out = n;
|
|
507
|
+
return TEPTRIS_OK;
|
|
508
|
+
}
|
|
509
|
+
if (c == 'n') {
|
|
510
|
+
teptris_status st = expect_word(ps, "nan");
|
|
511
|
+
if (st != TEPTRIS_OK) {
|
|
512
|
+
return st;
|
|
513
|
+
}
|
|
514
|
+
teptris_node *n = teptris_dom_new_node(ps->doc, TEPTRIS_FLOAT);
|
|
515
|
+
if (n == NULL) {
|
|
516
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_ALLOC, "out of memory");
|
|
517
|
+
}
|
|
518
|
+
n->as.f = NAN;
|
|
519
|
+
*out = n;
|
|
520
|
+
return TEPTRIS_OK;
|
|
521
|
+
}
|
|
522
|
+
if (!tep_is_dec(c)) {
|
|
523
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX, "invalid number");
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
char buf[TEP_NUM_MAX];
|
|
527
|
+
size_t blen = 0;
|
|
528
|
+
|
|
529
|
+
if (c == '0' && ps->p + 1 < ps->end) {
|
|
530
|
+
char r = ps->p[1];
|
|
531
|
+
if (r == 'x' || r == 'o' || r == 'b') {
|
|
532
|
+
if (signed_input) {
|
|
533
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
534
|
+
"sign not allowed on radix integer");
|
|
535
|
+
}
|
|
536
|
+
int radix = (r == 'x') ? 16 : (r == 'o') ? 8 : 2;
|
|
537
|
+
tep_adv(ps, 2);
|
|
538
|
+
teptris_status st = scan_run(ps, radix, buf, &blen);
|
|
539
|
+
if (st != TEPTRIS_OK) {
|
|
540
|
+
return st;
|
|
541
|
+
}
|
|
542
|
+
uint64_t mag;
|
|
543
|
+
if (!mag_from(buf, blen, radix, &mag) || mag > (uint64_t)INT64_MAX) {
|
|
544
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
545
|
+
"integer out of range");
|
|
546
|
+
}
|
|
547
|
+
teptris_node *n = teptris_dom_new_node(ps->doc, TEPTRIS_INTEGER);
|
|
548
|
+
if (n == NULL) {
|
|
549
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_ALLOC, "out of memory");
|
|
550
|
+
}
|
|
551
|
+
n->as.i = (int64_t)mag;
|
|
552
|
+
*out = n;
|
|
553
|
+
return TEPTRIS_OK;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/* Decimal fast path: plain digits, no underscores, <=18 of them.
|
|
558
|
+
* Digits cannot contain newlines, so the cursor moves directly. */
|
|
559
|
+
{
|
|
560
|
+
const char *dstart = ps->p;
|
|
561
|
+
uint64_t mag = 0;
|
|
562
|
+
while (ps->p < ps->end && tep_is_dec((unsigned char)*ps->p)) {
|
|
563
|
+
mag = mag * 10 + (uint64_t)((unsigned char)*ps->p - '0');
|
|
564
|
+
ps->p++;
|
|
565
|
+
}
|
|
566
|
+
size_t dlen = (size_t)(ps->p - dstart);
|
|
567
|
+
bool plain = dlen > 0 && dlen <= 18 &&
|
|
568
|
+
!(ps->p < ps->end && *ps->p == '_');
|
|
569
|
+
if (plain) {
|
|
570
|
+
if (dstart[0] == '0' && dlen > 1) {
|
|
571
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
572
|
+
"leading zero in number");
|
|
573
|
+
}
|
|
574
|
+
bool is_float = ps->p < ps->end &&
|
|
575
|
+
(*ps->p == '.' || *ps->p == 'e' || *ps->p == 'E');
|
|
576
|
+
if (!is_float) {
|
|
577
|
+
teptris_node *n = teptris_dom_new_node(ps->doc, TEPTRIS_INTEGER);
|
|
578
|
+
if (n == NULL) {
|
|
579
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_ALLOC,
|
|
580
|
+
"out of memory");
|
|
581
|
+
}
|
|
582
|
+
n->as.i = neg ? -(int64_t)mag : (int64_t)mag;
|
|
583
|
+
*out = n;
|
|
584
|
+
return TEPTRIS_OK;
|
|
585
|
+
}
|
|
586
|
+
/* Zero-copy fast path: peek the frac/exp spans without
|
|
587
|
+
* copying, then let ryu read the contiguous input span —
|
|
588
|
+
* '-' is part of it; a leading '+' (ryu rejects it) falls
|
|
589
|
+
* through to the copying path. */
|
|
590
|
+
if (!signed_input || neg) {
|
|
591
|
+
const char *q = ps->p; /* at '.', 'e' or 'E' */
|
|
592
|
+
bool ok = true;
|
|
593
|
+
uint64_t fmag = mag; /* frac digits continue the mantissa */
|
|
594
|
+
size_t fdig = 0;
|
|
595
|
+
if (q < ps->end && *q == '.') {
|
|
596
|
+
q++;
|
|
597
|
+
if (q < ps->end && tep_is_dec((unsigned char)*q)) {
|
|
598
|
+
while (q < ps->end && tep_is_dec((unsigned char)*q)) {
|
|
599
|
+
if (fdig < 18) {
|
|
600
|
+
fmag = fmag * 10 +
|
|
601
|
+
(uint64_t)((unsigned char)*q - '0');
|
|
602
|
+
fdig++;
|
|
603
|
+
}
|
|
604
|
+
q++;
|
|
605
|
+
}
|
|
606
|
+
if (q < ps->end && *q == '_') {
|
|
607
|
+
ok = false;
|
|
608
|
+
}
|
|
609
|
+
} else {
|
|
610
|
+
ok = false; /* copy path reports "expected digits" */
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
long e10 = 0;
|
|
614
|
+
bool e_of = false;
|
|
615
|
+
if (ok && q < ps->end && (*q == 'e' || *q == 'E')) {
|
|
616
|
+
q++;
|
|
617
|
+
bool eneg = false;
|
|
618
|
+
if (q < ps->end && (*q == '+' || *q == '-')) {
|
|
619
|
+
eneg = (*q == '-');
|
|
620
|
+
q++;
|
|
621
|
+
}
|
|
622
|
+
if (q < ps->end && tep_is_dec((unsigned char)*q)) {
|
|
623
|
+
while (q < ps->end && tep_is_dec((unsigned char)*q)) {
|
|
624
|
+
if (e10 < 100000) {
|
|
625
|
+
e10 = e10 * 10 +
|
|
626
|
+
(long)((unsigned char)*q - '0');
|
|
627
|
+
}
|
|
628
|
+
q++;
|
|
629
|
+
}
|
|
630
|
+
if (q < ps->end && *q == '_') {
|
|
631
|
+
ok = false;
|
|
632
|
+
}
|
|
633
|
+
if (eneg) {
|
|
634
|
+
e10 = -e10;
|
|
635
|
+
}
|
|
636
|
+
if (e10 > 400 || e10 < -400) {
|
|
637
|
+
e_of = true; /* ryu decides: inf/subnormal paths */
|
|
638
|
+
}
|
|
639
|
+
} else {
|
|
640
|
+
ok = false;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
if (ok) {
|
|
644
|
+
/* Clinger fast path: <=15 significant digits and a
|
|
645
|
+
* decimal exponent within [-22, 22] combine via one
|
|
646
|
+
* exact-power multiply/divide — both IEEE ops are
|
|
647
|
+
* correctly rounded, so the result matches ryu. */
|
|
648
|
+
long dexp = e10 - (long)fdig;
|
|
649
|
+
size_t sig = dlen + fdig;
|
|
650
|
+
if (!e_of && sig <= 15 && dexp >= -22 && dexp <= 22) {
|
|
651
|
+
static const double pow10[23] = {
|
|
652
|
+
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7,
|
|
653
|
+
1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
|
|
654
|
+
1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
|
|
655
|
+
double base = (double)fmag;
|
|
656
|
+
double dv = (dexp >= 0) ? base * pow10[dexp]
|
|
657
|
+
: base / pow10[-dexp];
|
|
658
|
+
if (neg) {
|
|
659
|
+
dv = -dv;
|
|
660
|
+
}
|
|
661
|
+
teptris_node *n =
|
|
662
|
+
teptris_dom_new_node(ps->doc, TEPTRIS_FLOAT);
|
|
663
|
+
if (n == NULL) {
|
|
664
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_ALLOC,
|
|
665
|
+
"out of memory");
|
|
666
|
+
}
|
|
667
|
+
n->as.f = dv;
|
|
668
|
+
ps->p = (char *)q; /* digits only: no newlines */
|
|
669
|
+
*out = n;
|
|
670
|
+
return TEPTRIS_OK;
|
|
671
|
+
}
|
|
672
|
+
const char *nstart = neg ? dstart - 1 : dstart;
|
|
673
|
+
size_t nlen = (size_t)(q - nstart);
|
|
674
|
+
double dv = 0.0;
|
|
675
|
+
if (teptris_ryu_s2d_n(nstart, (int)nlen, &dv) ==
|
|
676
|
+
TEPTRIS_RYU_SUCCESS) {
|
|
677
|
+
teptris_node *n =
|
|
678
|
+
teptris_dom_new_node(ps->doc, TEPTRIS_FLOAT);
|
|
679
|
+
if (n == NULL) {
|
|
680
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_ALLOC,
|
|
681
|
+
"out of memory");
|
|
682
|
+
}
|
|
683
|
+
n->as.f = dv;
|
|
684
|
+
ps->p = (char *)q; /* digits only: no newlines */
|
|
685
|
+
*out = n;
|
|
686
|
+
return TEPTRIS_OK;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
char fbuf[TEP_NUM_MAX * 2 + 8];
|
|
692
|
+
size_t fn = 0;
|
|
693
|
+
if (neg) {
|
|
694
|
+
fbuf[fn++] = '-';
|
|
695
|
+
}
|
|
696
|
+
memcpy(fbuf + fn, dstart, dlen);
|
|
697
|
+
fn += dlen;
|
|
698
|
+
if (*ps->p == '.') {
|
|
699
|
+
fbuf[fn++] = '.';
|
|
700
|
+
ps->p++;
|
|
701
|
+
const char *fs = ps->p;
|
|
702
|
+
while (ps->p < ps->end && tep_is_dec((unsigned char)*ps->p)) {
|
|
703
|
+
if (fn < sizeof(fbuf) - 24) {
|
|
704
|
+
fbuf[fn++] = *ps->p;
|
|
705
|
+
}
|
|
706
|
+
ps->p++;
|
|
707
|
+
}
|
|
708
|
+
if (fs == ps->p) {
|
|
709
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
710
|
+
"expected digits");
|
|
711
|
+
}
|
|
712
|
+
if (ps->p < ps->end && *ps->p == '_') {
|
|
713
|
+
ps->p = dstart;
|
|
714
|
+
goto slow_decimal;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
if (ps->p < ps->end && (*ps->p == 'e' || *ps->p == 'E')) {
|
|
718
|
+
fbuf[fn++] = 'e';
|
|
719
|
+
ps->p++;
|
|
720
|
+
if (ps->p < ps->end && (*ps->p == '+' || *ps->p == '-')) {
|
|
721
|
+
fbuf[fn++] = *ps->p;
|
|
722
|
+
ps->p++;
|
|
723
|
+
}
|
|
724
|
+
const char *es = ps->p;
|
|
725
|
+
while (ps->p < ps->end && tep_is_dec((unsigned char)*ps->p)) {
|
|
726
|
+
if (fn < sizeof(fbuf) - 8) {
|
|
727
|
+
fbuf[fn++] = *ps->p;
|
|
728
|
+
}
|
|
729
|
+
ps->p++;
|
|
730
|
+
}
|
|
731
|
+
if (es == ps->p) {
|
|
732
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
733
|
+
"expected digits");
|
|
734
|
+
}
|
|
735
|
+
if (ps->p < ps->end && *ps->p == '_') {
|
|
736
|
+
ps->p = dstart;
|
|
737
|
+
goto slow_decimal;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
fbuf[fn] = '\0';
|
|
741
|
+
teptris_node *n = teptris_dom_new_node(ps->doc, TEPTRIS_FLOAT);
|
|
742
|
+
if (n == NULL) {
|
|
743
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_ALLOC, "out of memory");
|
|
744
|
+
}
|
|
745
|
+
double d = 0.0;
|
|
746
|
+
if (teptris_ryu_s2d_n(fbuf, (int)fn, &d) != TEPTRIS_RYU_SUCCESS) {
|
|
747
|
+
d = strtod(fbuf, NULL);
|
|
748
|
+
}
|
|
749
|
+
n->as.f = d;
|
|
750
|
+
*out = n;
|
|
751
|
+
return TEPTRIS_OK;
|
|
752
|
+
}
|
|
753
|
+
if (dlen > 0) {
|
|
754
|
+
ps->p = dstart; /* underscores or >18 digits: generic path */
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
slow_decimal:;
|
|
759
|
+
teptris_status st = scan_run(ps, 10, buf, &blen);
|
|
760
|
+
if (st != TEPTRIS_OK) {
|
|
761
|
+
return st;
|
|
762
|
+
}
|
|
763
|
+
if (buf[0] == '0' && blen > 1) {
|
|
764
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
765
|
+
"leading zero in number");
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
bool is_float = ps->p < ps->end && (*ps->p == '.' || *ps->p == 'e' ||
|
|
769
|
+
*ps->p == 'E');
|
|
770
|
+
if (is_float) {
|
|
771
|
+
char fbuf[TEP_NUM_MAX * 2 + 8];
|
|
772
|
+
size_t fn = 0;
|
|
773
|
+
if (neg) {
|
|
774
|
+
fbuf[fn++] = '-';
|
|
775
|
+
}
|
|
776
|
+
memcpy(fbuf + fn, buf, blen);
|
|
777
|
+
fn += blen;
|
|
778
|
+
if (*ps->p == '.') {
|
|
779
|
+
fbuf[fn++] = '.';
|
|
780
|
+
tep_adv(ps, 1);
|
|
781
|
+
char frac[TEP_NUM_MAX];
|
|
782
|
+
size_t flen = 0;
|
|
783
|
+
st = scan_run(ps, 10, frac, &flen);
|
|
784
|
+
if (st != TEPTRIS_OK) {
|
|
785
|
+
return st;
|
|
786
|
+
}
|
|
787
|
+
memcpy(fbuf + fn, frac, flen);
|
|
788
|
+
fn += flen;
|
|
789
|
+
}
|
|
790
|
+
if (ps->p < ps->end && (*ps->p == 'e' || *ps->p == 'E')) {
|
|
791
|
+
fbuf[fn++] = 'e';
|
|
792
|
+
tep_adv(ps, 1);
|
|
793
|
+
if (ps->p < ps->end && (*ps->p == '+' || *ps->p == '-')) {
|
|
794
|
+
fbuf[fn++] = *ps->p;
|
|
795
|
+
tep_adv(ps, 1);
|
|
796
|
+
}
|
|
797
|
+
char ex[TEP_NUM_MAX];
|
|
798
|
+
size_t elen = 0;
|
|
799
|
+
st = scan_run(ps, 10, ex, &elen);
|
|
800
|
+
if (st != TEPTRIS_OK) {
|
|
801
|
+
return st;
|
|
802
|
+
}
|
|
803
|
+
memcpy(fbuf + fn, ex, elen);
|
|
804
|
+
fn += elen;
|
|
805
|
+
}
|
|
806
|
+
fbuf[fn] = '\0';
|
|
807
|
+
teptris_node *n = teptris_dom_new_node(ps->doc, TEPTRIS_FLOAT);
|
|
808
|
+
if (n == NULL) {
|
|
809
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_ALLOC, "out of memory");
|
|
810
|
+
}
|
|
811
|
+
double d = 0.0;
|
|
812
|
+
if (teptris_ryu_s2d_n(fbuf, (int)fn, &d) != TEPTRIS_RYU_SUCCESS) {
|
|
813
|
+
d = strtod(fbuf, NULL); /* >17 significant digits: ryu gap */
|
|
814
|
+
}
|
|
815
|
+
n->as.f = d;
|
|
816
|
+
*out = n;
|
|
817
|
+
return TEPTRIS_OK;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
uint64_t mag;
|
|
821
|
+
if (!mag_from(buf, blen, 10, &mag)) {
|
|
822
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX, "integer out of range");
|
|
823
|
+
}
|
|
824
|
+
teptris_node *n = teptris_dom_new_node(ps->doc, TEPTRIS_INTEGER);
|
|
825
|
+
if (n == NULL) {
|
|
826
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_ALLOC, "out of memory");
|
|
827
|
+
}
|
|
828
|
+
if (neg) {
|
|
829
|
+
if (mag > 9223372036854775808ULL) {
|
|
830
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
831
|
+
"integer out of range");
|
|
832
|
+
}
|
|
833
|
+
n->as.i = (mag == 9223372036854775808ULL) ? INT64_MIN : -(int64_t)mag;
|
|
834
|
+
} else {
|
|
835
|
+
if (mag > (uint64_t)INT64_MAX) {
|
|
836
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
837
|
+
"integer out of range");
|
|
838
|
+
}
|
|
839
|
+
n->as.i = (int64_t)mag;
|
|
840
|
+
}
|
|
841
|
+
*out = n;
|
|
842
|
+
return TEPTRIS_OK;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/* Value dispatch with the plain-integer hot path: digit-led values
|
|
846
|
+
* parse inline (Horner + arena node) when the token is a plain
|
|
847
|
+
* <=18-digit int; everything else defers to the general parser.
|
|
848
|
+
* Kept out-of-line on purpose: inlining it into the keyval/array
|
|
849
|
+
* loops bloated their footprints and cost string-heavy shapes more
|
|
850
|
+
* than the int path gained (measured). */
|
|
851
|
+
teptris_status teptris_parse_value_fast(teptris_parser *ps, teptris_node **out)
|
|
852
|
+
{
|
|
853
|
+
if (ps->p < ps->end) {
|
|
854
|
+
unsigned char c = (unsigned char)*ps->p;
|
|
855
|
+
if (c >= '0' && c <= '9') {
|
|
856
|
+
if (teptris_datetime_lookahead(ps)) {
|
|
857
|
+
/* straight to the datetime parser: falling through
|
|
858
|
+
* parse_value would re-run the lookahead (full digit
|
|
859
|
+
* re-verification) on every datetime value */
|
|
860
|
+
return teptris_parse_datetime(ps, out);
|
|
861
|
+
}
|
|
862
|
+
const char *start = ps->p;
|
|
863
|
+
uint64_t mag = 0;
|
|
864
|
+
while (ps->p < ps->end && (unsigned char)*ps->p >= '0' &&
|
|
865
|
+
(unsigned char)*ps->p <= '9') {
|
|
866
|
+
mag = mag * 10 + (uint64_t)((unsigned char)*ps->p - '0');
|
|
867
|
+
ps->p++;
|
|
868
|
+
}
|
|
869
|
+
size_t dlen = (size_t)(ps->p - start);
|
|
870
|
+
if (dlen > 0 && dlen <= 18 && start[0] != '0' &&
|
|
871
|
+
!(ps->p < ps->end &&
|
|
872
|
+
((unsigned char)*ps->p == '.' ||
|
|
873
|
+
(unsigned char)*ps->p == 'e' || (unsigned char)*ps->p == 'E' ||
|
|
874
|
+
(unsigned char)*ps->p == '_'))) {
|
|
875
|
+
teptris_node *n = teptris_arena_fast_alloc(
|
|
876
|
+
&ps->doc->arena, sizeof(teptris_node));
|
|
877
|
+
if (n == NULL) {
|
|
878
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_ALLOC,
|
|
879
|
+
"out of memory");
|
|
880
|
+
}
|
|
881
|
+
memset(n, 0, sizeof(*n));
|
|
882
|
+
n->kind = (uint8_t)TEPTRIS_INTEGER;
|
|
883
|
+
n->as.i = (int64_t)mag;
|
|
884
|
+
*out = n;
|
|
885
|
+
return TEPTRIS_OK;
|
|
886
|
+
}
|
|
887
|
+
ps->p = start;
|
|
888
|
+
/* float/radix/underscore shapes: parse_number directly —
|
|
889
|
+
* parse_value would redo the switch + lookahead first */
|
|
890
|
+
return teptris_parse_number(ps, out, false);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
return teptris_parse_value(ps, out);
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
teptris_status teptris_parse_value(teptris_parser *ps, teptris_node **out)
|
|
897
|
+
{
|
|
898
|
+
if (ps->p >= ps->end) {
|
|
899
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX, "expected value");
|
|
900
|
+
}
|
|
901
|
+
unsigned char c = (unsigned char)*ps->p;
|
|
902
|
+
|
|
903
|
+
switch (c) {
|
|
904
|
+
case '"':
|
|
905
|
+
case '\'':
|
|
906
|
+
return parse_string(ps, out);
|
|
907
|
+
case '[':
|
|
908
|
+
return teptris_parse_array(ps, out);
|
|
909
|
+
case '{':
|
|
910
|
+
return teptris_parse_inline(ps, out);
|
|
911
|
+
case 't':
|
|
912
|
+
case 'f': {
|
|
913
|
+
teptris_status st = expect_word(ps, (c == 't') ? "true" : "false");
|
|
914
|
+
if (st != TEPTRIS_OK) {
|
|
915
|
+
return st;
|
|
916
|
+
}
|
|
917
|
+
teptris_node *n = teptris_dom_new_node(ps->doc, TEPTRIS_BOOLEAN);
|
|
918
|
+
if (n == NULL) {
|
|
919
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_ALLOC, "out of memory");
|
|
920
|
+
}
|
|
921
|
+
n->as.b = (c == 't');
|
|
922
|
+
*out = n;
|
|
923
|
+
return TEPTRIS_OK;
|
|
924
|
+
}
|
|
925
|
+
case 'i':
|
|
926
|
+
case 'n':
|
|
927
|
+
return teptris_parse_number(ps, out, false);
|
|
928
|
+
case '+':
|
|
929
|
+
case '-':
|
|
930
|
+
return teptris_parse_number(ps, out, true);
|
|
931
|
+
default:
|
|
932
|
+
if (teptris_datetime_lookahead(ps)) {
|
|
933
|
+
return teptris_parse_datetime(ps, out);
|
|
934
|
+
}
|
|
935
|
+
if (tep_is_dec(c)) {
|
|
936
|
+
return teptris_parse_number(ps, out, false);
|
|
937
|
+
}
|
|
938
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX, "expected value");
|
|
939
|
+
}
|
|
940
|
+
}
|