@port-labs/jq-node-bindings 0.0.1
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.
- package/.editorconfig +5 -0
- package/.jshintignore +1 -0
- package/.jshintrc +23 -0
- package/binding.gyp +56 -0
- package/configure +26 -0
- package/deps/jq/.gitattributes +2 -0
- package/deps/jq/.travis.yml +53 -0
- package/deps/jq/AUTHORS +73 -0
- package/deps/jq/COPYING +70 -0
- package/deps/jq/ChangeLog +1349 -0
- package/deps/jq/Makefile.am +198 -0
- package/deps/jq/NEWS +88 -0
- package/deps/jq/README.md +64 -0
- package/deps/jq/builtin.c +1684 -0
- package/deps/jq/builtin.h +10 -0
- package/deps/jq/bytecode.c +161 -0
- package/deps/jq/bytecode.h +92 -0
- package/deps/jq/compile-ios.sh +102 -0
- package/deps/jq/compile.c +1210 -0
- package/deps/jq/compile.h +101 -0
- package/deps/jq/config/m4/check-math-func.m4 +4 -0
- package/deps/jq/config/m4/find-func-no-libs.m4 +8 -0
- package/deps/jq/config/m4/find-func-no-libs2.m4 +62 -0
- package/deps/jq/config/m4/find-func.m4 +9 -0
- package/deps/jq/config/m4/misc.m4 +3 -0
- package/deps/jq/configure.ac +221 -0
- package/deps/jq/docs/Gemfile +7 -0
- package/deps/jq/docs/Gemfile.lock +63 -0
- package/deps/jq/docs/README.md +25 -0
- package/deps/jq/docs/Rakefile +145 -0
- package/deps/jq/docs/content/1.tutorial/default.yml +327 -0
- package/deps/jq/docs/content/2.download/default.yml +117 -0
- package/deps/jq/docs/content/3.manual/manual.yml +2878 -0
- package/deps/jq/docs/content/3.manual/v1.3/manual.yml +1270 -0
- package/deps/jq/docs/content/3.manual/v1.4/manual.yml +1672 -0
- package/deps/jq/docs/content/index/index.yml +51 -0
- package/deps/jq/docs/default_manpage.md +22 -0
- package/deps/jq/docs/public/.htaccess +28 -0
- package/deps/jq/docs/public/bootstrap/css/bootstrap-responsive.css +1058 -0
- package/deps/jq/docs/public/bootstrap/css/bootstrap-responsive.min.css +9 -0
- package/deps/jq/docs/public/bootstrap/css/bootstrap.css +5224 -0
- package/deps/jq/docs/public/bootstrap/css/bootstrap.min.css +9 -0
- package/deps/jq/docs/public/bootstrap/img/glyphicons-halflings-white.png +0 -0
- package/deps/jq/docs/public/bootstrap/img/glyphicons-halflings.png +0 -0
- package/deps/jq/docs/public/bootstrap/js/bootstrap.js +2027 -0
- package/deps/jq/docs/public/bootstrap/js/bootstrap.min.js +6 -0
- package/deps/jq/docs/public/css/base.scss +99 -0
- package/deps/jq/docs/public/jq.png +0 -0
- package/deps/jq/docs/public/robots.txt +2 -0
- package/deps/jq/docs/site.yml +18 -0
- package/deps/jq/docs/templates/default.liquid +34 -0
- package/deps/jq/docs/templates/index.liquid +60 -0
- package/deps/jq/docs/templates/manual.liquid +122 -0
- package/deps/jq/docs/templates/shared/_footer.liquid +5 -0
- package/deps/jq/docs/templates/shared/_head.liquid +12 -0
- package/deps/jq/docs/templates/shared/_header.liquid +26 -0
- package/deps/jq/exec_stack.h +112 -0
- package/deps/jq/execute.c +1155 -0
- package/deps/jq/inject_errors.c +112 -0
- package/deps/jq/jq.1.default +39 -0
- package/deps/jq/jq.1.prebuilt +3075 -0
- package/deps/jq/jq.h +60 -0
- package/deps/jq/jq.spec +70 -0
- package/deps/jq/jq_parser.h +9 -0
- package/deps/jq/jq_test.c +346 -0
- package/deps/jq/jv.c +1333 -0
- package/deps/jq/jv.h +240 -0
- package/deps/jq/jv_alloc.c +179 -0
- package/deps/jq/jv_alloc.h +27 -0
- package/deps/jq/jv_aux.c +619 -0
- package/deps/jq/jv_dtoa.c +4275 -0
- package/deps/jq/jv_dtoa.h +22 -0
- package/deps/jq/jv_file.c +49 -0
- package/deps/jq/jv_parse.c +852 -0
- package/deps/jq/jv_print.c +348 -0
- package/deps/jq/jv_unicode.c +96 -0
- package/deps/jq/jv_unicode.h +11 -0
- package/deps/jq/jv_utf8_tables.h +37 -0
- package/deps/jq/lexer.c +2442 -0
- package/deps/jq/lexer.h +362 -0
- package/deps/jq/lexer.l +184 -0
- package/deps/jq/libm.h +160 -0
- package/deps/jq/linker.c +393 -0
- package/deps/jq/linker.h +7 -0
- package/deps/jq/locfile.c +91 -0
- package/deps/jq/locfile.h +29 -0
- package/deps/jq/m4/ax_compare_version.m4 +177 -0
- package/deps/jq/m4/ax_prog_bison_version.m4 +68 -0
- package/deps/jq/main.c +566 -0
- package/deps/jq/opcode_list.h +44 -0
- package/deps/jq/parser.c +3914 -0
- package/deps/jq/parser.h +193 -0
- package/deps/jq/parser.y +923 -0
- package/deps/jq/scripts/crosscompile +42 -0
- package/deps/jq/scripts/gen_utf8_tables.py +32 -0
- package/deps/jq/scripts/version +5 -0
- package/deps/jq/setup.sh +33 -0
- package/deps/jq/tests/jq.test +1235 -0
- package/deps/jq/tests/jqtest +5 -0
- package/deps/jq/tests/mantest +7 -0
- package/deps/jq/tests/modules/.jq +5 -0
- package/deps/jq/tests/modules/a.jq +2 -0
- package/deps/jq/tests/modules/b/b.jq +2 -0
- package/deps/jq/tests/modules/c/c.jq +16 -0
- package/deps/jq/tests/modules/c/d.jq +1 -0
- package/deps/jq/tests/modules/data.json +4 -0
- package/deps/jq/tests/modules/lib/jq/e/e.jq +1 -0
- package/deps/jq/tests/modules/lib/jq/f.jq +1 -0
- package/deps/jq/tests/modules/syntaxerror/syntaxerror.jq +1 -0
- package/deps/jq/tests/modules/test_bind_order.jq +4 -0
- package/deps/jq/tests/modules/test_bind_order0.jq +1 -0
- package/deps/jq/tests/modules/test_bind_order1.jq +2 -0
- package/deps/jq/tests/modules/test_bind_order2.jq +2 -0
- package/deps/jq/tests/onig.supp +21 -0
- package/deps/jq/tests/onig.test +85 -0
- package/deps/jq/tests/onigtest +5 -0
- package/deps/jq/tests/setup +36 -0
- package/deps/jq/tests/shtest +205 -0
- package/deps/jq/tests/torture/input0.json +7 -0
- package/deps/jq/util.c +462 -0
- package/deps/jq/util.h +64 -0
- package/deps/jq.gyp +35 -0
- package/index.d.ts +3 -0
- package/jest.config.js +10 -0
- package/lib/index.js +14 -0
- package/package.json +48 -0
- package/reports/jest-port-api.xml +35 -0
- package/src/binding.cc +177 -0
- package/src/binding.h +13 -0
- package/test/santiy.test.js +122 -0
- package/util/configure.js +27 -0
|
@@ -0,0 +1,852 @@
|
|
|
1
|
+
#include <stdio.h>
|
|
2
|
+
#include <stdlib.h>
|
|
3
|
+
#include <string.h>
|
|
4
|
+
#include <assert.h>
|
|
5
|
+
#include "jv.h"
|
|
6
|
+
#include "jv_dtoa.h"
|
|
7
|
+
#include "jv_unicode.h"
|
|
8
|
+
#include "jv_alloc.h"
|
|
9
|
+
#include "jv_dtoa.h"
|
|
10
|
+
|
|
11
|
+
typedef const char* presult;
|
|
12
|
+
|
|
13
|
+
#define TRY(x) do {presult msg__ = (x); if (msg__) return msg__; } while(0)
|
|
14
|
+
#ifdef __GNUC__
|
|
15
|
+
#define pfunc __attribute__((warn_unused_result)) presult
|
|
16
|
+
#else
|
|
17
|
+
#define pfunc presult
|
|
18
|
+
#endif
|
|
19
|
+
|
|
20
|
+
enum last_seen {
|
|
21
|
+
JV_LAST_NONE = 0,
|
|
22
|
+
JV_LAST_OPEN_ARRAY = '[',
|
|
23
|
+
JV_LAST_OPEN_OBJECT = '{',
|
|
24
|
+
JV_LAST_COLON = ':',
|
|
25
|
+
JV_LAST_COMMA = ',',
|
|
26
|
+
JV_LAST_VALUE = 'V',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
struct jv_parser {
|
|
30
|
+
const char* curr_buf;
|
|
31
|
+
int curr_buf_length;
|
|
32
|
+
int curr_buf_pos;
|
|
33
|
+
int curr_buf_is_partial;
|
|
34
|
+
int eof;
|
|
35
|
+
unsigned bom_strip_position;
|
|
36
|
+
|
|
37
|
+
int flags;
|
|
38
|
+
|
|
39
|
+
jv* stack; // parser
|
|
40
|
+
int stackpos; // parser
|
|
41
|
+
int stacklen; // both (optimization; it's really pathlen for streaming)
|
|
42
|
+
jv path; // streamer
|
|
43
|
+
enum last_seen last_seen; // streamer
|
|
44
|
+
jv output; // streamer
|
|
45
|
+
jv next; // both
|
|
46
|
+
|
|
47
|
+
char* tokenbuf;
|
|
48
|
+
int tokenpos;
|
|
49
|
+
int tokenlen;
|
|
50
|
+
|
|
51
|
+
int line, column;
|
|
52
|
+
|
|
53
|
+
struct dtoa_context dtoa;
|
|
54
|
+
|
|
55
|
+
enum {
|
|
56
|
+
JV_PARSER_NORMAL,
|
|
57
|
+
JV_PARSER_STRING,
|
|
58
|
+
JV_PARSER_STRING_ESCAPE,
|
|
59
|
+
JV_PARSER_WAITING_FOR_RS // parse error, waiting for RS
|
|
60
|
+
} st;
|
|
61
|
+
unsigned int last_ch_was_ws:1;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
static void parser_init(struct jv_parser* p, int flags) {
|
|
66
|
+
p->flags = flags;
|
|
67
|
+
if ((p->flags & JV_PARSE_STREAMING)) {
|
|
68
|
+
p->path = jv_array();
|
|
69
|
+
} else {
|
|
70
|
+
p->path = jv_invalid();
|
|
71
|
+
p->flags &= ~(JV_PARSE_STREAM_ERRORS);
|
|
72
|
+
}
|
|
73
|
+
p->stack = 0;
|
|
74
|
+
p->stacklen = p->stackpos = 0;
|
|
75
|
+
p->last_seen = JV_LAST_NONE;
|
|
76
|
+
p->output = jv_invalid();
|
|
77
|
+
p->next = jv_invalid();
|
|
78
|
+
p->tokenbuf = 0;
|
|
79
|
+
p->tokenlen = p->tokenpos = 0;
|
|
80
|
+
if ((p->flags & JV_PARSE_SEQ))
|
|
81
|
+
p->st = JV_PARSER_WAITING_FOR_RS;
|
|
82
|
+
else
|
|
83
|
+
p->st = JV_PARSER_NORMAL;
|
|
84
|
+
p->eof = 0;
|
|
85
|
+
p->curr_buf = 0;
|
|
86
|
+
p->curr_buf_length = p->curr_buf_pos = p->curr_buf_is_partial = 0;
|
|
87
|
+
p->bom_strip_position = 0;
|
|
88
|
+
p->last_ch_was_ws = 0;
|
|
89
|
+
p->line = 1;
|
|
90
|
+
p->column = 0;
|
|
91
|
+
jvp_dtoa_context_init(&p->dtoa);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
static void parser_reset(struct jv_parser* p) {
|
|
95
|
+
if ((p->flags & JV_PARSE_STREAMING)) {
|
|
96
|
+
jv_free(p->path);
|
|
97
|
+
p->path = jv_array();
|
|
98
|
+
p->stacklen = 0;
|
|
99
|
+
}
|
|
100
|
+
p->last_seen = JV_LAST_NONE;
|
|
101
|
+
jv_free(p->output);
|
|
102
|
+
p->output = jv_invalid();
|
|
103
|
+
jv_free(p->next);
|
|
104
|
+
p->next = jv_invalid();
|
|
105
|
+
for (int i=0; i<p->stackpos; i++)
|
|
106
|
+
jv_free(p->stack[i]);
|
|
107
|
+
p->stackpos = 0;
|
|
108
|
+
p->tokenpos = 0;
|
|
109
|
+
p->st = JV_PARSER_NORMAL;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
static void parser_free(struct jv_parser* p) {
|
|
113
|
+
parser_reset(p);
|
|
114
|
+
jv_free(p->path);
|
|
115
|
+
jv_free(p->output);
|
|
116
|
+
jv_mem_free(p->stack);
|
|
117
|
+
jv_mem_free(p->tokenbuf);
|
|
118
|
+
jvp_dtoa_context_free(&p->dtoa);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
static pfunc value(struct jv_parser* p, jv val) {
|
|
122
|
+
if ((p->flags & JV_PARSE_STREAMING)) {
|
|
123
|
+
if (jv_is_valid(p->next) || p->last_seen == JV_LAST_VALUE)
|
|
124
|
+
return "Expected separator between values";
|
|
125
|
+
if (p->stacklen > 0)
|
|
126
|
+
p->last_seen = JV_LAST_VALUE;
|
|
127
|
+
else
|
|
128
|
+
p->last_seen = JV_LAST_NONE;
|
|
129
|
+
} else {
|
|
130
|
+
if (jv_is_valid(p->next)) return "Expected separator between values";
|
|
131
|
+
}
|
|
132
|
+
jv_free(p->next);
|
|
133
|
+
p->next = val;
|
|
134
|
+
return 0;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
static void push(struct jv_parser* p, jv v) {
|
|
138
|
+
assert(p->stackpos <= p->stacklen);
|
|
139
|
+
if (p->stackpos == p->stacklen) {
|
|
140
|
+
p->stacklen = p->stacklen * 2 + 10;
|
|
141
|
+
p->stack = jv_mem_realloc(p->stack, p->stacklen * sizeof(jv));
|
|
142
|
+
}
|
|
143
|
+
assert(p->stackpos < p->stacklen);
|
|
144
|
+
p->stack[p->stackpos++] = v;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
static pfunc parse_token(struct jv_parser* p, char ch) {
|
|
148
|
+
switch (ch) {
|
|
149
|
+
case '[':
|
|
150
|
+
if (jv_is_valid(p->next)) return "Expected separator between values";
|
|
151
|
+
push(p, jv_array());
|
|
152
|
+
break;
|
|
153
|
+
|
|
154
|
+
case '{':
|
|
155
|
+
if (jv_is_valid(p->next)) return "Expected separator between values";
|
|
156
|
+
push(p, jv_object());
|
|
157
|
+
break;
|
|
158
|
+
|
|
159
|
+
case ':':
|
|
160
|
+
if (!jv_is_valid(p->next))
|
|
161
|
+
return "Expected string key before ':'";
|
|
162
|
+
if (p->stackpos == 0 || jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_OBJECT)
|
|
163
|
+
return "':' not as part of an object";
|
|
164
|
+
if (jv_get_kind(p->next) != JV_KIND_STRING)
|
|
165
|
+
return "Object keys must be strings";
|
|
166
|
+
push(p, p->next);
|
|
167
|
+
p->next = jv_invalid();
|
|
168
|
+
break;
|
|
169
|
+
|
|
170
|
+
case ',':
|
|
171
|
+
if (!jv_is_valid(p->next))
|
|
172
|
+
return "Expected value before ','";
|
|
173
|
+
if (p->stackpos == 0)
|
|
174
|
+
return "',' not as part of an object or array";
|
|
175
|
+
if (jv_get_kind(p->stack[p->stackpos-1]) == JV_KIND_ARRAY) {
|
|
176
|
+
p->stack[p->stackpos-1] = jv_array_append(p->stack[p->stackpos-1], p->next);
|
|
177
|
+
p->next = jv_invalid();
|
|
178
|
+
} else if (jv_get_kind(p->stack[p->stackpos-1]) == JV_KIND_STRING) {
|
|
179
|
+
assert(p->stackpos > 1 && jv_get_kind(p->stack[p->stackpos-2]) == JV_KIND_OBJECT);
|
|
180
|
+
p->stack[p->stackpos-2] = jv_object_set(p->stack[p->stackpos-2],
|
|
181
|
+
p->stack[p->stackpos-1], p->next);
|
|
182
|
+
p->stackpos--;
|
|
183
|
+
p->next = jv_invalid();
|
|
184
|
+
} else {
|
|
185
|
+
// this case hits on input like {"a", "b"}
|
|
186
|
+
return "Objects must consist of key:value pairs";
|
|
187
|
+
}
|
|
188
|
+
break;
|
|
189
|
+
|
|
190
|
+
case ']':
|
|
191
|
+
if (p->stackpos == 0 || jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_ARRAY)
|
|
192
|
+
return "Unmatched ']'";
|
|
193
|
+
if (jv_is_valid(p->next)) {
|
|
194
|
+
p->stack[p->stackpos-1] = jv_array_append(p->stack[p->stackpos-1], p->next);
|
|
195
|
+
p->next = jv_invalid();
|
|
196
|
+
} else {
|
|
197
|
+
if (jv_array_length(jv_copy(p->stack[p->stackpos-1])) != 0) {
|
|
198
|
+
// this case hits on input like [1,2,3,]
|
|
199
|
+
return "Expected another array element";
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
jv_free(p->next);
|
|
203
|
+
p->next = p->stack[--p->stackpos];
|
|
204
|
+
break;
|
|
205
|
+
|
|
206
|
+
case '}':
|
|
207
|
+
if (p->stackpos == 0)
|
|
208
|
+
return "Unmatched '}'";
|
|
209
|
+
if (jv_is_valid(p->next)) {
|
|
210
|
+
if (jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_STRING)
|
|
211
|
+
return "Objects must consist of key:value pairs";
|
|
212
|
+
assert(p->stackpos > 1 && jv_get_kind(p->stack[p->stackpos-2]) == JV_KIND_OBJECT);
|
|
213
|
+
p->stack[p->stackpos-2] = jv_object_set(p->stack[p->stackpos-2],
|
|
214
|
+
p->stack[p->stackpos-1], p->next);
|
|
215
|
+
p->stackpos--;
|
|
216
|
+
p->next = jv_invalid();
|
|
217
|
+
} else {
|
|
218
|
+
if (jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_OBJECT)
|
|
219
|
+
return "Unmatched '}'";
|
|
220
|
+
if (jv_object_length(jv_copy(p->stack[p->stackpos-1])) != 0)
|
|
221
|
+
return "Expected another key-value pair";
|
|
222
|
+
}
|
|
223
|
+
jv_free(p->next);
|
|
224
|
+
p->next = p->stack[--p->stackpos];
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
return 0;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
static pfunc stream_token(struct jv_parser* p, char ch) {
|
|
231
|
+
jv_kind k;
|
|
232
|
+
jv last;
|
|
233
|
+
|
|
234
|
+
switch (ch) {
|
|
235
|
+
case '[':
|
|
236
|
+
if (jv_is_valid(p->next))
|
|
237
|
+
return "Expected a separator between values";
|
|
238
|
+
p->path = jv_array_append(p->path, jv_number(0)); // push
|
|
239
|
+
p->last_seen = JV_LAST_OPEN_ARRAY;
|
|
240
|
+
p->stacklen++;
|
|
241
|
+
break;
|
|
242
|
+
|
|
243
|
+
case '{':
|
|
244
|
+
if (p->last_seen == JV_LAST_VALUE)
|
|
245
|
+
return "Expected a separator between values";
|
|
246
|
+
// Push object key: null, since we don't know it yet
|
|
247
|
+
p->path = jv_array_append(p->path, jv_null()); // push
|
|
248
|
+
p->last_seen = JV_LAST_OPEN_OBJECT;
|
|
249
|
+
p->stacklen++;
|
|
250
|
+
break;
|
|
251
|
+
|
|
252
|
+
case ':':
|
|
253
|
+
if (p->stacklen == 0 || jv_get_kind(jv_array_get(jv_copy(p->path), p->stacklen - 1)) == JV_KIND_NUMBER)
|
|
254
|
+
return "':' not as part of an object";
|
|
255
|
+
if (!jv_is_valid(p->next) || p->last_seen == JV_LAST_NONE)
|
|
256
|
+
return "Expected string key before ':'";
|
|
257
|
+
if (jv_get_kind(p->next) != JV_KIND_STRING)
|
|
258
|
+
return "Object keys must be strings";
|
|
259
|
+
if (p->last_seen != JV_LAST_VALUE)
|
|
260
|
+
return "':' should follow a key";
|
|
261
|
+
p->last_seen = JV_LAST_COLON;
|
|
262
|
+
p->path = jv_array_set(p->path, p->stacklen - 1, p->next);
|
|
263
|
+
p->next = jv_invalid();
|
|
264
|
+
break;
|
|
265
|
+
|
|
266
|
+
case ',':
|
|
267
|
+
if (p->last_seen != JV_LAST_VALUE)
|
|
268
|
+
return "Expected value before ','";
|
|
269
|
+
if (p->stacklen == 0)
|
|
270
|
+
return "',' not as part of an object or array";
|
|
271
|
+
last = jv_array_get(jv_copy(p->path), p->stacklen - 1);
|
|
272
|
+
k = jv_get_kind(last);
|
|
273
|
+
if (k == JV_KIND_NUMBER) {
|
|
274
|
+
int idx = jv_number_value(last);
|
|
275
|
+
|
|
276
|
+
if (jv_is_valid(p->next)) {
|
|
277
|
+
p->output = JV_ARRAY(jv_copy(p->path), p->next);
|
|
278
|
+
p->next = jv_invalid();
|
|
279
|
+
}
|
|
280
|
+
p->path = jv_array_set(p->path, p->stacklen - 1, jv_number(idx + 1));
|
|
281
|
+
p->last_seen = JV_LAST_COMMA;
|
|
282
|
+
} else if (k == JV_KIND_STRING) {
|
|
283
|
+
if (jv_is_valid(p->next)) {
|
|
284
|
+
p->output = JV_ARRAY(jv_copy(p->path), p->next);
|
|
285
|
+
p->next = jv_invalid();
|
|
286
|
+
}
|
|
287
|
+
p->path = jv_array_set(p->path, p->stacklen - 1, jv_true()); // ready for another name:value pair
|
|
288
|
+
p->last_seen = JV_LAST_COMMA;
|
|
289
|
+
} else {
|
|
290
|
+
assert(k == JV_KIND_NULL);
|
|
291
|
+
// this case hits on input like {,}
|
|
292
|
+
// make sure to handle input like {"a", "b"} and {"a":, ...}
|
|
293
|
+
jv_free(last);
|
|
294
|
+
return "Objects must consist of key:value pairs";
|
|
295
|
+
}
|
|
296
|
+
jv_free(last);
|
|
297
|
+
break;
|
|
298
|
+
|
|
299
|
+
case ']':
|
|
300
|
+
if (p->stacklen == 0)
|
|
301
|
+
return "Unmatched ']' at the top-level";
|
|
302
|
+
if (p->last_seen == JV_LAST_COMMA)
|
|
303
|
+
return "Expected another array element";
|
|
304
|
+
if (p->last_seen == JV_LAST_OPEN_ARRAY)
|
|
305
|
+
assert(!jv_is_valid(p->next));
|
|
306
|
+
|
|
307
|
+
last = jv_array_get(jv_copy(p->path), p->stacklen - 1);
|
|
308
|
+
k = jv_get_kind(last);
|
|
309
|
+
jv_free(last);
|
|
310
|
+
|
|
311
|
+
if (k != JV_KIND_NUMBER)
|
|
312
|
+
return "Unmatched ']' in the middle of an object";
|
|
313
|
+
if (jv_is_valid(p->next)) {
|
|
314
|
+
p->output = JV_ARRAY(jv_copy(p->path), p->next, jv_true());
|
|
315
|
+
p->next = jv_invalid();
|
|
316
|
+
} else if (p->last_seen != JV_LAST_OPEN_ARRAY) {
|
|
317
|
+
p->output = JV_ARRAY(jv_copy(p->path));
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
p->path = jv_array_slice(p->path, 0, --(p->stacklen)); // pop
|
|
321
|
+
//assert(!jv_is_valid(p->next));
|
|
322
|
+
jv_free(p->next);
|
|
323
|
+
p->next = jv_invalid();
|
|
324
|
+
|
|
325
|
+
if (p->last_seen == JV_LAST_OPEN_ARRAY)
|
|
326
|
+
p->output = JV_ARRAY(jv_copy(p->path), jv_array()); // Empty arrays are leaves
|
|
327
|
+
|
|
328
|
+
if (p->stacklen == 0)
|
|
329
|
+
p->last_seen = JV_LAST_NONE;
|
|
330
|
+
else
|
|
331
|
+
p->last_seen = JV_LAST_VALUE;
|
|
332
|
+
break;
|
|
333
|
+
|
|
334
|
+
case '}':
|
|
335
|
+
if (p->stacklen == 0)
|
|
336
|
+
return "Unmatched '}' at the top-level";
|
|
337
|
+
if (p->last_seen == JV_LAST_COMMA)
|
|
338
|
+
return "Expected another key:value pair";
|
|
339
|
+
if (p->last_seen == JV_LAST_OPEN_OBJECT)
|
|
340
|
+
assert(!jv_is_valid(p->next));
|
|
341
|
+
|
|
342
|
+
last = jv_array_get(jv_copy(p->path), p->stacklen - 1);
|
|
343
|
+
k = jv_get_kind(last);
|
|
344
|
+
jv_free(last);
|
|
345
|
+
if (k == JV_KIND_NUMBER)
|
|
346
|
+
return "Unmatched '}' in the middle of an array";
|
|
347
|
+
|
|
348
|
+
if (jv_is_valid(p->next)) {
|
|
349
|
+
if (k != JV_KIND_STRING)
|
|
350
|
+
return "Objects must consist of key:value pairs";
|
|
351
|
+
p->output = JV_ARRAY(jv_copy(p->path), p->next, jv_true());
|
|
352
|
+
p->next = jv_invalid();
|
|
353
|
+
} else {
|
|
354
|
+
// Perhaps {"a":[]}
|
|
355
|
+
if (p->last_seen == JV_LAST_COLON)
|
|
356
|
+
// Looks like {"a":}
|
|
357
|
+
return "Missing value in key:value pair";
|
|
358
|
+
if (p->last_seen == JV_LAST_COMMA)
|
|
359
|
+
// Looks like {"a":0,}
|
|
360
|
+
return "Expected another key-value pair";
|
|
361
|
+
if (p->last_seen == JV_LAST_OPEN_ARRAY)
|
|
362
|
+
return "Unmatched '}' in the middle of an array";
|
|
363
|
+
if (p->last_seen != JV_LAST_VALUE && p->last_seen != JV_LAST_OPEN_OBJECT)
|
|
364
|
+
return "Unmatched '}'";
|
|
365
|
+
if (p->last_seen != JV_LAST_OPEN_OBJECT)
|
|
366
|
+
p->output = JV_ARRAY(jv_copy(p->path));
|
|
367
|
+
}
|
|
368
|
+
p->path = jv_array_slice(p->path, 0, --(p->stacklen)); // pop
|
|
369
|
+
jv_free(p->next);
|
|
370
|
+
p->next = jv_invalid();
|
|
371
|
+
|
|
372
|
+
if (p->last_seen == JV_LAST_OPEN_OBJECT)
|
|
373
|
+
p->output = JV_ARRAY(jv_copy(p->path), jv_object()); // Empty arrays are leaves
|
|
374
|
+
|
|
375
|
+
if (p->stacklen == 0)
|
|
376
|
+
p->last_seen = JV_LAST_NONE;
|
|
377
|
+
else
|
|
378
|
+
p->last_seen = JV_LAST_VALUE;
|
|
379
|
+
break;
|
|
380
|
+
}
|
|
381
|
+
return 0;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
static void tokenadd(struct jv_parser* p, char c) {
|
|
385
|
+
assert(p->tokenpos <= p->tokenlen);
|
|
386
|
+
if (p->tokenpos == p->tokenlen) {
|
|
387
|
+
p->tokenlen = p->tokenlen*2 + 256;
|
|
388
|
+
p->tokenbuf = jv_mem_realloc(p->tokenbuf, p->tokenlen);
|
|
389
|
+
}
|
|
390
|
+
assert(p->tokenpos < p->tokenlen);
|
|
391
|
+
p->tokenbuf[p->tokenpos++] = c;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
static int unhex4(char* hex) {
|
|
395
|
+
int r = 0;
|
|
396
|
+
for (int i=0; i<4; i++) {
|
|
397
|
+
char c = *hex++;
|
|
398
|
+
int n;
|
|
399
|
+
if ('0' <= c && c <= '9') n = c - '0';
|
|
400
|
+
else if ('a' <= c && c <= 'f') n = c - 'a' + 10;
|
|
401
|
+
else if ('A' <= c && c <= 'F') n = c - 'A' + 10;
|
|
402
|
+
else return -1;
|
|
403
|
+
r <<= 4;
|
|
404
|
+
r |= n;
|
|
405
|
+
}
|
|
406
|
+
return r;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
static pfunc found_string(struct jv_parser* p) {
|
|
410
|
+
char* in = p->tokenbuf;
|
|
411
|
+
char* out = p->tokenbuf;
|
|
412
|
+
char* end = p->tokenbuf + p->tokenpos;
|
|
413
|
+
|
|
414
|
+
while (in < end) {
|
|
415
|
+
char c = *in++;
|
|
416
|
+
if (c == '\\') {
|
|
417
|
+
if (in >= end)
|
|
418
|
+
return "Expected escape character at end of string";
|
|
419
|
+
c = *in++;
|
|
420
|
+
switch (c) {
|
|
421
|
+
case '\\':
|
|
422
|
+
case '"':
|
|
423
|
+
case '/': *out++ = c; break;
|
|
424
|
+
case 'b': *out++ = '\b'; break;
|
|
425
|
+
case 'f': *out++ = '\f'; break;
|
|
426
|
+
case 't': *out++ = '\t'; break;
|
|
427
|
+
case 'n': *out++ = '\n'; break;
|
|
428
|
+
case 'r': *out++ = '\r'; break;
|
|
429
|
+
|
|
430
|
+
case 'u':
|
|
431
|
+
/* ahh, the complicated case */
|
|
432
|
+
if (in + 4 > end)
|
|
433
|
+
return "Invalid \\uXXXX escape";
|
|
434
|
+
int hexvalue = unhex4(in);
|
|
435
|
+
if (hexvalue < 0)
|
|
436
|
+
return "Invalid characters in \\uXXXX escape";
|
|
437
|
+
unsigned long codepoint = (unsigned long)hexvalue;
|
|
438
|
+
in += 4;
|
|
439
|
+
if (0xD800 <= codepoint && codepoint <= 0xDBFF) {
|
|
440
|
+
/* who thought UTF-16 surrogate pairs were a good idea? */
|
|
441
|
+
if (in + 6 > end || in[0] != '\\' || in[1] != 'u')
|
|
442
|
+
return "Invalid \\uXXXX\\uXXXX surrogate pair escape";
|
|
443
|
+
unsigned long surrogate = unhex4(in+2);
|
|
444
|
+
if (!(0xDC00 <= surrogate && surrogate <= 0xDFFF))
|
|
445
|
+
return "Invalid \\uXXXX\\uXXXX surrogate pair escape";
|
|
446
|
+
in += 6;
|
|
447
|
+
codepoint = 0x10000 + (((codepoint - 0xD800) << 10)
|
|
448
|
+
|(surrogate - 0xDC00));
|
|
449
|
+
}
|
|
450
|
+
if (codepoint > 0x10FFFF)
|
|
451
|
+
codepoint = 0xFFFD; // U+FFFD REPLACEMENT CHARACTER
|
|
452
|
+
out += jvp_utf8_encode(codepoint, out);
|
|
453
|
+
break;
|
|
454
|
+
|
|
455
|
+
default:
|
|
456
|
+
return "Invalid escape";
|
|
457
|
+
}
|
|
458
|
+
} else {
|
|
459
|
+
if (c > 0 && c < 0x001f)
|
|
460
|
+
return "Invalid string: control characters from U+0000 through U+001F must be escaped";
|
|
461
|
+
*out++ = c;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
TRY(value(p, jv_string_sized(p->tokenbuf, out - p->tokenbuf)));
|
|
465
|
+
p->tokenpos = 0;
|
|
466
|
+
return 0;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
static pfunc check_literal(struct jv_parser* p) {
|
|
470
|
+
if (p->tokenpos == 0) return 0;
|
|
471
|
+
|
|
472
|
+
const char* pattern = 0;
|
|
473
|
+
int plen;
|
|
474
|
+
jv v;
|
|
475
|
+
switch (p->tokenbuf[0]) {
|
|
476
|
+
case 't': pattern = "true"; plen = 4; v = jv_true(); break;
|
|
477
|
+
case 'f': pattern = "false"; plen = 5; v = jv_false(); break;
|
|
478
|
+
case 'n': pattern = "null"; plen = 4; v = jv_null(); break;
|
|
479
|
+
}
|
|
480
|
+
if (pattern) {
|
|
481
|
+
if (p->tokenpos != plen) return "Invalid literal";
|
|
482
|
+
for (int i=0; i<plen; i++)
|
|
483
|
+
if (p->tokenbuf[i] != pattern[i])
|
|
484
|
+
return "Invalid literal";
|
|
485
|
+
TRY(value(p, v));
|
|
486
|
+
} else {
|
|
487
|
+
// FIXME: better parser
|
|
488
|
+
p->tokenbuf[p->tokenpos] = 0; // FIXME: invalid
|
|
489
|
+
char* end = 0;
|
|
490
|
+
double d = jvp_strtod(&p->dtoa, p->tokenbuf, &end);
|
|
491
|
+
if (end == 0 || *end != 0)
|
|
492
|
+
return "Invalid numeric literal";
|
|
493
|
+
TRY(value(p, jv_number(d)));
|
|
494
|
+
}
|
|
495
|
+
p->tokenpos = 0;
|
|
496
|
+
return 0;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
typedef enum {
|
|
500
|
+
LITERAL,
|
|
501
|
+
WHITESPACE,
|
|
502
|
+
STRUCTURE,
|
|
503
|
+
QUOTE,
|
|
504
|
+
INVALID
|
|
505
|
+
} chclass;
|
|
506
|
+
|
|
507
|
+
static chclass classify(char c) {
|
|
508
|
+
switch (c) {
|
|
509
|
+
case ' ':
|
|
510
|
+
case '\t':
|
|
511
|
+
case '\r':
|
|
512
|
+
case '\n':
|
|
513
|
+
return WHITESPACE;
|
|
514
|
+
case '"':
|
|
515
|
+
return QUOTE;
|
|
516
|
+
case '[':
|
|
517
|
+
case ',':
|
|
518
|
+
case ']':
|
|
519
|
+
case '{':
|
|
520
|
+
case ':':
|
|
521
|
+
case '}':
|
|
522
|
+
return STRUCTURE;
|
|
523
|
+
default:
|
|
524
|
+
return LITERAL;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
static const presult OK = "output produced";
|
|
530
|
+
|
|
531
|
+
static int parse_check_done(struct jv_parser* p, jv* out) {
|
|
532
|
+
if (p->stackpos == 0 && jv_is_valid(p->next)) {
|
|
533
|
+
*out = p->next;
|
|
534
|
+
p->next = jv_invalid();
|
|
535
|
+
return 1;
|
|
536
|
+
} else {
|
|
537
|
+
return 0;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
static int stream_check_done(struct jv_parser* p, jv* out) {
|
|
542
|
+
if (p->stacklen == 0 && jv_is_valid(p->next)) {
|
|
543
|
+
*out = JV_ARRAY(jv_copy(p->path),p->next);
|
|
544
|
+
p->next = jv_invalid();
|
|
545
|
+
return 1;
|
|
546
|
+
} else if (jv_is_valid(p->output)) {
|
|
547
|
+
if (jv_array_length(jv_copy(p->output)) > 2) {
|
|
548
|
+
// At end of an array or object, necessitating one more output by
|
|
549
|
+
// which to indicate this
|
|
550
|
+
*out = jv_array_slice(jv_copy(p->output), 0, 2);
|
|
551
|
+
p->output = jv_array_slice(p->output, 0, 1); // arrange one more output
|
|
552
|
+
} else {
|
|
553
|
+
// No further processing needed
|
|
554
|
+
*out = p->output;
|
|
555
|
+
p->output = jv_invalid();
|
|
556
|
+
}
|
|
557
|
+
return 1;
|
|
558
|
+
} else {
|
|
559
|
+
return 0;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
static int parse_check_truncation(struct jv_parser* p) {
|
|
564
|
+
return ((p->flags & JV_PARSE_SEQ) && !p->last_ch_was_ws && (p->stackpos > 0 || p->tokenpos > 0 || jv_get_kind(p->next) == JV_KIND_NUMBER));
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
static int stream_check_truncation(struct jv_parser* p) {
|
|
568
|
+
jv_kind k = jv_get_kind(p->next);
|
|
569
|
+
return (p->stacklen > 0 || k == JV_KIND_NUMBER || k == JV_KIND_TRUE || k == JV_KIND_FALSE || k == JV_KIND_NULL);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
static int parse_is_top_num(struct jv_parser* p) {
|
|
573
|
+
return (p->stackpos == 0 && jv_get_kind(p->next) == JV_KIND_NUMBER);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
static int stream_is_top_num(struct jv_parser* p) {
|
|
577
|
+
return (p->stacklen == 0 && jv_get_kind(p->next) == JV_KIND_NUMBER);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
#define check_done(p, o) \
|
|
581
|
+
(((p)->flags & JV_PARSE_STREAMING) ? stream_check_done((p), (o)) : parse_check_done((p), (o)))
|
|
582
|
+
|
|
583
|
+
#define token(p, ch) \
|
|
584
|
+
(((p)->flags & JV_PARSE_STREAMING) ? stream_token((p), (ch)) : parse_token((p), (ch)))
|
|
585
|
+
|
|
586
|
+
#define check_truncation(p) \
|
|
587
|
+
(((p)->flags & JV_PARSE_STREAMING) ? stream_check_truncation((p)) : parse_check_truncation((p)))
|
|
588
|
+
|
|
589
|
+
#define is_top_num(p) \
|
|
590
|
+
(((p)->flags & JV_PARSE_STREAMING) ? stream_is_top_num((p)) : parse_is_top_num((p)))
|
|
591
|
+
|
|
592
|
+
static pfunc scan(struct jv_parser* p, char ch, jv* out) {
|
|
593
|
+
p->column++;
|
|
594
|
+
if (ch == '\n') {
|
|
595
|
+
p->line++;
|
|
596
|
+
p->column = 0;
|
|
597
|
+
}
|
|
598
|
+
if (ch == '\036' /* ASCII RS; see draft-ietf-json-sequence-07 */) {
|
|
599
|
+
if (check_truncation(p)) {
|
|
600
|
+
if (check_literal(p) == 0 && is_top_num(p))
|
|
601
|
+
return "Potentially truncated top-level numeric value";
|
|
602
|
+
return "Truncated value";
|
|
603
|
+
}
|
|
604
|
+
TRY(check_literal(p));
|
|
605
|
+
if (p->st == JV_PARSER_NORMAL && check_done(p, out))
|
|
606
|
+
return OK;
|
|
607
|
+
// shouldn't happen?
|
|
608
|
+
assert(!jv_is_valid(*out));
|
|
609
|
+
parser_reset(p);
|
|
610
|
+
jv_free(*out);
|
|
611
|
+
*out = jv_invalid();
|
|
612
|
+
return OK;
|
|
613
|
+
}
|
|
614
|
+
presult answer = 0;
|
|
615
|
+
p->last_ch_was_ws = 0;
|
|
616
|
+
if (p->st == JV_PARSER_NORMAL) {
|
|
617
|
+
chclass cls = classify(ch);
|
|
618
|
+
if (cls == WHITESPACE)
|
|
619
|
+
p->last_ch_was_ws = 1;
|
|
620
|
+
if (cls != LITERAL) {
|
|
621
|
+
TRY(check_literal(p));
|
|
622
|
+
if (check_done(p, out)) answer = OK;
|
|
623
|
+
}
|
|
624
|
+
switch (cls) {
|
|
625
|
+
case LITERAL:
|
|
626
|
+
tokenadd(p, ch);
|
|
627
|
+
break;
|
|
628
|
+
case WHITESPACE:
|
|
629
|
+
break;
|
|
630
|
+
case QUOTE:
|
|
631
|
+
p->st = JV_PARSER_STRING;
|
|
632
|
+
break;
|
|
633
|
+
case STRUCTURE:
|
|
634
|
+
TRY(token(p, ch));
|
|
635
|
+
break;
|
|
636
|
+
case INVALID:
|
|
637
|
+
return "Invalid character";
|
|
638
|
+
}
|
|
639
|
+
if (check_done(p, out)) answer = OK;
|
|
640
|
+
} else {
|
|
641
|
+
if (ch == '"' && p->st == JV_PARSER_STRING) {
|
|
642
|
+
TRY(found_string(p));
|
|
643
|
+
p->st = JV_PARSER_NORMAL;
|
|
644
|
+
if (check_done(p, out)) answer = OK;
|
|
645
|
+
} else {
|
|
646
|
+
tokenadd(p, ch);
|
|
647
|
+
if (ch == '\\' && p->st == JV_PARSER_STRING) {
|
|
648
|
+
p->st = JV_PARSER_STRING_ESCAPE;
|
|
649
|
+
} else {
|
|
650
|
+
p->st = JV_PARSER_STRING;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
return answer;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
struct jv_parser* jv_parser_new(int flags) {
|
|
658
|
+
struct jv_parser* p = jv_mem_alloc(sizeof(struct jv_parser));
|
|
659
|
+
parser_init(p, flags);
|
|
660
|
+
p->flags = flags;
|
|
661
|
+
return p;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
void jv_parser_free(struct jv_parser* p) {
|
|
665
|
+
parser_free(p);
|
|
666
|
+
jv_mem_free(p);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
static const unsigned char UTF8_BOM[] = {0xEF,0xBB,0xBF};
|
|
670
|
+
|
|
671
|
+
int jv_parser_remaining(struct jv_parser* p) {
|
|
672
|
+
if (p->curr_buf == 0)
|
|
673
|
+
return 0;
|
|
674
|
+
return (p->curr_buf_length - p->curr_buf_pos);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
void jv_parser_set_buf(struct jv_parser* p, const char* buf, int length, int is_partial) {
|
|
678
|
+
assert((p->curr_buf == 0 || p->curr_buf_pos == p->curr_buf_length)
|
|
679
|
+
&& "previous buffer not exhausted");
|
|
680
|
+
while (length > 0 && p->bom_strip_position < sizeof(UTF8_BOM)) {
|
|
681
|
+
if ((unsigned char)*buf == UTF8_BOM[p->bom_strip_position]) {
|
|
682
|
+
// matched a BOM character
|
|
683
|
+
buf++;
|
|
684
|
+
length--;
|
|
685
|
+
p->bom_strip_position++;
|
|
686
|
+
} else {
|
|
687
|
+
if (p->bom_strip_position == 0) {
|
|
688
|
+
// no BOM in this document
|
|
689
|
+
p->bom_strip_position = sizeof(UTF8_BOM);
|
|
690
|
+
} else {
|
|
691
|
+
// malformed BOM (prefix present, rest missing)
|
|
692
|
+
p->bom_strip_position = 0xff;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
p->curr_buf = buf;
|
|
697
|
+
p->curr_buf_length = length;
|
|
698
|
+
p->curr_buf_pos = 0;
|
|
699
|
+
p->curr_buf_is_partial = is_partial;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
static jv make_error(struct jv_parser*, const char *, ...) JV_PRINTF_LIKE(2, 3);
|
|
703
|
+
|
|
704
|
+
static jv make_error(struct jv_parser* p, const char *fmt, ...) {
|
|
705
|
+
va_list ap;
|
|
706
|
+
va_start(ap, fmt);
|
|
707
|
+
jv e = jv_string_vfmt(fmt, ap);
|
|
708
|
+
va_end(ap);
|
|
709
|
+
if ((p->flags & JV_PARSE_STREAM_ERRORS))
|
|
710
|
+
return JV_ARRAY(e, jv_copy(p->path));
|
|
711
|
+
return jv_invalid_with_msg(e);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
jv jv_parser_next(struct jv_parser* p) {
|
|
715
|
+
if (p->eof)
|
|
716
|
+
return jv_invalid();
|
|
717
|
+
if (!p->curr_buf)
|
|
718
|
+
return jv_invalid(); // Need a buffer
|
|
719
|
+
if (p->bom_strip_position == 0xff) {
|
|
720
|
+
if (!(p->flags & JV_PARSE_SEQ))
|
|
721
|
+
return jv_invalid_with_msg(jv_string("Malformed BOM"));
|
|
722
|
+
p->st =JV_PARSER_WAITING_FOR_RS;
|
|
723
|
+
parser_reset(p);
|
|
724
|
+
}
|
|
725
|
+
jv value = jv_invalid();
|
|
726
|
+
if ((p->flags & JV_PARSE_STREAMING) && stream_check_done(p, &value))
|
|
727
|
+
return value;
|
|
728
|
+
char ch;
|
|
729
|
+
presult msg = 0;
|
|
730
|
+
while (!msg && p->curr_buf_pos < p->curr_buf_length) {
|
|
731
|
+
ch = p->curr_buf[p->curr_buf_pos++];
|
|
732
|
+
if (p->st == JV_PARSER_WAITING_FOR_RS) {
|
|
733
|
+
if (ch == '\n') {
|
|
734
|
+
p->line++;
|
|
735
|
+
p->column = 0;
|
|
736
|
+
} else {
|
|
737
|
+
p->column++;
|
|
738
|
+
}
|
|
739
|
+
if (ch == '\036')
|
|
740
|
+
p->st = JV_PARSER_NORMAL;
|
|
741
|
+
continue; // need to resync, wait for RS
|
|
742
|
+
}
|
|
743
|
+
msg = scan(p, ch, &value);
|
|
744
|
+
}
|
|
745
|
+
if (msg == OK) {
|
|
746
|
+
return value;
|
|
747
|
+
} else if (msg) {
|
|
748
|
+
jv_free(value);
|
|
749
|
+
if (ch != '\036' && (p->flags & JV_PARSE_SEQ)) {
|
|
750
|
+
// Skip to the next RS
|
|
751
|
+
p->st = JV_PARSER_WAITING_FOR_RS;
|
|
752
|
+
value = make_error(p, "%s at line %d, column %d (need RS to resync)", msg, p->line, p->column);
|
|
753
|
+
parser_reset(p);
|
|
754
|
+
return value;
|
|
755
|
+
}
|
|
756
|
+
value = make_error(p, "%s at line %d, column %d", msg, p->line, p->column);
|
|
757
|
+
parser_reset(p);
|
|
758
|
+
if (!(p->flags & JV_PARSE_SEQ)) {
|
|
759
|
+
// We're not parsing a JSON text sequence; throw this buffer away.
|
|
760
|
+
// XXX We should fail permanently here.
|
|
761
|
+
p->curr_buf = 0;
|
|
762
|
+
p->curr_buf_pos = 0;
|
|
763
|
+
} // Else ch must be RS; don't clear buf so we can start parsing again after this ch
|
|
764
|
+
return value;
|
|
765
|
+
} else if (p->curr_buf_is_partial) {
|
|
766
|
+
assert(p->curr_buf_pos == p->curr_buf_length);
|
|
767
|
+
// need another buffer
|
|
768
|
+
return jv_invalid();
|
|
769
|
+
} else {
|
|
770
|
+
// at EOF
|
|
771
|
+
p->eof = 1;
|
|
772
|
+
assert(p->curr_buf_pos == p->curr_buf_length);
|
|
773
|
+
jv_free(value);
|
|
774
|
+
if (p->st == JV_PARSER_WAITING_FOR_RS)
|
|
775
|
+
return make_error(p, "Unfinished abandoned text at EOF at line %d, column %d", p->line, p->column);
|
|
776
|
+
if (p->st != JV_PARSER_NORMAL) {
|
|
777
|
+
value = make_error(p, "Unfinished string at EOF at line %d, column %d", p->line, p->column);
|
|
778
|
+
parser_reset(p);
|
|
779
|
+
p->st = JV_PARSER_WAITING_FOR_RS;
|
|
780
|
+
return value;
|
|
781
|
+
}
|
|
782
|
+
if ((msg = check_literal(p))) {
|
|
783
|
+
value = make_error(p, "%s at EOF at line %d, column %d", msg, p->line, p->column);
|
|
784
|
+
parser_reset(p);
|
|
785
|
+
p->st = JV_PARSER_WAITING_FOR_RS;
|
|
786
|
+
return value;
|
|
787
|
+
}
|
|
788
|
+
if (((p->flags & JV_PARSE_STREAMING) && p->stacklen != 0) ||
|
|
789
|
+
(!(p->flags & JV_PARSE_STREAMING) && p->stackpos != 0)) {
|
|
790
|
+
value = make_error(p, "Unfinished JSON term at EOF at line %d, column %d", p->line, p->column);
|
|
791
|
+
parser_reset(p);
|
|
792
|
+
p->st = JV_PARSER_WAITING_FOR_RS;
|
|
793
|
+
return value;
|
|
794
|
+
}
|
|
795
|
+
// p->next is either invalid (nothing here, but no syntax error)
|
|
796
|
+
// or valid (this is the value). either way it's the thing to return
|
|
797
|
+
if ((p->flags & JV_PARSE_STREAMING) && jv_is_valid(p->next)) {
|
|
798
|
+
value = JV_ARRAY(jv_copy(p->path), p->next); // except in streaming mode we've got to make it [path,value]
|
|
799
|
+
} else {
|
|
800
|
+
value = p->next;
|
|
801
|
+
}
|
|
802
|
+
p->next = jv_invalid();
|
|
803
|
+
if ((p->flags & JV_PARSE_SEQ) && !p->last_ch_was_ws && jv_get_kind(value) == JV_KIND_NUMBER) {
|
|
804
|
+
jv_free(value);
|
|
805
|
+
return make_error(p, "Potentially truncated top-level numeric value at EOF at line %d, column %d", p->line, p->column);
|
|
806
|
+
}
|
|
807
|
+
return value;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
jv jv_parse_sized(const char* string, int length) {
|
|
812
|
+
struct jv_parser parser;
|
|
813
|
+
parser_init(&parser, 0);
|
|
814
|
+
jv_parser_set_buf(&parser, string, length, 0);
|
|
815
|
+
jv value = jv_parser_next(&parser);
|
|
816
|
+
if (jv_is_valid(value)) {
|
|
817
|
+
jv next = jv_parser_next(&parser);
|
|
818
|
+
if (jv_is_valid(next)) {
|
|
819
|
+
// multiple JSON values, we only wanted one
|
|
820
|
+
jv_free(value);
|
|
821
|
+
jv_free(next);
|
|
822
|
+
value = jv_invalid_with_msg(jv_string("Unexpected extra JSON values"));
|
|
823
|
+
} else if (jv_invalid_has_msg(jv_copy(next))) {
|
|
824
|
+
// parser error after the first JSON value
|
|
825
|
+
jv_free(value);
|
|
826
|
+
value = next;
|
|
827
|
+
} else {
|
|
828
|
+
// a single valid JSON value
|
|
829
|
+
jv_free(next);
|
|
830
|
+
}
|
|
831
|
+
} else if (jv_invalid_has_msg(jv_copy(value))) {
|
|
832
|
+
// parse error, we'll return it
|
|
833
|
+
} else {
|
|
834
|
+
// no value at all
|
|
835
|
+
jv_free(value);
|
|
836
|
+
value = jv_invalid_with_msg(jv_string("Expected JSON value"));
|
|
837
|
+
}
|
|
838
|
+
parser_free(&parser);
|
|
839
|
+
|
|
840
|
+
if (!jv_is_valid(value) && jv_invalid_has_msg(jv_copy(value))) {
|
|
841
|
+
jv msg = jv_invalid_get_msg(value);
|
|
842
|
+
value = jv_invalid_with_msg(jv_string_fmt("%s (while parsing '%s')",
|
|
843
|
+
jv_string_value(msg),
|
|
844
|
+
string));
|
|
845
|
+
jv_free(msg);
|
|
846
|
+
}
|
|
847
|
+
return value;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
jv jv_parse(const char* string) {
|
|
851
|
+
return jv_parse_sized(string, strlen(string));
|
|
852
|
+
}
|