LCOV - code coverage report
Current view: top level - src/compiler - parser.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 436 553 78.8 %
Date: 2016-11-30 13:12:14 Functions: 35 42 83.3 %
Branches: 228 406 56.2 %

           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 : }

Generated by: LCOV version 1.12