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,11 @@
|
|
|
1
|
+
#ifndef TEPTRIS_EMITTER_H
|
|
2
|
+
#define TEPTRIS_EMITTER_H
|
|
3
|
+
|
|
4
|
+
#include "teptris/teptris.h"
|
|
5
|
+
|
|
6
|
+
teptris_status teptris_emit_document(const teptris_document *doc, char **buf,
|
|
7
|
+
size_t *len);
|
|
8
|
+
teptris_status teptris_emit_document_json(const teptris_document *doc,
|
|
9
|
+
char **buf, size_t *len);
|
|
10
|
+
|
|
11
|
+
#endif /* TEPTRIS_EMITTER_H */
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#include "teptris/memory/arena.h"
|
|
2
|
+
|
|
3
|
+
#include <stdint.h>
|
|
4
|
+
#include <stdlib.h>
|
|
5
|
+
#include <string.h>
|
|
6
|
+
|
|
7
|
+
#define TEPTRIS_ARENA_BLOCK_SIZE (64u * 1024u)
|
|
8
|
+
#define TEPTRIS_ARENA_ALIGN 16u
|
|
9
|
+
|
|
10
|
+
static size_t round_up(size_t n, size_t align)
|
|
11
|
+
{
|
|
12
|
+
return (n + align - 1) & ~(align - 1);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/* Block header size rounded so the data area starts 16-byte aligned. */
|
|
16
|
+
static size_t block_data_offset(void)
|
|
17
|
+
{
|
|
18
|
+
return round_up(sizeof(teptris_arena_block), TEPTRIS_ARENA_ALIGN);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
void teptris_arena_init(teptris_arena *arena)
|
|
22
|
+
{
|
|
23
|
+
arena->head = NULL;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static teptris_arena_block *block_new(size_t cap)
|
|
27
|
+
{
|
|
28
|
+
size_t data_off = block_data_offset();
|
|
29
|
+
teptris_arena_block *b = malloc(data_off + cap);
|
|
30
|
+
if (b == NULL) {
|
|
31
|
+
return NULL;
|
|
32
|
+
}
|
|
33
|
+
b->next = NULL;
|
|
34
|
+
b->used = 0;
|
|
35
|
+
b->cap = cap;
|
|
36
|
+
return b;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static char *block_data(teptris_arena_block *b)
|
|
40
|
+
{
|
|
41
|
+
return (char *)b + block_data_offset();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
void *teptris_arena_alloc(teptris_arena *arena, size_t size)
|
|
45
|
+
{
|
|
46
|
+
size_t need = round_up(size, TEPTRIS_ARENA_ALIGN);
|
|
47
|
+
teptris_arena_block *b = arena->head;
|
|
48
|
+
|
|
49
|
+
if (b != NULL && b->used + need <= b->cap) {
|
|
50
|
+
void *p = block_data(b) + b->used;
|
|
51
|
+
b->used += need;
|
|
52
|
+
return p;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
size_t cap = TEPTRIS_ARENA_BLOCK_SIZE;
|
|
56
|
+
if (need > cap) {
|
|
57
|
+
cap = need;
|
|
58
|
+
}
|
|
59
|
+
/* Geometric growth: fewer blocks means fewer malloc/free (and free's
|
|
60
|
+
* madvise) calls on large documents. */
|
|
61
|
+
if (arena->head != NULL && arena->head->cap * 2 > cap) {
|
|
62
|
+
cap = arena->head->cap * 2;
|
|
63
|
+
}
|
|
64
|
+
teptris_arena_block *nb = block_new(cap);
|
|
65
|
+
if (nb == NULL) {
|
|
66
|
+
return NULL;
|
|
67
|
+
}
|
|
68
|
+
nb->next = arena->head;
|
|
69
|
+
arena->head = nb;
|
|
70
|
+
void *p = block_data(nb) + nb->used;
|
|
71
|
+
nb->used += need;
|
|
72
|
+
return p;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
void *teptris_arena_try_grow(teptris_arena *arena, void *last, size_t old_size,
|
|
76
|
+
size_t new_size)
|
|
77
|
+
{
|
|
78
|
+
teptris_arena_block *b = arena->head;
|
|
79
|
+
if (b == NULL || last == NULL) {
|
|
80
|
+
return NULL;
|
|
81
|
+
}
|
|
82
|
+
size_t old_rounded = round_up(old_size, TEPTRIS_ARENA_ALIGN);
|
|
83
|
+
size_t new_rounded = round_up(new_size, TEPTRIS_ARENA_ALIGN);
|
|
84
|
+
if (new_size < old_size) {
|
|
85
|
+
return NULL;
|
|
86
|
+
}
|
|
87
|
+
if ((char *)last + old_rounded == block_data(b) + b->used &&
|
|
88
|
+
b->used + (new_rounded - old_rounded) <= b->cap) {
|
|
89
|
+
b->used += new_rounded - old_rounded;
|
|
90
|
+
return last;
|
|
91
|
+
}
|
|
92
|
+
return NULL;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
void teptris_arena_destroy(teptris_arena *arena)
|
|
96
|
+
{
|
|
97
|
+
teptris_arena_block *b = arena->head;
|
|
98
|
+
while (b != NULL) {
|
|
99
|
+
teptris_arena_block *next = b->next;
|
|
100
|
+
free(b);
|
|
101
|
+
b = next;
|
|
102
|
+
}
|
|
103
|
+
arena->head = NULL;
|
|
104
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#ifndef TEPTRIS_ARENA_H
|
|
2
|
+
#define TEPTRIS_ARENA_H
|
|
3
|
+
|
|
4
|
+
#include <stddef.h>
|
|
5
|
+
|
|
6
|
+
#ifdef __cplusplus
|
|
7
|
+
extern "C" {
|
|
8
|
+
#endif
|
|
9
|
+
|
|
10
|
+
/* Per-document bump arena: every allocation reachable from a document is
|
|
11
|
+
* carved from here and released in one pass at document free. */
|
|
12
|
+
typedef struct teptris_arena_block {
|
|
13
|
+
struct teptris_arena_block *next;
|
|
14
|
+
size_t used;
|
|
15
|
+
size_t cap;
|
|
16
|
+
/* data follows, 16-byte aligned */
|
|
17
|
+
} teptris_arena_block;
|
|
18
|
+
|
|
19
|
+
typedef struct teptris_arena {
|
|
20
|
+
teptris_arena_block *head; /* most recently allocated block */
|
|
21
|
+
} teptris_arena;
|
|
22
|
+
|
|
23
|
+
void teptris_arena_init(teptris_arena *arena);
|
|
24
|
+
void *teptris_arena_alloc(teptris_arena *arena, size_t size);
|
|
25
|
+
|
|
26
|
+
/* Inline bump: the hot path (node/table-entry allocation) is three
|
|
27
|
+
* instructions when the head block has room; falls back to the
|
|
28
|
+
* out-of-line allocator for a new block. Must mirror arena.c's
|
|
29
|
+
* block_data offset exactly (header rounded to 16). */
|
|
30
|
+
static inline void *teptris_arena_fast_alloc(teptris_arena *arena, size_t size)
|
|
31
|
+
{
|
|
32
|
+
size_t need = (size + 15) & ~(size_t)15;
|
|
33
|
+
teptris_arena_block *b = arena->head;
|
|
34
|
+
if (b != NULL && b->used + need <= b->cap) {
|
|
35
|
+
char *base = (char *)b +
|
|
36
|
+
((sizeof(teptris_arena_block) + 15) & ~(size_t)15);
|
|
37
|
+
b->used += need;
|
|
38
|
+
return base + b->used - need;
|
|
39
|
+
}
|
|
40
|
+
return teptris_arena_alloc(arena, size);
|
|
41
|
+
}
|
|
42
|
+
/* Grow the most recent arena allocation in place when possible.
|
|
43
|
+
* Returns the (possibly moved) pointer, or NULL if `last` is not the
|
|
44
|
+
* tail — caller then allocates+copies itself. */
|
|
45
|
+
void *teptris_arena_try_grow(teptris_arena *arena, void *last, size_t old_size,
|
|
46
|
+
size_t new_size);
|
|
47
|
+
void teptris_arena_destroy(teptris_arena *arena);
|
|
48
|
+
|
|
49
|
+
#ifdef __cplusplus
|
|
50
|
+
}
|
|
51
|
+
#endif
|
|
52
|
+
|
|
53
|
+
#endif /* TEPTRIS_ARENA_H */
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
#include <string.h>
|
|
2
|
+
|
|
3
|
+
#include "teptris/common/chartype.h"
|
|
4
|
+
#include "teptris/memory/arena.h"
|
|
5
|
+
#include "teptris/parse/parse.h"
|
|
6
|
+
|
|
7
|
+
static int d2(const char *p)
|
|
8
|
+
{
|
|
9
|
+
return (p[0] - '0') * 10 + (p[1] - '0');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
static int num4(const char *p)
|
|
13
|
+
{
|
|
14
|
+
return d2(p) * 100 + d2(p + 2);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static bool is_dig(const char *p)
|
|
18
|
+
{
|
|
19
|
+
return tep_is_dec((unsigned char)*p);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static int days_in_month(int y, int m)
|
|
23
|
+
{
|
|
24
|
+
static const int d[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
|
25
|
+
if (m == 2) {
|
|
26
|
+
bool leap = (y % 4 == 0 && y % 100 != 0) || y % 400 == 0;
|
|
27
|
+
return leap ? 29 : 28;
|
|
28
|
+
}
|
|
29
|
+
return d[m - 1];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
bool teptris_datetime_lookahead(const teptris_parser *ps)
|
|
33
|
+
{
|
|
34
|
+
const char *p = ps->p;
|
|
35
|
+
size_t avail = (size_t)(ps->end - p);
|
|
36
|
+
/* Separator bytes first: every int/float value pays this check on
|
|
37
|
+
* its way to parse_number, and plain numbers fail both in two
|
|
38
|
+
* loads (p[4] is a digit or delimiter, never '-'; p[2] likewise
|
|
39
|
+
* never ':'). Digit verification only runs when a datetime shape
|
|
40
|
+
* is actually possible. */
|
|
41
|
+
if (avail >= 10 && p[4] == '-' && p[7] == '-' && is_dig(p) &&
|
|
42
|
+
is_dig(p + 1) && is_dig(p + 2) && is_dig(p + 3) && is_dig(p + 5) &&
|
|
43
|
+
is_dig(p + 6) && is_dig(p + 8) && is_dig(p + 9)) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
/* TOML 1.1: seconds are optional — HH:MM alone dispatches here
|
|
47
|
+
* (HH:MM:SS still matches: p[5] == ':'). */
|
|
48
|
+
if (avail >= 5 && p[2] == ':' && is_dig(p) && is_dig(p + 1) &&
|
|
49
|
+
is_dig(p + 3) && is_dig(p + 4)) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static teptris_status finish_node(teptris_parser *ps, teptris_kind kind,
|
|
56
|
+
const teptris_datetime *dt, size_t total,
|
|
57
|
+
teptris_node **out)
|
|
58
|
+
{
|
|
59
|
+
teptris_node *n = teptris_dom_new_node(ps->doc, kind);
|
|
60
|
+
if (n == NULL) {
|
|
61
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_ALLOC, "out of memory");
|
|
62
|
+
}
|
|
63
|
+
n->as.dt = *dt;
|
|
64
|
+
/* the consumed span is datetime grammar only — no newline can
|
|
65
|
+
* occur in it, so advance directly (skips tep_adv's memchr) */
|
|
66
|
+
ps->p += total;
|
|
67
|
+
*out = n;
|
|
68
|
+
return TEPTRIS_OK;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
teptris_status teptris_parse_datetime(teptris_parser *ps, teptris_node **out)
|
|
72
|
+
{
|
|
73
|
+
const char *p = ps->p;
|
|
74
|
+
const char *end = ps->end;
|
|
75
|
+
const char *start = p;
|
|
76
|
+
teptris_datetime dt;
|
|
77
|
+
memset(&dt, 0, sizeof(dt));
|
|
78
|
+
|
|
79
|
+
if (p[4] == '-') {
|
|
80
|
+
int y = num4(p);
|
|
81
|
+
int mo = d2(p + 5);
|
|
82
|
+
int da = d2(p + 8);
|
|
83
|
+
if (mo < 1 || mo > 12) {
|
|
84
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX, "invalid month");
|
|
85
|
+
}
|
|
86
|
+
if (da < 1 || da > days_in_month(y, mo)) {
|
|
87
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX, "invalid day");
|
|
88
|
+
}
|
|
89
|
+
dt.year = (int32_t)y;
|
|
90
|
+
dt.month = (uint8_t)mo;
|
|
91
|
+
dt.day = (uint8_t)da;
|
|
92
|
+
p += 10;
|
|
93
|
+
|
|
94
|
+
bool has_time =
|
|
95
|
+
p < end && (*p == 'T' || *p == 't' || *p == ' ') &&
|
|
96
|
+
(size_t)(end - p) >= 6 && is_dig(p + 1) && is_dig(p + 2) &&
|
|
97
|
+
p[3] == ':' && is_dig(p + 4) && is_dig(p + 5);
|
|
98
|
+
if (!has_time) {
|
|
99
|
+
return finish_node(ps, TEPTRIS_DATE_LOCAL, &dt,
|
|
100
|
+
(size_t)(p - start), out);
|
|
101
|
+
}
|
|
102
|
+
p++; /* separator */
|
|
103
|
+
|
|
104
|
+
int h = d2(p);
|
|
105
|
+
int mi = d2(p + 3);
|
|
106
|
+
int s = 0; /* TOML 1.1: seconds optional */
|
|
107
|
+
size_t consumed = 5;
|
|
108
|
+
if (p[5] == ':') {
|
|
109
|
+
if ((size_t)(end - p) < 8 || !is_dig(p + 6) || !is_dig(p + 7)) {
|
|
110
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
111
|
+
"invalid second");
|
|
112
|
+
}
|
|
113
|
+
s = d2(p + 6);
|
|
114
|
+
consumed = 8;
|
|
115
|
+
}
|
|
116
|
+
if (h > 23) {
|
|
117
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX, "invalid hour");
|
|
118
|
+
}
|
|
119
|
+
if (mi > 59) {
|
|
120
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX, "invalid minute");
|
|
121
|
+
}
|
|
122
|
+
if (s > 60) {
|
|
123
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX, "invalid second");
|
|
124
|
+
}
|
|
125
|
+
dt.hour = (uint8_t)h;
|
|
126
|
+
dt.minute = (uint8_t)mi;
|
|
127
|
+
dt.second = (uint8_t)s;
|
|
128
|
+
p += consumed;
|
|
129
|
+
|
|
130
|
+
if (p < end && *p == '.') {
|
|
131
|
+
p++;
|
|
132
|
+
if (p >= end || !is_dig(p)) {
|
|
133
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
134
|
+
"expected fractional digits");
|
|
135
|
+
}
|
|
136
|
+
uint32_t scale = 100000000u; /* 9 digits */
|
|
137
|
+
uint32_t ns = 0;
|
|
138
|
+
while (p < end && is_dig(p)) {
|
|
139
|
+
if (scale > 0) {
|
|
140
|
+
ns += (uint32_t)(*p - '0') * scale;
|
|
141
|
+
scale /= 10;
|
|
142
|
+
}
|
|
143
|
+
p++; /* beyond 9 digits: validated, then truncated */
|
|
144
|
+
}
|
|
145
|
+
dt.nanosecond = ns;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (p < end && (*p == 'Z' || *p == 'z')) {
|
|
149
|
+
dt.offset_seconds = 0;
|
|
150
|
+
p++;
|
|
151
|
+
return finish_node(ps, TEPTRIS_DATETIME_OFFSET, &dt,
|
|
152
|
+
(size_t)(p - start), out);
|
|
153
|
+
}
|
|
154
|
+
if (p < end && (*p == '+' || *p == '-') &&
|
|
155
|
+
(size_t)(end - p) >= 6 && is_dig(p + 1) && is_dig(p + 2) &&
|
|
156
|
+
p[3] == ':' && is_dig(p + 4) && is_dig(p + 5)) {
|
|
157
|
+
int sign = (*p == '-') ? -1 : 1;
|
|
158
|
+
int oh = d2(p + 1);
|
|
159
|
+
int om = d2(p + 4);
|
|
160
|
+
if (oh > 23 || om > 59) {
|
|
161
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
162
|
+
"invalid UTC offset");
|
|
163
|
+
}
|
|
164
|
+
dt.offset_seconds = sign * (oh * 3600 + om * 60);
|
|
165
|
+
p += 6;
|
|
166
|
+
return finish_node(ps, TEPTRIS_DATETIME_OFFSET, &dt,
|
|
167
|
+
(size_t)(p - start), out);
|
|
168
|
+
}
|
|
169
|
+
return finish_node(ps, TEPTRIS_DATETIME_LOCAL, &dt,
|
|
170
|
+
(size_t)(p - start), out);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* time-only; TOML 1.1: seconds optional */
|
|
174
|
+
int h = d2(p);
|
|
175
|
+
int mi = d2(p + 3);
|
|
176
|
+
int s = 0;
|
|
177
|
+
size_t consumed = 5;
|
|
178
|
+
if ((size_t)(end - p) >= 8 && p[5] == ':') {
|
|
179
|
+
if (!is_dig(p + 6) || !is_dig(p + 7)) {
|
|
180
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX, "invalid second");
|
|
181
|
+
}
|
|
182
|
+
s = d2(p + 6);
|
|
183
|
+
consumed = 8;
|
|
184
|
+
}
|
|
185
|
+
if (h > 23) {
|
|
186
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX, "invalid hour");
|
|
187
|
+
}
|
|
188
|
+
if (mi > 59) {
|
|
189
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX, "invalid minute");
|
|
190
|
+
}
|
|
191
|
+
if (s > 60) {
|
|
192
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX, "invalid second");
|
|
193
|
+
}
|
|
194
|
+
dt.hour = (uint8_t)h;
|
|
195
|
+
dt.minute = (uint8_t)mi;
|
|
196
|
+
dt.second = (uint8_t)s;
|
|
197
|
+
p += consumed;
|
|
198
|
+
|
|
199
|
+
if (p < end && *p == '.') {
|
|
200
|
+
p++;
|
|
201
|
+
if (p >= end || !is_dig(p)) {
|
|
202
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
203
|
+
"expected fractional digits");
|
|
204
|
+
}
|
|
205
|
+
uint32_t scale = 100000000u;
|
|
206
|
+
uint32_t ns = 0;
|
|
207
|
+
while (p < end && is_dig(p)) {
|
|
208
|
+
if (scale > 0) {
|
|
209
|
+
ns += (uint32_t)(*p - '0') * scale;
|
|
210
|
+
scale /= 10;
|
|
211
|
+
}
|
|
212
|
+
p++;
|
|
213
|
+
}
|
|
214
|
+
dt.nanosecond = ns;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return finish_node(ps, TEPTRIS_TIME_LOCAL, &dt, (size_t)(p - start), out);
|
|
218
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#include <string.h>
|
|
2
|
+
|
|
3
|
+
#include "teptris/common/chartype.h"
|
|
4
|
+
#include "teptris/memory/arena.h"
|
|
5
|
+
#include "teptris/parse/parse.h"
|
|
6
|
+
|
|
7
|
+
/* Quoted key: single-line basic or literal string, decoded to an
|
|
8
|
+
* arena-owned NUL-terminated view. Opening delimiter at the cursor. */
|
|
9
|
+
static teptris_status quoted_key(teptris_parser *ps, teptris_view *out)
|
|
10
|
+
{
|
|
11
|
+
bool basic = *ps->p == '"';
|
|
12
|
+
tep_adv(ps, 1);
|
|
13
|
+
|
|
14
|
+
const char *content;
|
|
15
|
+
size_t plen;
|
|
16
|
+
if (teptris_try_plain_body(ps, basic ? '"' : '\'', &content, &plen)) {
|
|
17
|
+
char *buf = teptris_arena_alloc(&ps->doc->arena, plen + 1);
|
|
18
|
+
if (buf == NULL) {
|
|
19
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_ALLOC, "out of memory");
|
|
20
|
+
}
|
|
21
|
+
memcpy(buf, content, plen);
|
|
22
|
+
buf[plen] = '\0';
|
|
23
|
+
tep_adv(ps, plen + 1);
|
|
24
|
+
out->ptr = buf;
|
|
25
|
+
out->len = plen;
|
|
26
|
+
return TEPTRIS_OK;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
teptris_parser save = *ps;
|
|
30
|
+
size_t len = 0;
|
|
31
|
+
teptris_status st = basic ? tep_scan_basic(ps, false, NULL, &len)
|
|
32
|
+
: tep_scan_literal(ps, false, NULL, &len);
|
|
33
|
+
if (st != TEPTRIS_OK) {
|
|
34
|
+
return st;
|
|
35
|
+
}
|
|
36
|
+
char *buf = teptris_arena_alloc(&ps->doc->arena, len + 1);
|
|
37
|
+
if (buf == NULL) {
|
|
38
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_ALLOC, "out of memory");
|
|
39
|
+
}
|
|
40
|
+
*ps = save;
|
|
41
|
+
st = basic ? tep_scan_basic(ps, false, buf, &len)
|
|
42
|
+
: tep_scan_literal(ps, false, buf, &len);
|
|
43
|
+
if (st != TEPTRIS_OK) {
|
|
44
|
+
return st;
|
|
45
|
+
}
|
|
46
|
+
buf[len] = '\0';
|
|
47
|
+
out->ptr = buf;
|
|
48
|
+
out->len = len;
|
|
49
|
+
return TEPTRIS_OK;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
teptris_status teptris_parse_key_path(teptris_parser *ps,
|
|
53
|
+
teptris_view *sbuf, size_t scap,
|
|
54
|
+
teptris_view **parts, size_t *count)
|
|
55
|
+
{
|
|
56
|
+
teptris_arena *a = &ps->doc->arena;
|
|
57
|
+
teptris_view *v = sbuf;
|
|
58
|
+
size_t n = 0, cap = scap;
|
|
59
|
+
|
|
60
|
+
for (;;) {
|
|
61
|
+
teptris_status st = tep_skip_ws(ps);
|
|
62
|
+
if (st != TEPTRIS_OK) {
|
|
63
|
+
return st;
|
|
64
|
+
}
|
|
65
|
+
if (ps->p >= ps->end) {
|
|
66
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX, "expected key");
|
|
67
|
+
}
|
|
68
|
+
unsigned char c = (unsigned char)*ps->p;
|
|
69
|
+
|
|
70
|
+
teptris_view key;
|
|
71
|
+
if (c == '"' || c == '\'') {
|
|
72
|
+
st = quoted_key(ps, &key);
|
|
73
|
+
if (st != TEPTRIS_OK) {
|
|
74
|
+
return st;
|
|
75
|
+
}
|
|
76
|
+
} else if (tep_is_barekey(c)) {
|
|
77
|
+
const char *start = ps->p;
|
|
78
|
+
while (ps->p < ps->end && tep_is_barekey((unsigned char)*ps->p)) {
|
|
79
|
+
ps->p++;
|
|
80
|
+
}
|
|
81
|
+
key.ptr = start;
|
|
82
|
+
key.len = (size_t)(ps->p - start);
|
|
83
|
+
} else {
|
|
84
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_SYNTAX,
|
|
85
|
+
"expected key (bare or quoted)");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (n == cap) {
|
|
89
|
+
size_t ncap = cap * 2;
|
|
90
|
+
teptris_view *nv = teptris_arena_try_grow(
|
|
91
|
+
a, v, cap * sizeof(teptris_view), ncap * sizeof(teptris_view));
|
|
92
|
+
if (nv == NULL) {
|
|
93
|
+
nv = teptris_arena_alloc(a, ncap * sizeof(teptris_view));
|
|
94
|
+
if (nv == NULL) {
|
|
95
|
+
return tep_fail_at(ps, NULL, TEPTRIS_ERR_ALLOC,
|
|
96
|
+
"out of memory");
|
|
97
|
+
}
|
|
98
|
+
memcpy(nv, v, n * sizeof(teptris_view));
|
|
99
|
+
v = nv;
|
|
100
|
+
} else {
|
|
101
|
+
v = nv;
|
|
102
|
+
}
|
|
103
|
+
cap = ncap;
|
|
104
|
+
}
|
|
105
|
+
v[n++] = key;
|
|
106
|
+
|
|
107
|
+
st = tep_skip_ws(ps);
|
|
108
|
+
if (st != TEPTRIS_OK) {
|
|
109
|
+
return st;
|
|
110
|
+
}
|
|
111
|
+
if (ps->p < ps->end && *ps->p == '.') {
|
|
112
|
+
tep_adv(ps, 1);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
*parts = v;
|
|
119
|
+
*count = n;
|
|
120
|
+
return TEPTRIS_OK;
|
|
121
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#ifndef TEPTRIS_PARSE_H
|
|
2
|
+
#define TEPTRIS_PARSE_H
|
|
3
|
+
|
|
4
|
+
#include "teptris/dom/dom.h"
|
|
5
|
+
#include "teptris/teptris.h"
|
|
6
|
+
|
|
7
|
+
typedef struct teptris_parser {
|
|
8
|
+
teptris_document *doc;
|
|
9
|
+
const char *src; /* input start (error position rescans) */
|
|
10
|
+
const char *p; /* cursor */
|
|
11
|
+
const char *end; /* one past last input byte */
|
|
12
|
+
uint32_t depth;
|
|
13
|
+
uint32_t max_depth;
|
|
14
|
+
teptris_node *cur; /* open table (root until the first header) */
|
|
15
|
+
} teptris_parser;
|
|
16
|
+
|
|
17
|
+
/* parser.c entry: validates UTF-8, skips BOM, runs the line loop. */
|
|
18
|
+
teptris_status teptris_parser_run(teptris_document *doc, const char *data,
|
|
19
|
+
size_t len);
|
|
20
|
+
|
|
21
|
+
/* Record a failure into doc->err at the current cursor (or `at` when
|
|
22
|
+
* non-NULL). Always returns `code` so callers `return tep_fail(...)`. */
|
|
23
|
+
teptris_status tep_fail_at(teptris_parser *ps, const char *at, teptris_status code,
|
|
24
|
+
const char *fmt, ...);
|
|
25
|
+
|
|
26
|
+
/* Advance the cursor n bytes (clamped to the end). Position state
|
|
27
|
+
* (line/bol) is lazy: tep_fail_at computes it by rescan on error. */
|
|
28
|
+
void tep_adv(teptris_parser *ps, size_t n);
|
|
29
|
+
|
|
30
|
+
/* scalars.c: dispatch on the first byte; parses any value. */
|
|
31
|
+
teptris_status teptris_parse_value(teptris_parser *ps, teptris_node **out);
|
|
32
|
+
teptris_status teptris_parse_value_fast(teptris_parser *ps, teptris_node **out);
|
|
33
|
+
/* Number (int/float/inf/nan); signed_input: cursor at '+'/'-'. */
|
|
34
|
+
teptris_status teptris_parse_number(teptris_parser *ps, teptris_node **out,
|
|
35
|
+
bool signed_input);
|
|
36
|
+
|
|
37
|
+
/* parser.c: composite values. */
|
|
38
|
+
teptris_status teptris_parse_array(teptris_parser *ps, teptris_node **out);
|
|
39
|
+
teptris_status teptris_parse_inline(teptris_parser *ps, teptris_node **out);
|
|
40
|
+
|
|
41
|
+
/* Shared by keys.c for quoted keys. Decode a basic string body (opening
|
|
42
|
+
* quote(s) consumed by the caller) into `out` (NULL = count only). */
|
|
43
|
+
teptris_status tep_scan_basic(teptris_parser *ps, bool multiline, char *out,
|
|
44
|
+
size_t *out_len);
|
|
45
|
+
teptris_status tep_scan_literal(teptris_parser *ps, bool multiline, char *out,
|
|
46
|
+
size_t *out_len);
|
|
47
|
+
|
|
48
|
+
/* scalars.c: fast path — lookahead for a single-line string body with no
|
|
49
|
+
* escapes and no control bytes (the common case). Does not move the
|
|
50
|
+
* cursor; on success the caller copies span→arena and advances len+1. */
|
|
51
|
+
bool teptris_try_plain_body(teptris_parser *ps, char close, const char **content,
|
|
52
|
+
size_t *len);
|
|
53
|
+
|
|
54
|
+
/* keys.c: dotted key path. Parts are views (input or arena copies). */
|
|
55
|
+
/* Parses a dotted key path. `sbuf`/`scap` is a CALLER-OWNED buffer
|
|
56
|
+
* (typically a stack array of 8): paths that fit never allocate, and
|
|
57
|
+
* *parts points into it or into the document arena — valid until the
|
|
58
|
+
* caller's frame ends or the document is freed. */
|
|
59
|
+
teptris_status teptris_parse_key_path(teptris_parser *ps,
|
|
60
|
+
teptris_view *sbuf, size_t scap,
|
|
61
|
+
teptris_view **parts, size_t *count);
|
|
62
|
+
|
|
63
|
+
/* datetime.c */
|
|
64
|
+
bool teptris_datetime_lookahead(const teptris_parser *ps);
|
|
65
|
+
teptris_status teptris_parse_datetime(teptris_parser *ps, teptris_node **out);
|
|
66
|
+
|
|
67
|
+
/* parser.c: skip space/tab (parser loop) — arrays also allow newlines
|
|
68
|
+
* and comments. Both validate comment contents. */
|
|
69
|
+
teptris_status tep_skip_ws(teptris_parser *ps);
|
|
70
|
+
teptris_status tep_skip_ws_nl(teptris_parser *ps);
|
|
71
|
+
/* True at cursor when at '\n', "\r\n" or EOF. */
|
|
72
|
+
bool tep_at_eol(const teptris_parser *ps);
|
|
73
|
+
/* Consume ws, optional comment, and the EOL; error when trailing bytes. */
|
|
74
|
+
teptris_status tep_finish_line(teptris_parser *ps);
|
|
75
|
+
|
|
76
|
+
#endif /* TEPTRIS_PARSE_H */
|