Example #1
0
/* `enum` must already be matched. */
static void parse_enum_decl(fb_parser_t *P, fb_compound_type_t *ct)
{
    fb_token_t *t, *t0;
    fb_member_t *member;

    if (!(ct->symbol.ident = match(P, LEX_TOK_ID, "enum declaration expected identifier"))) {
        goto fail;
    }
    if (optional(P, ':')) {
        parse_type(P, &ct->type);
        if (ct->type.type != vt_scalar_type) {
            error_tok(P, ct->type.t, "integral type expected");
        } else {
            switch (ct->type.t->id) {
            case tok_kw_float:
            case tok_kw_double:
                error_tok(P, ct->type.t, "integral type expected");
            default:
                break;
            }
        }
    }
    ct->metadata = parse_metadata(P);
    if (!((t0 = match(P, '{', "enum declaration expected '{'")))) {
        goto fail;
    }
    for (;;) {
        if (!(t = match(P, LEX_TOK_ID,
                "member identifier expected"))) {
            goto fail;
        }
        if (P->failed >= FLATCC_MAX_ERRORS) {
            goto fail;
        }
        member = fb_add_member(P, &ct->members);
        member->symbol.ident = t;
        if (optional(P, '=')) {
            t = P->token;
            parse_value(P, &member->value, 0, "integral constant expected");
            /* Leave detailed type (e.g. no floats) and range checking to a later stage. */
        }
        /*
         * Trailing comma is optional in flatc but not in grammar, we
         * follow flatc.
         */
        if (!optional(P, ',') || P->token->id == '}') {
            break;
        }
        P->doc = 0;
    }
    if (t0) {
        advance(P, '}', "enum missing closing '}' to match", t0);
    }
    revert_symbols(&ct->members);
    return;
fail:
    recover(P, '}', 1);
}
Example #2
0
static void parse_value(fb_parser_t *P, fb_value_t *v, int flags, const char *error_msg)
{
    fb_token_t *t;
    fb_token_t *sign;

    sign = optional(P, '-');
    t = P->token;

    switch (t->id) {
    case LEX_TOK_INT:
        read_integer_value(P, t, v, sign != 0);
        break;
    case LEX_TOK_FLOAT:
        read_float_value(P, t, v, sign != 0);
        break;
    case tok_kw_true:
        v->b = 1;
        v->type = vt_bool;
        break;
    case tok_kw_false:
        v->b = 0;
        v->type = vt_bool;
        break;
    case LEX_TOK_STRING_BEGIN:
        next(P);
        parse_string_literal(P, v);
        if (!(flags & allow_string_value)) {
            v->type = vt_invalid;
            error_tok(P, t, error_msg);
            return;
        }
        if (sign) {
            v->type = vt_invalid;
            error_tok(P, t, "string constants cannot be signed");
            return;
        }
        return;
    case LEX_TOK_ID:
        parse_ref(P, &v->ref);
        v->type = vt_name_ref;
        if (sign) {
            v->type = vt_invalid;
            /* Technically they could, but we do not allow it. */
            error_tok(P, t, "named values cannot be signed");
        }
        return;
    default:
        /* We might have consumed a sign, but never mind that. */
        error_tok(P, t, error_msg);
        return;
    }
    if (sign && v->type == vt_bool) {
        v->type = vt_invalid;
        error_tok(P, t, "boolean constants cannot be signed");
    }
    next(P);
}
Example #3
0
/* ':' must already be matched */
static void parse_type(fb_parser_t *P, fb_value_t *v)
{
    fb_token_t *t = 0;
    fb_token_t *t0 = P->token;
    int vector = 0;

    v->type = vt_invalid;
    while ((t = optional(P, '['))) {
        ++vector;
    }
    if (vector > 1) {
        error_tok(P, t0, "vector type can only be one-dimensional");
    }
    switch (P->token->id) {
    case tok_kw_int:
    case tok_kw_bool:
    case tok_kw_byte:
    case tok_kw_long:
    case tok_kw_uint:
    case tok_kw_float:
    case tok_kw_short:
    case tok_kw_ubyte:
    case tok_kw_ulong:
    case tok_kw_ushort:
    case tok_kw_double:
        v->t = P->token;
        v->type = vector ? vt_vector_type : vt_scalar_type;
        next(P);
        break;
    case tok_kw_string:
        v->t = P->token;
        v->type = vector ? vt_vector_string_type : vt_string_type;
        next(P);
        break;
    case LEX_TOK_ID:
        parse_ref(P, &v->ref);
        v->type = vector ? vt_vector_type_ref : vt_type_ref;
        break;
    case ']':
        error_tok(P, t, "vector type cannot be empty");
        break;
    default:
        error_tok(P, t, "invalid type specifier");
        break;
    }
    while (optional(P, ']') && vector--) {
    }
    if (vector) {
        error_tok_2(P, t, "vector type missing ']' to match", t0);
    }
    if ((t = optional(P, ']'))) {
        error_tok_2(P, t, "extra ']' not matching", t0);
        while (optional(P, ']')) {
        }
    }
}
Example #4
0
/*
 * We disallow escape characters, newlines and other control characters,
 * but especially escape characters because they would require us to
 * reallocate the string and convert the escaped characters. We also
 * disallow non-utf8 characters, but we do not check for it. The tab
 * character could meaningfully be accepted, but we don't.
 *
 * String literals are only used to name attributes, namespaces,
 * file identifiers and file externsions, so we really have no need
 * for these extra featuresescape .
 *
 * JSON strings should be handled separately, if or when supported -
 * either by converting escapes and reallocating the string, or
 * simply by ignoring the escape errors and use the string unmodified.
 */
