Branch data Line data Source code
1 : : /*
2 : : * FlatBuffers IDL parser.
3 : : *
4 : : * Originally based on the numeric parser in the Luthor lexer project.
5 : : *
6 : : * We are moving away from TDOP approach because the grammer doesn't
7 : : * really benefit from it. We use the same overall framework.
8 : : */
9 : :
10 : : #include <stdio.h>
11 : : #include <stdlib.h>
12 : : #include <memory.h>
13 : : #include <assert.h>
14 : : #include <stdarg.h>
15 : :
16 : : #include "semantics.h"
17 : : #include "codegen.h"
18 : : #include "fileio.h"
19 : : #include "pstrutil.h"
20 : : #include "flatcc/portable/pparseint.h"
21 : :
22 : 0 : void fb_default_error_out(void *err_ctx, const char *buf, int len)
23 : : {
24 : : (void)err_ctx;
25 : :
26 : 0 : fwrite(buf, 1, len, stderr);
27 : 0 : }
28 : :
29 : 0 : int fb_print_error(fb_parser_t *P, const char * format, ...)
30 : : {
31 : : int n;
32 : : va_list ap;
33 : : char buf[ERROR_BUFSIZ];
34 : :
35 : 0 : va_start (ap, format);
36 : 0 : n = vsnprintf (buf, ERROR_BUFSIZ, format, ap);
37 : 0 : va_end (ap);
38 [ # # ]: 0 : if (n >= ERROR_BUFSIZ) {
39 : 0 : strcpy(buf + ERROR_BUFSIZ - 5, "...\n");
40 : : n = ERROR_BUFSIZ - 1;
41 : : }
42 : 0 : P->error_out(P->error_ctx, buf, n);
43 : 0 : return n;
44 : : }
45 : :
46 : 0 : const char *error_find_file_of_token(fb_parser_t *P, fb_token_t *t)
47 : : {
48 : : /*
49 : : * Search token in dependent buffers if not in current token
50 : : * buffer. We can do this as a linear search because we limit the
51 : : * number of output errors.
52 : : */
53 [ # # ][ # # ]: 0 : while (P) {
[ # # ][ # # ]
[ # # ]
54 [ # # ][ # # ]: 0 : if (P->ts <= t && P->te > t) {
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
55 : 0 : return P->schema.errorname;
56 : : }
57 : 0 : P = P->dependencies;
58 : : }
59 : : return "";
60 : : }
61 : :
62 : 0 : void error_report(fb_parser_t *P, fb_token_t *t, const char *msg, fb_token_t *peer, const char *s, int len)
63 : : {
64 : : const char *file, *peer_file;
65 : :
66 [ # # ]: 0 : if (t && !s) {
67 : 0 : s = t->text;
68 : 0 : len = (int)t->len;
69 : : }
70 [ # # ]: 0 : if (!msg) {
71 : : msg = "";
72 : : }
73 [ # # ]: 0 : if (!s) {
74 : : s = "";
75 : : len = 0;
76 : : }
77 [ # # ]: 0 : if (t && !peer) {
78 : : file = error_find_file_of_token(P, t);
79 : 0 : fb_print_error(P, "%s:%ld:%ld: error: '%.*s': %s\n",
80 : : file, (long)t->linenum, (long)t->pos, len, s, msg);
81 [ # # ]: 0 : } else if (t && peer) {
82 : : file = error_find_file_of_token(P, t);
83 : : peer_file = error_find_file_of_token(P, peer);
84 : 0 : fb_print_error(P, "%s:%ld:%ld: error: '%.*s': %s: %s:%ld:%ld: '%.*s'\n",
85 : : file, (long)t->linenum, (long)t->pos, len, s, msg,
86 : 0 : peer_file, (long)peer->linenum, (long)peer->pos, (int)peer->len, peer->text);
87 [ # # ]: 0 : } else if (!t && !peer) {
88 : 0 : fb_print_error(P, "error: %s\n", msg);
89 [ # # ]: 0 : } else if (peer) {
90 : : peer_file = error_find_file_of_token(P, peer);
91 : 0 : fb_print_error(P, "error: %s: %s:%ld:%ld: '%.*s'\n",
92 : : msg,
93 : 0 : peer_file, (long)peer->linenum, (long)peer->pos, (int)peer->len, peer->text);
94 : : } else {
95 : 0 : fb_print_error(P, "internal error: unexpected state\n");
96 : : }
97 : 0 : ++P->failed;
98 : 0 : }
99 : :
100 : 0 : void error_ref_sym(fb_parser_t *P, fb_ref_t *ref, const char *msg, fb_symbol_t *s2)
101 : : {
102 : : fb_ref_t *p;
103 : : char buf[FLATCC_MAX_IDENT_SHOW + 1];
104 : : int k = FLATCC_MAX_IDENT_SHOW;
105 : : int n = 0;
106 : : int n0 = 0;
107 : : p = ref;
108 [ # # ]: 0 : while (p && k > 0) {
109 : 0 : n = p->ident->len;
110 [ # # ]: 0 : if (k < n) {
111 : : n = k;
112 : : }
113 : 0 : memcpy(buf + n0, p->ident->text, n);
114 : 0 : k -= n;
115 : 0 : n0 += n;
116 : 0 : buf[n0] = '.';
117 : 0 : --k;
118 : 0 : ++n0;
119 : 0 : p = p->link;
120 : : }
121 : 0 : buf[n0] = '\0';
122 [ # # ]: 0 : if (n0 > 0) {
123 : 0 : --n0;
124 : : }
125 [ # # ]: 0 : if (k <= 0) {
126 : 0 : memcpy(buf + FLATCC_MAX_IDENT_SHOW + 1 - 4, "...\0", 4);
127 : : n0 = FLATCC_MAX_IDENT_SHOW;
128 : : }
129 [ # # ]: 0 : error_report(P, ref->ident, msg, s2 ? s2->ident : 0, buf, n0);
130 : 0 : }
131 : :
132 : : //#define LEX_DEBUG
133 : :
134 : : /* Flatbuffers reserve keywords. */
135 : : #define LEX_KEYWORDS
136 : :
137 : : #define LEX_C_BLOCK_COMMENT
138 : : /*
139 : : * Flatbuffers also support /// on a single line for documentation but
140 : : * we can handle that within the normal line comment parsing logic.
141 : : */
142 : : #define LEX_C99_LINE_COMMENT
143 : : /*
144 : : * String escapes are not defined in fb schema but it only uses strings
145 : : * for attribute, namespace, file ext, and file id. For JSON objects we
146 : : * use C string escapes but control characters must be detected.
147 : : */
148 : : #define LEX_C_STRING
149 : : /* Hex and octal notation not allowed in flatbuffer schema. Scientific
150 : : * notation is supported, and the lexer handles this by default. */
151 : : //#define LEX_C_NUMERIC
152 : :
153 : : #define lex_isblank(c) ((c) == ' ' || (c) == '\t')
154 : :
155 : : #include "parser.h"
156 : :
157 : : #ifdef LEX_DEBUG
158 : :
159 : : static void print_token(fb_token_t *t)
160 : : {
161 : : lex_fprint_token(stderr, t->id, t->text, t->text + t->len, t->linenum, t->pos);
162 : : }
163 : :
164 : : static void debug_token(const char *info, fb_token_t *t)
165 : : {
166 : : fprintf(stderr, "%s\n ", info);
167 : : print_token(t);
168 : : }
169 : : #else
170 : : #define debug_token(info, t) ((void)0)
171 : : #endif
172 : :
173 : : static void revert_metadata(fb_metadata_t **list)
174 : : {
175 [ + + ]: 598 : REVERT_LIST(fb_metadata_t, link, list);
176 : : }
177 : :
178 : : static void revert_symbols(fb_symbol_t **list)
179 : : {
180 [ + + ][ + + ]: 1785 : REVERT_LIST(fb_symbol_t, link, list);
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ - + ]
[ + + ][ + + ]
181 : : }
182 : :
183 : : static void revert_names(fb_name_t **list)
184 : : {
185 [ + + ]: 55 : REVERT_LIST(fb_name_t, link, list);
186 : : }
187 : :
188 : : static inline fb_doc_t *fb_add_doc(fb_parser_t *P, fb_token_t *t)
189 : : {
190 : : fb_doc_t *p;
191 : :
192 : 55 : p = new_elem(P, sizeof(*p));
193 : 55 : p->ident = t;
194 : 55 : p->link = P->doc;
195 : 55 : P->doc = p;
196 : : return p;
197 : : }
198 : :
199 : : #define fb_assign_doc(P, p) {\
200 : : revert_symbols(&P->doc); p->doc = P->doc; P->doc = 0; }
201 : :
202 : 72 : static inline fb_compound_type_t *fb_add_table(fb_parser_t *P)
203 : : {
204 : : fb_compound_type_t *p;
205 : :
206 : 72 : p = new_elem(P, sizeof(*p));
207 : 72 : p->symbol.link = P->schema.symbols;
208 : 72 : p->symbol.kind = fb_is_table;
209 : 72 : P->schema.symbols = &p->symbol;
210 : 72 : p->scope = P->current_scope;
211 : 72 : fb_assign_doc(P, p);
212 : 72 : return p;
213 : : }
214 : :
215 : 52 : static inline fb_compound_type_t *fb_add_struct(fb_parser_t *P)
216 : : {
217 : : fb_compound_type_t *p;
218 : :
219 : 52 : p = new_elem(P, sizeof(*p));
220 : 52 : p->symbol.link = P->schema.symbols;
221 : 52 : p->symbol.kind = fb_is_struct;
222 : 52 : P->schema.symbols = &p->symbol;
223 : 52 : p->scope = P->current_scope;
224 : 52 : fb_assign_doc(P, p);
225 : 52 : return p;
226 : : }
227 : :
228 : 1 : static inline fb_compound_type_t *fb_add_rpc_service(fb_parser_t *P)
229 : : {
230 : : fb_compound_type_t *p;
231 : :
232 : 1 : p = new_elem(P, sizeof(*p));
233 : 1 : p->symbol.link = P->schema.symbols;
234 : 1 : p->symbol.kind = fb_is_rpc_service;
235 : 1 : P->schema.symbols = &p->symbol;
236 : 1 : p->scope = P->current_scope;
237 : 1 : fb_assign_doc(P, p);
238 : 1 : return p;
239 : : }
240 : :
241 : 35 : static inline fb_compound_type_t *fb_add_enum(fb_parser_t *P)
242 : : {
243 : : fb_compound_type_t *p;
244 : :
245 : 35 : p = new_elem(P, sizeof(*p));
246 : 35 : p->symbol.link = P->schema.symbols;
247 : 35 : p->symbol.kind = fb_is_enum;
248 : 35 : P->schema.symbols = &p->symbol;
249 : 35 : p->scope = P->current_scope;
250 : 35 : fb_assign_doc(P, p);
251 : 35 : return p;
252 : : }
253 : :
254 : 11 : static inline fb_compound_type_t *fb_add_union(fb_parser_t *P)
255 : : {
256 : : fb_compound_type_t *p;
257 : :
258 : 11 : p = new_elem(P, sizeof(*p));
259 : 11 : p->symbol.link = P->schema.symbols;
260 : 11 : p->symbol.kind = fb_is_union;
261 : 11 : P->schema.symbols = &p->symbol;
262 : 11 : p->scope = P->current_scope;
263 : 11 : fb_assign_doc(P, p);
264 : 11 : return p;
265 : : }
266 : :
267 : : static inline fb_ref_t *fb_add_ref(fb_parser_t *P, fb_token_t *t)
268 : : {
269 : : fb_ref_t *p;
270 : :
271 : 513 : p = new_elem(P, sizeof(*p));
272 : 513 : p->ident = t;
273 : : return p;
274 : : }
275 : :
276 : : static inline fb_attribute_t *fb_add_attribute(fb_parser_t *P)
277 : : {
278 : : fb_attribute_t *p;
279 : :
280 : 21 : p = new_elem(P, sizeof(*p));
281 : 21 : p->name.link = P->schema.attributes;
282 : 21 : P->schema.attributes = &p->name;
283 : : return p;
284 : : }
285 : :
286 : : static inline fb_include_t *fb_add_include(fb_parser_t *P)
287 : : {
288 : : fb_include_t *p;
289 : 50 : p = new_elem(P, sizeof(*p));
290 : 50 : p->link = P->schema.includes;
291 : 50 : return P->schema.includes = p;
292 : : }
293 : :
294 : 91 : static inline fb_scope_t *fb_add_scope(fb_parser_t *P, fb_ref_t *name)
295 : : {
296 : : fb_scope_t *p;
297 : :
298 : 91 : p = fb_scope_table_find(&P->schema.root_schema->scope_index, name, 0);
299 [ + + ]: 91 : if (p) {
300 : : return p;
301 : : }
302 : 41 : p = new_elem(P, sizeof(*p));
303 : 41 : p->name = name;
304 : 41 : p->prefix = P->schema.prefix;
305 : :
306 : 41 : fb_scope_table_insert_item(&P->schema.root_schema->scope_index, p, ht_keep);
307 : 41 : return p;
308 : : }
309 : :
310 : : static inline fb_metadata_t *fb_add_metadata(fb_parser_t *P, fb_metadata_t **metadata)
311 : : {
312 : : fb_metadata_t *p;
313 : 353 : p = new_elem(P, sizeof(*p));
314 : 353 : p->link = *metadata;
315 : : return *metadata = p;
316 : : }
317 : :
318 : 600 : static inline fb_member_t *fb_add_member(fb_parser_t *P, fb_symbol_t **members)
319 : : {
320 : : fb_member_t *p;
321 : 600 : p = new_elem(P, sizeof(*p));
322 : 600 : p->symbol.link = *members;
323 : 600 : p->symbol.kind = fb_is_member;
324 : 600 : *members = (fb_symbol_t *)p;
325 : 600 : fb_assign_doc(P, p);
326 : 600 : return p;
327 : : }
328 : :
329 : : static inline int is_end(fb_token_t *t)
330 : : {
331 : : return t->id == LEX_TOK_EOF;
332 : : }
333 : :
334 : 11932 : static fb_token_t *next(fb_parser_t *P)
335 : : {
336 : : again:
337 : 6021 : ++P->token;
338 [ - + ]: 6021 : if (P->token == P->te) {
339 : : /* We keep returning end of token to help binary operators etc., if any. */
340 : 0 : --P->token;
341 : : assert(0);
342 [ # # ]: 0 : switch (P->token->id) {
343 : : case LEX_TOK_EOS: case LEX_TOK_EOB: case LEX_TOK_EOF:
344 : 0 : P->token->id = LEX_TOK_EOF;
345 : 0 : return P->token;
346 : : }
347 : : error_tok(P, P->token, "unexpected end of input");
348 : : }
349 [ + + ]: 6021 : if (P->token->id == tok_kw_doc_comment) {
350 : : /* Note: we can have blanks that are control characters here, such as \t. */
351 : : fb_add_doc(P, P->token);
352 : : goto again;
353 : : }
354 : : debug_token("next", P->token);
355 : : return P->token;
356 : : }
357 : :
358 : 0 : static void recover(fb_parser_t *P, long token_id, int consume)
359 : : {
360 [ # # ]: 0 : while (!is_end(P->token)) {
361 [ # # ]: 0 : if (P->token->id == token_id) {
362 [ # # ]: 0 : if (consume) {
363 : 0 : next(P);
364 : : }
365 : 0 : P->doc = 0;
366 : 0 : return;
367 : : }
368 : 0 : next(P);
369 : : }
370 : : }
371 : :
372 : 0 : static void recover2(fb_parser_t *P, long token_id, int consume, long token_id_2, int consume_2)
373 : : {
374 [ # # ]: 0 : while (!is_end(P->token)) {
375 [ # # ]: 0 : if (P->token->id == token_id) {
376 [ # # ]: 0 : if (consume) {
377 : 0 : next(P);
378 : : }
379 : 0 : P->doc = 0;
380 : 0 : return;
381 : : }
382 [ # # ]: 0 : if (P->token->id == token_id_2) {
383 [ # # ]: 0 : if (consume_2) {
384 : 0 : next(P);
385 : : }
386 : 0 : P->doc = 0;
387 : 0 : return;
388 : : }
389 : 0 : next(P);
390 : : }
391 : : }
392 : :
393 : : static inline fb_token_t *optional(fb_parser_t *P, long id) {
394 : : fb_token_t *t = 0;
395 [ + + ][ - + ]: 5094 : if (P->token->id == id) {
[ + + ][ + + ]
[ + - + + ]
[ + + - +
+ + ][ + + ]
[ + - + + ]
[ + + ][ + + ]
[ + + ][ - + ]
[ # # ][ + + ]
[ + + ]
396 : : t = P->token;
397 : 1655 : next(P);
398 : : }
399 : : return t;
400 : : }
401 : :
402 : 1851 : static inline fb_token_t *match(fb_parser_t *P, long id, char *msg) {
403 : : fb_token_t *t = 0;
404 [ + - ]: 1851 : if (P->token->id == id) {
405 : : t = P->token;
406 : 1851 : next(P);
407 : : } else {
408 : : error_tok(P, P->token, msg);
409 : : }
410 : 1851 : return t;
411 : : }
412 : :
413 : 812 : static fb_token_t *advance(fb_parser_t *P, long id, const char *msg, fb_token_t *peer)
414 : : {
415 : : /*
416 : : * `advance` is generally used at end of statements so it is a
417 : : * convenient place to get rid of rogue doc comments we can't attach
418 : : * to anything meaningful.
419 : : */
420 : 812 : P->doc = 0;
421 [ - + ]: 812 : if (P->token->id != id) {
422 : : error_tok_2(P, P->token, msg, peer);
423 : 0 : return P->token;
424 : : }
425 : 812 : return next(P);
426 : : }
427 : :
428 : 287 : static void read_integer_value(fb_parser_t *P, fb_token_t *t, fb_value_t *v, int sign)
429 : : {
430 : : int status;
431 : :
432 : 287 : v->type = vt_uint;
433 : : /* The token does not store the sign internally. */
434 : 287 : parse_integer(t->text, t->len, &v->u, &status);
435 [ - + ]: 287 : if (status != PARSE_INTEGER_UNSIGNED) {
436 : 0 : v->type = vt_invalid;
437 : : error_tok(P, t, "invalid integer format");
438 : : }
439 [ + + ]: 287 : if (sign) {
440 : 1 : v->i = -(int64_t)v->u;
441 : 1 : v->type = vt_int;
442 : : #ifdef FLATCC_FAIL_ON_INT_SIGN_OVERFLOW
443 : : /* Sometimes we might want this, so don't fail by default. */
444 : : if (v->i > 0) {
445 : : v->type = vt_invalid;
446 : : error_tok(P, t, "sign overflow in integer format");
447 : : }
448 : : #endif
449 : : }
450 : 287 : }
451 : :
452 : 2 : static void read_float_value(fb_parser_t *P, fb_token_t *t, fb_value_t *v, int sign)
453 : : {
454 : : char *end;
455 : :
456 : 1 : v->type = vt_float;
457 : 1 : v->f = strtod(t->text, &end);
458 [ - + ]: 1 : if (end != t->text + t->len) {
459 : 0 : v->type = vt_invalid;
460 : : error_tok(P, t, "invalid float format");
461 [ - + ]: 1 : } else if (t->text[0] == '.') {
462 : 0 : v->type = vt_invalid;
463 : : /* The FB spec requires this, in line with the JSON format. */
464 : : error_tok(P, t, "numeric values must start with a digit");
465 [ - + ]: 1 : } else if (sign) {
466 : 0 : v->f = -v->f;
467 : : }
468 : 1 : }
469 : :
470 : : /*
471 : : * We disallow escape characters, newlines and other control characters,
472 : : * but especially escape characters because they would require us to
473 : : * reallocate the string and convert the escaped characters. We also
474 : : * disallow non-utf8 characters, but we do not check for it. The tab
475 : : * character could meaningfully be accepted, but we don't.
476 : : *
477 : : * String literals are only used to name attributes, namespaces,
478 : : * file identifiers and file externsions, so we really have no need
479 : : * for these extra featuresescape .
480 : : *
481 : : * JSON strings should be handled separately, if or when supported -
482 : : * either by converting escapes and reallocating the string, or
483 : : * simply by ignoring the escape errors and use the string unmodified.
484 : : */
485 : 164 : static void parse_string_literal(fb_parser_t *P, fb_value_t *v)
486 : : {
487 : : fb_token_t *t;
488 : :
489 : 164 : v->type = vt_string;
490 : 164 : v->s.s = 0;
491 : 164 : v->s.len = 0;
492 : :
493 : : for (;;) {
494 : 164 : t = P->token;
495 [ + - - - : 328 : switch (t->id) {
- + ]
496 : : case LEX_TOK_STRING_PART:
497 [ + - ]: 164 : if (v->s.s == 0) {
498 : 164 : v->s.s = (char *)t->text;
499 : : }
500 : : break;
501 : : case LEX_TOK_STRING_ESCAPE:
502 : 0 : v->type = vt_invalid;
503 : : error_tok(P, t, "escape not allowed in strings");
504 : : break;
505 : : case LEX_TOK_STRING_CTRL:
506 : 0 : v->type = vt_invalid;
507 : : error_tok_as_string(P, t, "control characters not allowed in strings", "?", 1);
508 : : break;
509 : : case LEX_TOK_STRING_NEWLINE:
510 : 0 : v->type = vt_invalid;
511 : : error_tok(P, t, "newline not allowed in strings");
512 : : break;
513 : : case LEX_TOK_STRING_UNTERMINATED:
514 : : case LEX_TOK_STRING_END:
515 : : goto done;
516 : :
517 : : default:
518 : : error_tok(P, t, "internal error: unexpected token in string");
519 : 0 : v->type = vt_invalid;
520 : 0 : goto done;
521 : : }
522 : 164 : next(P);
523 : : }
524 : : done:
525 : : /*
526 : : * If we were to ignore all errors, we would get the full
527 : : * string as is excluding delimiting quotes.
528 : : */
529 [ + - ]: 164 : if (v->s.s) {
530 : 164 : v->s.len = (long)(P->token->text - v->s.s);
531 : : }
532 [ - + ]: 164 : if (!match(P, LEX_TOK_STRING_END, "unterminated string")) {
533 : 0 : v->type = vt_invalid;
534 : : }
535 : 164 : }
536 : :
537 : : /* Current token must be an identifier. */
538 : 303 : static void parse_ref(fb_parser_t *P, fb_ref_t **ref)
539 : : {
540 : 606 : *ref = fb_add_ref(P, P->token);
541 : 303 : next(P);
542 : 303 : ref = &((*ref)->link);
543 [ + + ]: 816 : while (optional(P, '.')) {
544 [ - + ]: 210 : if (P->token->id != LEX_TOK_ID) {
545 : : error_tok(P, P->token, "namespace prefix expected identifier");
546 : : break;
547 : : }
548 : 210 : *ref = fb_add_ref(P, P->token);
549 : 210 : ref = &((*ref)->link);
550 : 210 : next(P);
551 : : }
552 : 303 : }
553 : :
554 : : /* `flags` */
555 : : enum { allow_string_value = 1, allow_id_value = 2 };
556 : 446 : static void parse_value(fb_parser_t *P, fb_value_t *v, int flags, const char *error_msg)
557 : : {
558 : : fb_token_t *t;
559 : : fb_token_t *sign;
560 : :
561 : : sign = optional(P, '-');
562 : 446 : t = P->token;
563 : :
564 [ + + + + : 446 : switch (t->id) {
+ + - ]
565 : : case LEX_TOK_INT:
566 : 287 : read_integer_value(P, t, v, sign != 0);
567 : 287 : break;
568 : : case LEX_TOK_FLOAT:
569 : 1 : read_float_value(P, t, v, sign != 0);
570 : 1 : break;
571 : : case tok_kw_true:
572 : 2 : v->b = 1;
573 : 2 : v->type = vt_bool;
574 : 2 : break;
575 : : case tok_kw_false:
576 : 10 : v->b = 0;
577 : 10 : v->type = vt_bool;
578 : 10 : break;
579 : : case LEX_TOK_STRING_BEGIN:
580 : 75 : next(P);
581 : 75 : parse_string_literal(P, v);
582 [ - + ]: 75 : if (!(flags & allow_string_value)) {
583 : 0 : v->type = vt_invalid;
584 : : error_tok(P, t, error_msg);
585 : : return;
586 : : }
587 [ - + ]: 75 : if (sign) {
588 : 0 : v->type = vt_invalid;
589 : : error_tok(P, t, "string constants cannot be signed");
590 : : return;
591 : : }
592 : : return;
593 : : case LEX_TOK_ID:
594 : 71 : parse_ref(P, &v->ref);
595 : 71 : v->type = vt_name_ref;
596 [ - + ]: 71 : if (sign) {
597 : 0 : v->type = vt_invalid;
598 : : /* Technically they could, but we do not allow it. */
599 : : error_tok(P, t, "named values cannot be signed");
600 : : }
601 : : return;
602 : : default:
603 : : /* We might have consumed a sign, but never mind that. */
604 : : error_tok(P, t, error_msg);
605 : : return;
606 : : }
607 [ + + ][ - + ]: 300 : if (sign && v->type == vt_bool) {
608 : 0 : v->type = vt_invalid;
609 : : error_tok(P, t, "boolean constants cannot be signed");
610 : : }
611 : 300 : next(P);
612 : : }
613 : :
614 : : /* ':' must already be matched */
615 : 523 : static void parse_type(fb_parser_t *P, fb_value_t *v)
616 : : {
617 : : fb_token_t *t = 0;
618 : 523 : fb_token_t *t0 = P->token;
619 : : int vector = 0;
620 : :
621 : 523 : v->type = vt_invalid;
622 [ + + ]: 1107 : while ((t = optional(P, '['))) {
623 : 61 : ++vector;
624 : : }
625 [ - + ]: 523 : if (vector > 1) {
626 : : error_tok(P, t0, "vector type can only be one-dimensional");
627 : : }
628 [ + + + - : 523 : switch (P->token->id) {
- ]
629 : : case tok_kw_int:
630 : : case tok_kw_bool:
631 : : case tok_kw_byte:
632 : : case tok_kw_long:
633 : : case tok_kw_uint:
634 : : case tok_kw_float:
635 : : case tok_kw_short:
636 : : case tok_kw_ubyte:
637 : : case tok_kw_ulong:
638 : : case tok_kw_ushort:
639 : : case tok_kw_double:
640 : 305 : v->t = P->token;
641 [ + + ]: 305 : v->type = vector ? vt_vector_type : vt_scalar_type;
642 : 305 : next(P);
643 : 305 : break;
644 : : case tok_kw_string:
645 : 43 : v->t = P->token;
646 [ + + ]: 43 : v->type = vector ? vt_vector_string_type : vt_string_type;
647 : 43 : next(P);
648 : 43 : break;
649 : : case LEX_TOK_ID:
650 : 175 : parse_ref(P, &v->ref);
651 [ + + ]: 175 : v->type = vector ? vt_vector_type_ref : vt_type_ref;
652 : 175 : break;
653 : : case ']':
654 : : error_tok(P, t, "vector type cannot be empty");
655 : : break;
656 : : default:
657 : : error_tok(P, t, "invalid type specifier");
658 : : break;
659 : : }
660 [ + + ][ + - ]: 584 : while (optional(P, ']') && vector--) {
661 : : }
662 [ - + ]: 523 : if (vector) {
663 : : error_tok_2(P, t, "vector type missing ']' to match", t0);
664 : : }
665 [ - + ]: 523 : if ((t = optional(P, ']'))) {
666 : : error_tok_2(P, t, "extra ']' not matching", t0);
667 [ # # ]: 0 : while (optional(P, ']')) {
668 : : }
669 : : }
670 : 523 : }
671 : :
672 : 656 : static fb_metadata_t *parse_metadata(fb_parser_t *P)
673 : : {
674 : : fb_token_t *t, *t0;
675 : : fb_metadata_t *md = 0;
676 : :
677 [ + + ]: 656 : if (!(t0 = optional(P, '('))) {
678 : : return 0;
679 : : }
680 [ + - ]: 245 : if ((t = optional(P, LEX_TOK_ID)))
681 : : for (;;) {
682 : : fb_add_metadata(P, &md);
683 : 353 : md->ident = t;
684 [ + + ]: 353 : if (optional(P, ':')) {
685 : 294 : parse_value(P, &md->value, allow_string_value, "scalar or string value expected");
686 : : }
687 [ + - ]: 353 : if (P->failed >= FLATCC_MAX_ERRORS) {
688 : : return md;
689 : : }
690 [ + + ]: 353 : if (!optional(P, ',')) {
691 : : break;
692 : : }
693 [ + - ]: 108 : if (!(t = match(P, LEX_TOK_ID, "attribute name expected identifier after ','"))) {
694 : : break;
695 : : }
696 : : }
697 : 245 : advance(P, ')', "metadata expected ')' to match", t0);
698 : : revert_metadata(&md);
699 : : return md;
700 : : }
701 : :
702 : 482 : static void parse_field(fb_parser_t *P, fb_member_t *fld)
703 : : {
704 : : fb_token_t *t;
705 [ + - ]: 482 : if (!(t = match(P, LEX_TOK_ID, "field expected identifier"))) {
706 : : goto fail;
707 : : }
708 : 482 : fld->symbol.ident = t;
709 [ + - ]: 482 : if (!match(P, ':', "field expected ':' before mandatory type")) {
710 : : goto fail;
711 : : }
712 : 482 : parse_type(P, &fld->type);
713 [ + + ]: 482 : if (optional(P, '=')) {
714 : : /*
715 : : * Because types can be named references, we do not check the
716 : : * default assignment before the schema is fully parsed.
717 : : * We allow the initializer to be a name in case it is an enum
718 : : * name.
719 : : */
720 : 113 : parse_value(P, &fld->value, allow_id_value, "initializer must be of scalar type");
721 : : }
722 : 482 : fld->metadata = parse_metadata(P);
723 : 482 : advance(P, ';', "field must be terminated with ';'", 0);
724 : 482 : return;
725 : : fail:
726 : 0 : recover2(P, ';', 1, '}', 0);
727 : : }
728 : :
729 : 3 : static void parse_method(fb_parser_t *P, fb_member_t *fld)
730 : : {
731 : : fb_token_t *t;
732 [ + - ]: 3 : if (!(t = match(P, LEX_TOK_ID, "method expected identifier"))) {
733 : : goto fail;
734 : : }
735 : 3 : fld->symbol.ident = t;
736 [ + - ]: 3 : if (!match(P, '(', "method expected '(' after identifier")) {
737 : : goto fail;
738 : : }
739 : 3 : parse_type(P, &fld->req_type);
740 [ + - ]: 3 : if (!match(P, ')', "method expected ')' after request type")) {
741 : : goto fail;
742 : : }
743 [ + - ]: 3 : if (!match(P, ':', "method expected ':' before mandatory response type")) {
744 : : goto fail;
745 : : }
746 : 3 : parse_type(P, &fld->type);
747 [ - + ]: 3 : if ((t = optional(P, '='))) {
748 : : error_tok(P, t, "method does not accept an initializer");
749 : : goto fail;
750 : : }
751 : 3 : fld->metadata = parse_metadata(P);
752 : 3 : advance(P, ';', "method must be terminated with ';'", 0);
753 : 3 : return;
754 : : fail:
755 : 0 : recover2(P, ';', 1, '}', 0);
756 : : }
757 : :
758 : : /* `enum` must already be matched. */
759 : 35 : static void parse_enum_decl(fb_parser_t *P, fb_compound_type_t *ct)
760 : : {
761 : : fb_token_t *t, *t0;
762 : : fb_member_t *member;
763 : :
764 [ + - ]: 35 : if (!(ct->symbol.ident = match(P, LEX_TOK_ID, "enum declaration expected identifier"))) {
765 : : goto fail;
766 : : }
767 [ + - ]: 35 : if (optional(P, ':')) {
768 : 35 : parse_type(P, &ct->type);
769 [ - + ]: 35 : if (ct->type.type != vt_scalar_type) {
770 : 0 : error_tok(P, ct->type.t, "integral type expected");
771 : : } else {
772 [ - + ]: 35 : switch (ct->type.t->id) {
773 : : case tok_kw_float:
774 : : case tok_kw_double:
775 : : error_tok(P, ct->type.t, "integral type expected");
776 : : default:
777 : : break;
778 : : }
779 : : }
780 : : }
781 : 35 : ct->metadata = parse_metadata(P);
782 [ + - ]: 35 : if (!((t0 = match(P, '{', "enum declaration expected '{'")))) {
783 : : goto fail;
784 : : }
785 : : for (;;) {
786 [ + - ]: 83 : if (!(t = match(P, LEX_TOK_ID,
787 : : "member identifier expected"))) {
788 : : goto fail;
789 : : }
790 [ + - ]: 83 : if (P->failed >= FLATCC_MAX_ERRORS) {
791 : : goto fail;
792 : : }
793 : 83 : member = fb_add_member(P, &ct->members);
794 : 83 : member->symbol.ident = t;
795 [ + + ]: 83 : if (optional(P, '=')) {
796 : : t = P->token;
797 : 36 : parse_value(P, &member->value, 0, "integral constant expected");
798 : : /* Leave detailed type (e.g. no floats) and range checking to a later stage. */
799 : : }
800 : : /*
801 : : * Trailing comma is optional in flatc but not in grammar, we
802 : : * follow flatc.
803 : : */
804 [ + + ][ + + ]: 83 : if (!optional(P, ',') || P->token->id == '}') {
805 : : break;
806 : : }
807 : 48 : P->doc = 0;
808 : : }
809 : : if (t0) {
810 : 35 : advance(P, '}', "enum missing closing '}' to match", t0);
811 : : }
812 : : revert_symbols(&ct->members);
813 : : return;
814 : : fail:
815 : 0 : recover(P, '}', 1);
816 : : }
817 : :
818 : : /* `union` must already be matched. */
819 : 11 : static void parse_union_decl(fb_parser_t *P, fb_compound_type_t *ct)
820 : : {
821 : : fb_token_t *t0;
822 : : fb_member_t *member;
823 : : fb_ref_t *ref;
824 : :
825 [ + - ]: 11 : if (!(ct->symbol.ident = match(P, LEX_TOK_ID, "union declaration expected identifier"))) {
826 : : goto fail;
827 : : }
828 : 11 : ct->metadata = parse_metadata(P);
829 [ + - ]: 11 : if (!((t0 = match(P, '{', "union declaration expected '{'")))) {
830 : : goto fail;
831 : : }
832 : : for (;;) {
833 [ - + ]: 21 : if (P->token->id != LEX_TOK_ID) {
834 : : error_tok(P, P->token, "union expects an identifier");
835 : : goto fail;
836 : : }
837 [ + - ]: 21 : if (P->failed >= FLATCC_MAX_ERRORS) {
838 : : goto fail;
839 : : }
840 : 21 : member = fb_add_member(P, &ct->members);
841 : 21 : parse_ref(P, &ref);
842 : 21 : member->type.ref = ref;
843 : 21 : member->type.type = vt_type_ref;
844 [ + + ]: 22 : while (ref->link) {
845 : 1 : ref = ref->link;
846 : : }
847 : : /* The union member is the unqualified reference. */
848 : 21 : member->symbol.ident = ref->ident;
849 [ + + ]: 21 : if (optional(P, '=')) {
850 : 3 : parse_value(P, &member->value, 0, "integral constant expected");
851 : : /* Leave detailed type (e.g. no floats) and range checking to a later stage. */
852 : : }
853 [ + + ][ + + ]: 21 : if (!optional(P, ',') || P->token->id == '}') {
854 : : break;
855 : : }
856 : 10 : P->doc = 0;
857 : : }
858 : 11 : advance(P, '}', "union missing closing '}' to match", t0);
859 : : revert_symbols(&ct->members);
860 : : /* Add implicit `NONE` member first in the list. */
861 : 11 : member = fb_add_member(P, &ct->members);
862 : 11 : member->symbol.ident = &P->t_none;
863 : 11 : return;
864 : : fail:
865 : 0 : recover2(P, ';', 1, '}', 0);
866 : : }
867 : :
868 : : /* `struct` , `table`, or 'rpc_service' must already be matched. */
869 : 125 : static void parse_compound_type(fb_parser_t *P, fb_compound_type_t *ct, long token)
870 : : {
871 : : fb_token_t *t = 0;
872 : :
873 [ + - ]: 125 : if (!(t = match(P, LEX_TOK_ID, "Declaration expected an identifier"))) {
874 : : goto fail;
875 : : }
876 : 125 : ct->symbol.ident = t;
877 : 125 : ct->metadata = parse_metadata(P);
878 [ + - ]: 125 : if (!(match(P, '{', "Declaration expected '{'"))) {
879 : : goto fail;
880 : : }
881 : 125 : t = P->token;
882 : :
883 : : /* Allow empty tables and structs. */
884 : : #if 0
885 : : if (P->token->id == '}') {
886 : : error_tok(P, t, "table / struct declaration cannot be empty");
887 : : }
888 : : #endif
889 [ + + ]: 610 : while (P->token->id != '}') {
890 [ + + ]: 485 : if (token == tok_kw_rpc_service) {
891 : 3 : parse_method(P, fb_add_member(P, &ct->members));
892 : : } else {
893 : 482 : parse_field(P, fb_add_member(P, &ct->members));
894 : : }
895 [ + - ]: 485 : if (P->failed >= FLATCC_MAX_ERRORS) {
896 : : goto fail;
897 : : }
898 : : }
899 [ - + ][ # # ]: 125 : if (!optional(P, '}') && t) {
900 : 0 : error_tok_2(P, P->token, "Declaration missing closing '}' to match", t);
901 : : }
902 : : revert_symbols(&ct->members);
903 : : return;
904 : : fail:
905 : 0 : recover(P, '}', 1);
906 : : }
907 : :
908 : 25 : static void parse_namespace(fb_parser_t *P)
909 : : {
910 : 25 : fb_ref_t *ref = 0;
911 : 25 : fb_token_t *t = P->token;
912 : :
913 [ - + ][ # # ]: 25 : if (optional(P, ';') && t) {
914 : : /* Revert to global namespace. */
915 : 0 : P->current_scope = 0;
916 : 0 : return;
917 : : }
918 [ - + ]: 25 : if (P->token->id != LEX_TOK_ID) {
919 : : error_tok(P, P->token, "namespace expects an identifier");
920 : 0 : recover(P, ';', 1);
921 : 0 : return;
922 : : }
923 : 25 : parse_ref(P, &ref);
924 : 25 : advance(P, ';', "missing ';' expected by namespace at", t);
925 : 25 : P->current_scope = fb_add_scope(P, ref);
926 : : }
927 : :
928 : 11 : static void parse_root_type(fb_parser_t *P, fb_root_type_t *rt)
929 : : {
930 : 11 : fb_token_t *t = P->token;
931 : :
932 [ - + ]: 11 : if (rt->name) {
933 : : error_tok(P, P->token, "root_type already set");
934 : : }
935 : 11 : parse_ref(P, &rt->name);
936 : 11 : rt->scope = P->current_scope;
937 : 11 : advance(P, ';', "missing ';' expected by root_type at", t);
938 : 11 : }
939 : :
940 : 34 : static void parse_include(fb_parser_t *P)
941 : : {
942 : 34 : fb_token_t *t = P->token;
943 : :
944 [ + + ]: 118 : while (optional(P, tok_kw_include)) {
945 [ - + ]: 50 : if (P->opts.disable_includes) {
946 : : error_tok(P, t, "include statements not supported by current environment");
947 : : }
948 [ + - ]: 50 : if (P->failed >= FLATCC_MAX_ERRORS) {
949 : : return;
950 : : }
951 [ - + ]: 50 : if (!match(P, LEX_TOK_STRING_BEGIN,
952 : : "include expected a string literal as filename")) {
953 : 0 : recover(P, ';', 1);
954 : : }
955 : 50 : parse_string_literal(P, &fb_add_include(P)->name);
956 : 50 : match(P, ';', "include statement expected ';'");
957 : : }
958 : : }
959 : :
960 : 21 : static void parse_attribute(fb_parser_t *P, fb_attribute_t *a)
961 : : {
962 : 21 : fb_token_t *t = P->token;
963 : :
964 [ + - ]: 21 : if (match(P, LEX_TOK_STRING_BEGIN, "attribute expected string literal")) {
965 : 21 : parse_string_literal(P, &a->name.name);
966 [ - + ]: 21 : if (a->name.name.s.len == 0) {
967 : : error_tok_as_string(P, t, "attribute name cannot be empty", 0, 0);
968 : : }
969 : : }
970 : 21 : match(P, ';', "attribute expected ';'");
971 : 21 : }
972 : :
973 : 9 : static void parse_file_extension(fb_parser_t *P, fb_value_t *v)
974 : : {
975 [ - + ]: 9 : if (v->type == vt_string) {
976 : 0 : error_tok_as_string(P, P->token, "file extension already set", v->s.s, v->s.len);
977 : : }
978 [ + - ]: 9 : if (!match(P, LEX_TOK_STRING_BEGIN, "file_extension expected string literal")) {
979 : : goto fail;
980 : : }
981 : 9 : parse_string_literal(P, v);
982 : 9 : match(P, ';', "file_extension expected ';'");
983 : 9 : return;
984 : : fail:
985 : 0 : recover(P, ';', 1);
986 : : }
987 : :
988 : 9 : static void parse_file_identifier(fb_parser_t *P, fb_value_t *v)
989 : : {
990 : : fb_token_t *t;
991 [ - + ]: 9 : if (v->type != vt_missing) {
992 : 0 : error_tok_as_string(P, P->token, "file identifier already set", v->s.s, v->s.len);
993 : : }
994 [ + - ]: 9 : if (!match(P, LEX_TOK_STRING_BEGIN, "file_identifier expected string literal")) {
995 : : goto fail;
996 : : }
997 : 9 : t = P->token;
998 : 9 : parse_string_literal(P, v);
999 [ + - ][ - + ]: 9 : if (v->s.s && v->s.len != 4) {
1000 : 0 : v->type = vt_invalid;
1001 : : error_tok(P, t, "file_identifier must be 4 characters");
1002 : : }
1003 : 9 : match(P, ';', "file_identifier expected ';'");
1004 : 9 : return;
1005 : : fail:
1006 : 0 : recover(P, ';', 1);
1007 : : }
1008 : :
1009 : 248 : static void parse_schema_decl(fb_parser_t *P)
1010 : : {
1011 [ + + + + : 248 : switch(P->token->id) {
+ + + + +
+ - - - +
- - ]
1012 : : case tok_kw_namespace:
1013 : 25 : next(P);
1014 : 25 : parse_namespace(P);
1015 : 25 : break;
1016 : : case tok_kw_file_extension:
1017 : 9 : next(P);
1018 : 9 : parse_file_extension(P, &P->schema.file_extension);
1019 : 9 : break;
1020 : : case tok_kw_file_identifier:
1021 : 9 : next(P);
1022 : 9 : parse_file_identifier(P, &P->schema.file_identifier);
1023 : 9 : break;
1024 : : case tok_kw_root_type:
1025 : 11 : next(P);
1026 : 11 : parse_root_type(P, &P->schema.root_type);
1027 : 11 : break;
1028 : : case tok_kw_attribute:
1029 : 21 : next(P);
1030 : 21 : parse_attribute(P, fb_add_attribute(P));
1031 : 21 : break;
1032 : : case tok_kw_struct:
1033 : 52 : next(P);
1034 : 52 : parse_compound_type(P, fb_add_struct(P), tok_kw_struct);
1035 : 52 : break;
1036 : : case tok_kw_table:
1037 : 72 : next(P);
1038 : 72 : parse_compound_type(P, fb_add_table(P), tok_kw_table);
1039 : 72 : break;
1040 : : case tok_kw_rpc_service:
1041 : 1 : next(P);
1042 : 1 : parse_compound_type(P, fb_add_rpc_service(P), tok_kw_rpc_service);
1043 : 1 : break;
1044 : : case tok_kw_enum:
1045 : 35 : next(P);
1046 : 35 : parse_enum_decl(P, fb_add_enum(P));
1047 : 35 : break;
1048 : : case tok_kw_union:
1049 : 11 : next(P);
1050 : 11 : parse_union_decl(P, fb_add_union(P));
1051 : 11 : break;
1052 : : case tok_kw_include:
1053 : : error_tok(P, P->token, "include statements must be placed first in the schema");
1054 : : break;
1055 : : case '{':
1056 : : error_tok(P, P->token, "JSON objects in schema file is not supported - but a schema specific JSON parser can be generated");
1057 : : break;
1058 : : case LEX_TOK_CTRL:
1059 : : error_tok_as_string(P, P->token, "unexpected control character in schema definition", "?", 1);
1060 : : break;
1061 : : case LEX_TOK_COMMENT_CTRL:
1062 [ + - ]: 2 : if (lex_isblank(P->token->text[0])) {
1063 : : /* This also strips tabs in doc comments. */
1064 : 2 : next(P);
1065 : 2 : break;
1066 : : }
1067 : : error_tok_as_string(P, P->token, "unexpected control character in comment", "?", 1);
1068 : : break;
1069 : : case LEX_TOK_COMMENT_UNTERMINATED:
1070 : : error_tok_as_string(P, P->token, "unterminated comment", "<eof>", 5);
1071 : : break;
1072 : : default:
1073 : : error_tok(P, P->token, "unexpected token in schema definition");
1074 : : break;
1075 : : }
1076 : 248 : }
1077 : :
1078 : 34 : static int parse_schema(fb_parser_t *P)
1079 : : {
1080 : : fb_token_t *t, *t0;
1081 : 34 : parse_include(P);
1082 : 34 : t = P->token;
1083 : : for (;;) {
1084 [ + + ]: 282 : if (is_end(t)) {
1085 : : break;
1086 : : }
1087 [ + - ]: 248 : if (P->failed >= FLATCC_MAX_ERRORS) {
1088 : : return -1;
1089 : : }
1090 : : t0 = t;
1091 : 248 : parse_schema_decl(P);
1092 : 248 : t = P->token;
1093 [ + - ]: 248 : if (t == t0) {
1094 [ # # ]: 0 : if (P->failed) {
1095 : : return -1;
1096 : : }
1097 : : error_tok(P, t, "extra tokens in input");
1098 : 0 : return -1;
1099 : : }
1100 : : }
1101 : : revert_names(&P->schema.attributes);
1102 : : revert_symbols(&P->schema.symbols);
1103 : 34 : return 0;
1104 : : }
1105 : :
1106 : : static inline void clear_elem_buffers(fb_parser_t *P)
1107 : : {
1108 : : void **p, **p2;
1109 : :
1110 : : p = P->elem_buffers;
1111 [ + + ]: 100 : while (p) {
1112 : 34 : p2 = *((void**)p);
1113 : 34 : free(p);
1114 : : p = p2;
1115 : : };
1116 : : }
1117 : :
1118 : 6055 : static void push_token(fb_parser_t *P, long id, const char *first, const char *last)
1119 : : {
1120 : : size_t offset;
1121 : : fb_token_t *t;
1122 : :
1123 : 6055 : P->te = P->ts + P->tcapacity;
1124 [ + + ]: 6055 : if (P->token == P->te) {
1125 : 34 : offset = P->token - P->ts;
1126 [ - + ]: 34 : P->tcapacity = P->tcapacity ? 2 * P->tcapacity : 1024;
1127 : 34 : P->ts = realloc(P->ts, P->tcapacity * sizeof(fb_token_t));
1128 : 34 : checkmem(P->ts);
1129 : 34 : P->te = P->ts + P->tcapacity;
1130 : 34 : P->token = P->ts + offset;
1131 : : }
1132 : 6055 : t = P->token;
1133 : 6055 : t->id = id;
1134 : 6055 : t->text = first;
1135 : 6055 : t->len = (long)(last - first);
1136 : 6055 : t->linenum = P->linenum;
1137 : 6055 : t->pos = (long)(first - P->line + 1);
1138 : 6055 : ++P->token;
1139 : 6055 : }
1140 : :
1141 : : /*
1142 : : * If the file contains a control character, we can get multiple
1143 : : * comments per line.
1144 : : */
1145 : : static inline void push_comment(fb_parser_t *P, const char *first, const char *last)
1146 : : {
1147 [ + + ][ - + ]: 239 : if (P->doc_mode) {
[ + + ][ - + ]
1148 : 146 : push_token(P, tok_kw_doc_comment, first, last);
1149 : : }
1150 : : }
1151 : :
1152 : : static void inject_token(fb_token_t *t, const char *lex, long id)
1153 : : {
1154 : 68 : t->id = id;
1155 : 68 : t->text = lex;
1156 : 68 : t->len = (long)strlen(lex);
1157 : 68 : t->pos = 0;
1158 : 68 : t->linenum = 0;
1159 : : }
1160 : :
1161 : : /* --- Customize lexer --- */
1162 : :
1163 : : /* Depends on the `context` argument given to the lex function. */
1164 : : #define ctx(name) (((fb_parser_t *)context)->name)
1165 : :
1166 : : #define lex_emit_newline(first, last) (ctx(linenum)++, ctx(line) = last)
1167 : :
1168 : : #define lex_emit_string_newline(first, last) \
1169 : : (ctx(linenum)++, ctx(line) = last, \
1170 : : push_token((fb_parser_t*)context, LEX_TOK_STRING_NEWLINE, first, last))
1171 : :
1172 : : /*
1173 : : * Add emtpy comment on comment start - otherwise we miss empty lines.
1174 : : * Save is_doc becuase comment_part does not remember.
1175 : : */
1176 : : #define lex_emit_comment_begin(first, last, is_doc) \
1177 : : { ctx(doc_mode) = is_doc; push_comment((fb_parser_t*)context, last, last); }
1178 : : #define lex_emit_comment_part(first, last) push_comment((fb_parser_t*)context, first, last)
1179 : : #define lex_emit_comment_end(first, last) (ctx(doc_mode) = 0)
1180 : :
1181 : : /* By default emitted as lex_emit_other which would be ignored. */
1182 : : #define lex_emit_comment_unterminated(pos) \
1183 : : push_token((fb_parser_t*)context, LEX_TOK_COMMENT_UNTERMINATED, pos, pos)
1184 : : #define lex_emit_comment_ctrl(pos) \
1185 : : push_token((fb_parser_t*)context, LEX_TOK_COMMENT_CTRL, pos, pos + 1)
1186 : :
1187 : : /*
1188 : : * Provide hook to lexer for emitting tokens. We can override many
1189 : : * things, but most default to calling lex_emit, so that is all we need
1190 : : * to handle.
1191 : : *
1192 : : * `context` is a magic name available to macros in the lexer.
1193 : : */
1194 : : #define lex_emit(token, first, last) \
1195 : : push_token((fb_parser_t*)context, token, first, last)
1196 : :
1197 : : /*
1198 : : * We could just eos directly as it defaults to emit, but formally we
1199 : : * should use the eof marker which is always zero, so parser can check
1200 : : * for it easily, if needed.
1201 : : */
1202 : : #define lex_emit_eos(first, last) \
1203 : : push_token((fb_parser_t*)context, LEX_TOK_EOF, first, last)
1204 : :
1205 : : /*
1206 : : * This event happens in place of eos if we exhaust the input buffer.
1207 : : * In this case we treat this as end of input, but this choice prevents
1208 : : * us from parsing across multiple buffers.
1209 : : */
1210 : : #define lex_emit_eob(pos) \
1211 : : push_token((fb_parser_t*)context, LEX_TOK_EOF, pos, pos)
1212 : :
1213 : : /*
1214 : : * Luthor is our speedy generic lexer - it knows most common operators
1215 : : * and therefore allows us to fail meaningfully on those that we don't
1216 : : * support here, which is most.
1217 : : */
1218 : : #include "lex/luthor.c"
1219 : :
1220 : : #include "keywords.h"
1221 : :
1222 : : /* Root schema `rs` is null for top level parser. */
1223 : 66 : int fb_init_parser(fb_parser_t *P, fb_options_t *opts, const char *name,
1224 : : fb_error_fun error_out, void *error_ctx, fb_root_schema_t *rs)
1225 : : {
1226 : : size_t n, name_len;
1227 : : char *s;
1228 : :
1229 : 66 : memset(P, 0, sizeof(*P));
1230 : :
1231 [ + + ]: 66 : if (error_out) {
1232 : 50 : P->error_out = error_out;
1233 : 50 : P->error_ctx = error_ctx;
1234 : : } else {
1235 : 16 : P->error_out = fb_default_error_out;
1236 : : }
1237 [ + - ]: 66 : if (opts) {
1238 : 66 : memcpy(&P->opts, opts, sizeof(*opts));
1239 : : } else {
1240 : 0 : flatcc_init_options(&P->opts);
1241 : : }
1242 [ + + ]: 66 : P->schema.root_schema = rs ? rs : &P->schema.root_schema_instance;
1243 [ + - ][ - + ]: 66 : switch (P->opts.offset_size) {
1244 : : case 2:
1245 : : case 4:
1246 : : case 8:
1247 : : break;
1248 : : default:
1249 : : error(P, "invalid offset configured, must be 2, 4 (default), or 8");
1250 : 0 : return -1;
1251 : : }
1252 [ + - ][ - + ]: 66 : switch (P->opts.voffset_size) {
1253 : : case 2:
1254 : : case 4:
1255 : : case 8:
1256 : : break;
1257 : : default:
1258 : : error(P, "invalid voffset configured, must be 2 (default), 4, or 8");
1259 : 0 : return -1;
1260 : : }
1261 [ - + ]: 66 : if (!name) {
1262 : : /* Mostly for testing, just so we always have a name. */
1263 : : name = FLATCC_DEFAULT_FILENAME;
1264 : : }
1265 [ - + ]: 66 : if (name == 0) {
1266 : : name = "";
1267 : : }
1268 : 66 : name_len = strlen(name);
1269 : 66 : checkmem((P->schema.basename = fb_create_basename(name, name_len, opts->default_schema_ext)));
1270 : 66 : n = strlen(P->schema.basename);
1271 : 66 : checkmem(s = fb_copy_path_n(P->schema.basename, n));
1272 : 66 : pstrntoupper(s, n);
1273 : 66 : P->schema.basenameup = s;
1274 : 66 : P->schema.name.name.s.s = s;
1275 : 66 : P->schema.name.name.s.len = (long)n;
1276 : 66 : checkmem((P->schema.errorname = fb_create_basename(name, name_len, "")));
1277 [ + + ]: 66 : if (opts->ns) {
1278 : 6 : P->schema.prefix.s = (char *)opts->ns;
1279 : 6 : P->schema.prefix.len = (long)strlen(opts->ns);
1280 : : }
1281 : 66 : P->current_scope = fb_add_scope(P, 0);
1282 : : assert(P->current_scope == fb_scope_table_find(&P->schema.root_schema->scope_index, 0, 0));
1283 : 66 : return 0;
1284 : : }
1285 : :
1286 : : /*
1287 : : * Main entry function for this specific parser type.
1288 : : * We expect a zero terminated string.
1289 : : *
1290 : : * The parser structure is uninitialized upon entry, and should be
1291 : : * cleared with `clear_flatbuffer_parser` subsequently.
1292 : : *
1293 : : * Datastructures point into the token buffer and into the input
1294 : : * buffer, so the parser and input should not be cleared prematurely.
1295 : : *
1296 : : * The input buffer must remain valid until the parser is cleared
1297 : : * because the internal represenation stores pointers into the buffer.
1298 : : *
1299 : : * `own_buffer` indicates that the the buffer should be deallocated when
1300 : : * the parser is cleaned up.
1301 : : */
1302 : 34 : int fb_parse(fb_parser_t *P, const char *input, size_t len, int own_buffer)
1303 : : {
1304 : : static const char *id_none = "NONE";
1305 : : static const char *id_ubyte = "ubyte";
1306 : :
1307 : 34 : P->line = input;
1308 : 34 : P->linenum = 1;
1309 : :
1310 : : /* Used with union defaults. */
1311 : 34 : inject_token(&P->t_none, id_none, LEX_TOK_ID);
1312 : 34 : inject_token(&P->t_ubyte, id_ubyte, tok_kw_ubyte);
1313 : :
1314 [ + + ]: 34 : if (own_buffer) {
1315 : 33 : P->managed_input = input;
1316 : : }
1317 : 34 : lex(input, len, 0, P);
1318 : :
1319 : 34 : P->te = P->token;
1320 : 34 : P->token = P->ts;
1321 : : /* Only used while processing table id's. */
1322 : 34 : checkmem((P->tmp_field_marker = malloc((size_t)P->opts.vt_max_count)));
1323 [ - + ]: 34 : if (P->token->id == tok_kw_doc_comment) {
1324 : 0 : next(P);
1325 : : }
1326 : 34 : parse_schema(P);
1327 : 34 : return P->failed;
1328 : : }
1329 : :
1330 : 41 : static void __destroy_scope_item(void *item, fb_scope_t *scope)
1331 : : {
1332 : : /* Each scope points into table that is cleared separately. */
1333 : : (void)item;
1334 : :
1335 : 41 : fb_symbol_table_clear(&scope->symbol_index);
1336 : 41 : }
1337 : :
1338 : 66 : void fb_clear_parser(fb_parser_t *P)
1339 : : {
1340 : : fb_symbol_t *sym;
1341 : : fb_compound_type_t *ct;
1342 : :
1343 [ + + ]: 237 : for (sym = P->schema.symbols; sym; sym = sym->link) {
1344 [ + - ]: 171 : switch (sym->kind) {
1345 : : case fb_is_struct:
1346 : : case fb_is_table:
1347 : : case fb_is_rpc_service:
1348 : : case fb_is_enum:
1349 : : case fb_is_union:
1350 : : ct = (fb_compound_type_t *)sym;
1351 : 171 : fb_symbol_table_clear(&ct->index);
1352 : 171 : fb_value_set_clear(&ct->value_set);
1353 : : }
1354 : : }
1355 : 66 : fb_schema_table_clear(&P->schema.root_schema_instance.include_index);
1356 : 66 : fb_name_table_clear(&P->schema.root_schema_instance.attribute_index);
1357 : 66 : ptr_set_clear(&P->schema.visible_schema);
1358 [ + + ]: 66 : if (P->tmp_field_marker) {
1359 : 34 : free(P->tmp_field_marker);
1360 : : }
1361 [ + + ]: 66 : if (P->ts) {
1362 : 34 : free(P->ts);
1363 : : }
1364 [ + - ]: 66 : if (P->schema.basename) {
1365 : 66 : free((void *)P->schema.basename);
1366 : : }
1367 [ + - ]: 66 : if (P->schema.basenameup) {
1368 : : /* Add self to set of visible schema. */
1369 : 66 : ptr_set_insert_item(&P->schema.visible_schema, &P->schema, ht_keep);
1370 : 66 : free((void *)P->schema.basenameup);
1371 : : }
1372 [ + - ]: 66 : if (P->schema.errorname) {
1373 : 66 : free((void *)P->schema.errorname);
1374 : : }
1375 : : /*
1376 : : * P->referer_path in included files points to parent P->path, so
1377 : : * don't free it, and don't access it after this point.
1378 : : */
1379 [ + + ]: 66 : if (P->path) {
1380 : 33 : free((void *)P->path);
1381 : : }
1382 : 66 : fb_scope_table_destroy(&P->schema.root_schema_instance.scope_index,
1383 : : __destroy_scope_item, 0);
1384 : : /* Destroy last since destructor has references into elem buffer. */
1385 : : clear_elem_buffers(P);
1386 [ + + ]: 66 : if (P->managed_input) {
1387 : 33 : free((void *)P->managed_input);
1388 : : }
1389 : 66 : memset(P, 0, sizeof(*P));
1390 : 66 : }
|