static void parse_string_literal(fb_parser_t *P, fb_value_t *v)
{
    fb_token_t *t;

    v->type = vt_string;
    v->s.s = 0;
    v->s.len = 0;

    for (;;) {
        t = P->token;
        switch (t->id) {
        case LEX_TOK_STRING_PART:
            if (v->s.s == 0) {
                v->s.s = (char *)t->text;
            }
            break;
        case LEX_TOK_STRING_ESCAPE:
            v->type = vt_invalid;
            error_tok(P, t, "escape not allowed in strings");
            break;
        case LEX_TOK_STRING_CTRL:
            v->type = vt_invalid;
            error_tok_as_string(P, t, "control characters not allowed in strings", "?", 1);
            break;
        case LEX_TOK_STRING_NEWLINE:
            v->type = vt_invalid;
            error_tok(P, t, "newline not allowed in strings");
            break;
        case LEX_TOK_STRING_UNTERMINATED:
        case LEX_TOK_STRING_END:
            goto done;

        default:
            error_tok(P, t, "internal error: unexpected token in string");
            v->type = vt_invalid;
            goto done;
        }
        next(P);
    }
done:
    /*
     * If we were to ignore all errors, we would get the full
     * string as is excluding delimiting quotes.
     */
    if (v->s.s) {
        v->s.len = P->token->text - v->s.s;
    }
    if (!match(P, LEX_TOK_STRING_END, "unterminated string")) {
        v->type = vt_invalid;
    }
}
Example #5
0
static void parse_method(fb_parser_t *P, fb_member_t *fld)
{
    fb_token_t *t;
    if (!(t = match(P, LEX_TOK_ID, "method expected identifier"))) {
        goto fail;
    }
    fld->symbol.ident = t;
    if (!match(P, '(', "method expected '(' after identifier")) {
        goto fail;
    }
    parse_type(P, &fld->req_type);
    if (!match(P, ')', "method expected ')' after request type")) {
        goto fail;
    }
    if (!match(P, ':', "method expected ':' before mandatory response type")) {
        goto fail;
    }
    parse_type(P, &fld->type);
    if ((t = optional(P, '='))) {
        error_tok(P, t, "method does not accept an initializer");
        goto fail;
    }
    fld->metadata = parse_metadata(P);
    advance(P, ';', "method must be terminated with ';'", 0);
    return;
fail:
    recover2(P, ';', 1, '}', 0);
}
Example #6
0
static int parse_schema(fb_parser_t *P)
{
    fb_token_t *t, *t0;
    parse_include(P);
    t = P->token;
    for (;;) {
        if (is_end(t)) {
            break;
        }
        if (P->failed >= FLATCC_MAX_ERRORS) {
            return -1;
        }
        t0 = t;
        parse_schema_decl(P);
        t = P->token;
        if (t == t0) {
            if (P->failed) {
                return -1;
            }
            error_tok(P, t, "extra tokens in input");
            return -1;
        }
    }
    revert_names(&P->schema.attributes);
    revert_symbols(&P->schema.symbols);
    return 0;
}
Example #7
0
/* `struct` or `table` must already be matched. */
static void parse_compound_type(fb_parser_t *P, fb_compound_type_t *ct)
{
    fb_token_t *t = 0;

    if (!(t = match(P, LEX_TOK_ID, "Declaration expected an identifier"))) {
        goto fail;
    }
    ct->symbol.ident = t;
    ct->metadata = parse_metadata(P);
    if (!(match(P, '{', "Declaration expected '{'"))) {
        goto fail;
    }
    t = P->token;

/* Allow empty tables and structs. */
#if 0
    if (P->token->id == '}') {
        error_tok(P, t, "table / struct declaration cannot be empty");
    }
#endif
    while (P->token->id != '}') {
        parse_field(P, fb_add_member(P, &ct->members));
        if (P->failed >= FLATCC_MAX_ERRORS) {
            goto fail;
        }
    }
    if (!optional(P, '}') && t) {
        error_tok_2(P, P->token, "Declaration missing closing '}' to match", t);
    }
    revert_symbols(&ct->members);
    return;
fail:
    recover(P, '}', 1);
}
Example #8
0
static void parse_schema_decl(fb_parser_t *P)
{
    switch(P->token->id) {
    case tok_kw_namespace:
        next(P);
        parse_namespace(P);
        break;
    case tok_kw_file_extension:
        next(P);
        parse_file_extension(P, &P->schema.file_extension);
        break;
    case tok_kw_file_identifier:
        next(P);
        parse_file_identifier(P, &P->schema.file_identifier);
        break;
    case tok_kw_root_type:
        next(P);
        parse_root_type(P, &P->schema.root_type);
        break;
    case tok_kw_attribute:
        next(P);
        parse_attribute(P, fb_add_attribute(P));
        break;
    case tok_kw_struct:
        next(P);
        parse_compound_type(P, fb_add_struct(P));
        break;
    case tok_kw_table:
        next(P);
        parse_compound_type(P, fb_add_table(P));
        break;
    case tok_kw_enum:
        next(P);
        parse_enum_decl(P, fb_add_enum(P));
        break;
    case tok_kw_union:
        next(P);
        parse_union_decl(P, fb_add_union(P));
        break;
    case '{':
        error_tok(P, P->token, "JSON objects are not supported by this implementation");
        break;
    default:
        error_tok(P, P->token, "unexpected token in schema definition");
        break;
    }
}
Example #9
0
static void read_float_value(fb_parser_t *P, fb_token_t *t, fb_value_t *v, int sign)
{
    char *end;

    v->type = vt_float;
    v->f = strtod(t->text, &end);
    if (end != t->text + t->len) {
        v->type = vt_invalid;
        error_tok(P, t, "invalid float format");
    } else if (t->text[0] == '.') {
        v->type = vt_invalid;
        /* The FB spec requires this, in line with the JSON format. */
        error_tok(P, t, "numeric values must start with a digit");
    } else if (sign) {
        v->f = -v->f;
    }
}
Example #10
0
static inline fb_token_t *match(fb_parser_t *P, long id, char *msg) {
    fb_token_t *t = 0;
    if (P->token->id == id) {
        t = P->token;
        next(P);
    } else {
        error_tok(P, P->token, msg);
    }
    return t;
}
Example #11
0
static void parse_root_type(fb_parser_t *P, fb_root_type_t *rt)
{
    fb_token_t *t = P->token;

    if (rt->name) {
        error_tok(P, P->token, "root_type already set");
    }
    parse_ref(P, &rt->name);
    rt->scope = P->current_scope;
    advance(P, ';', "missing ';' expected by root_type at", t);
}
Example #12
0
static void read_integer_value(fb_parser_t *P, fb_token_t *t, fb_value_t *v, int sign)
{
    char *end;

    v->type = vt_uint;
    v->u = strtoull(t->text, &end, 0);
    if (end && end != t->text + t->len) {
        v->type = vt_invalid;
        error_tok(P, t, "invalid integer format");
    }
    if (sign) {
        v->i = -(int64_t)v->u;
        v->type = vt_int;
#ifdef FLATCC_FAIL_ON_INT_SIGN_OVERFLOW
        /* Sometimes we might want this, so don't fail by default. */
        if (v->i > 0) {
            v->type = vt_invalid;
            error_tok(P, t, "sign overflow in integer format");
        }
#endif
    }
}
Example #13
0
static void read_integer_value(fb_parser_t *P, fb_token_t *t, fb_value_t *v, int sign)
{
    int status;

    v->type = vt_uint;
    /* The token does not store the sign internally. */
    parse_integer(t->text, t->len, &v->u, &status);
    if (status != PARSE_INTEGER_UNSIGNED) {
        v->type = vt_invalid;
        error_tok(P, t, "invalid integer format");
    }
    if (sign) {
        v->i = -(int64_t)v->u;
        v->type = vt_int;
#ifdef FLATCC_FAIL_ON_INT_SIGN_OVERFLOW
        /* Sometimes we might want this, so don't fail by default. */
        if (v->i > 0) {
            v->type = vt_invalid;
            error_tok(P, t, "sign overflow in integer format");
        }
#endif
    }
}
Example #14
0
/* Current token must be an identifier. */
static void parse_ref(fb_parser_t *P, fb_ref_t **ref)
{
    *ref = fb_add_ref(P, P->token);
    next(P);
    ref = &((*ref)->link);
    while (optional(P, '.')) {
        if (P->token->id != LEX_TOK_ID) {
            error_tok(P, P->token, "namespace prefix expected identifier");
            break;
        }
        *ref = fb_add_ref(P, P->token);
        ref = &((*ref)->link);
        next(P);
    }
}
Example #15
0
/* `union` must already be matched. */
static void parse_union_decl(fb_parser_t *P, fb_compound_type_t *ct)
{
    fb_token_t *t0;
    fb_member_t *member;
    fb_ref_t *ref;

    if (!(ct->symbol.ident = match(P, LEX_TOK_ID, "union declaration expected identifier"))) {
        goto fail;
    }
    ct->metadata = parse_metadata(P);
    if (!((t0 = match(P, '{', "union declaration expected '{'")))) {
        goto fail;
    }
    for (;;) {
        if (P->token->id != LEX_TOK_ID) {
            error_tok(P, P->token, "union expects an identifier");
            goto fail;
        }
        if (P->failed >= FLATCC_MAX_ERRORS) {
            goto fail;
        }
        member = fb_add_member(P, &ct->members);
        parse_ref(P, &ref);
        member->type.ref = ref;
        member->type.type = vt_type_ref;
        while (ref->link) {
            ref = ref->link;
        }
        /* The union member is the unqualified reference. */
        member->symbol.ident = ref->ident;
        if (optional(P, '=')) {
            parse_value(P, &member->value, 0, "integral constant expected");
            /* Leave detailed type (e.g. no floats) and range checking to a later stage. */
        }
        if (!optional(P, ',') || P->token->id == '}') {
            break;
        }
        P->doc = 0;
    }
    advance(P, '}', "union missing closing '}' to match", t0);
    revert_symbols(&ct->members);
    /* Add implicit `NONE` member first in the list. */
    member = fb_add_member(P, &ct->members);
    member->symbol.ident = &P->t_none;
    return;
fail:
    recover2(P, ';', 1, '}', 0);
}
Example #16
0
static void parse_include(fb_parser_t *P)
{
    fb_token_t *t = P->token;

    while (optional(P, tok_kw_include)) {
        if (P->opts.disable_includes) {
            error_tok(P, t, "include statements not supported by current environment");
        }
        if (P->failed >= FLATCC_MAX_ERRORS) {
            return;
        }
        if (!match(P, LEX_TOK_STRING_BEGIN,
                    "include expected a string literal as filename")) {
            recover(P, ';', 1);
        }
        parse_string_literal(P, &fb_add_include(P)->name);
        match(P, ';', "include statement expected ';'");
    }
}
Example #17
0
static void parse_namespace(fb_parser_t *P)
{
    fb_ref_t *ref = 0;
    fb_token_t *t = P->token;

    if (optional(P, ';') && t) {
        /* Revert to global namespace. */
        P->current_scope = 0;
        return;
    }
    if (P->token->id != LEX_TOK_ID) {
        error_tok(P, P->token, "namespace expects an identifier");
        recover(P, ';', 1);
        return;
    }
    parse_ref(P, &ref);
    advance(P, ';', "missing ';' expected by namespace at", t);
    P->current_scope = fb_add_scope(P, ref);
}
Example #18
0
static void parse_file_identifier(fb_parser_t *P, fb_value_t *v)
{
    fb_token_t *t;
    if (v->type != vt_missing) {
        error_tok_as_string(P, P->token, "file identifier already set", v->s.s, v->s.len);
    }
    if (!match(P, LEX_TOK_STRING_BEGIN, "file_identifier expected string literal")) {
        goto fail;
    }
    t = P->token;
    parse_string_literal(P, v);
    if (v->s.s && v->s.len != 4) {
        v->type = vt_invalid;
        error_tok(P, t, "file_identifier must be 4 characters");
    }
    match(P, ';', "file_identifier expected ';'");
    return;
fail:
    recover(P, ';', 1);
}
Example #19
0
static fb_token_t *next(fb_parser_t *P)
{
again:
    ++P->token;
    if (P->token == P->te) {
        /* We keep returning end of token to help binary operators etc., if any. */
        --P->token;
        switch (P->token->id) {
        case LEX_TOK_EOS: case LEX_TOK_EOB: case LEX_TOK_EOF:
            P->token->id = LEX_TOK_EOF;
            return P->token;
        }
        error_tok(P, P->token, "Unexpected end of input");
    }
    if (P->token->id == tok_kw_doc_comment) {
        fb_add_doc(P, P->token);
        goto again;
    }
    debug_token("next", P->token);
    return P->token;
}
Example #20
0
static fb_token_t *next(fb_parser_t *P)
{
again:
    ++P->token;
    if (P->token == P->te) {
        /* We keep returning end of token to help binary operators etc., if any. */
        --P->token;
        assert(0);
        switch (P->token->id) {
        case LEX_TOK_EOS: case LEX_TOK_EOB: case LEX_TOK_EOF:
            P->token->id = LEX_TOK_EOF;
            return P->token;
        }
        error_tok(P, P->token, "unexpected end of input");
    }
    if (P->token->id == tok_kw_doc_comment) {
        /* Note: we can have blanks that are control characters here, such as \t. */
        fb_add_doc(P, P->token);
        goto again;
    }
    debug_token("next", P->token);
    return P->token;
}
Example #21
0
static void parse_schema_decl(fb_parser_t *P)
{
    switch(P->token->id) {
    case tok_kw_namespace:
        next(P);
        parse_namespace(P);
        break;
    case tok_kw_file_extension:
        next(P);
        parse_file_extension(P, &P->schema.file_extension);
        break;
    case tok_kw_file_identifier:
        next(P);
        parse_file_identifier(P, &P->schema.file_identifier);
        break;
    case tok_kw_root_type:
        next(P);
        parse_root_type(P, &P->schema.root_type);
        break;
    case tok_kw_attribute:
        next(P);
        parse_attribute(P, fb_add_attribute(P));
        break;
    case tok_kw_struct:
        next(P);
        parse_compound_type(P, fb_add_struct(P), tok_kw_struct);
        break;
    case tok_kw_table:
        next(P);
        parse_compound_type(P, fb_add_table(P), tok_kw_table);
        break;
    case tok_kw_rpc_service:
        next(P);
        parse_compound_type(P, fb_add_rpc_service(P), tok_kw_rpc_service);
        break;
    case tok_kw_enum:
        next(P);
        parse_enum_decl(P, fb_add_enum(P));
        break;
    case tok_kw_union:
        next(P);
        parse_union_decl(P, fb_add_union(P));
        break;
    case tok_kw_include:
        error_tok(P, P->token, "include statements must be placed first in the schema");
        break;
    case '{':
        error_tok(P, P->token, "JSON objects in schema file is not supported - but a schema specific JSON parser can be generated");
        break;
    case LEX_TOK_CTRL:
        error_tok_as_string(P, P->token, "unexpected control character in schema definition", "?", 1);
        break;
    case LEX_TOK_COMMENT_CTRL:
        if (lex_isblank(P->token->text[0])) {
            /* This also strips tabs in doc comments. */
            next(P);
            break;
        }
        error_tok_as_string(P, P->token, "unexpected control character in comment", "?", 1);
        break;
    case LEX_TOK_COMMENT_UNTERMINATED:
        error_tok_as_string(P, P->token, "unterminated comment", "<eof>", 5);
        break;
    default:
        error_tok(P, P->token, "unexpected token in schema definition");
        break;
    }
}