diff options
Diffstat (limited to 'src/compiler/codegen_c.c')
-rw-r--r-- | src/compiler/codegen_c.c | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/src/compiler/codegen_c.c b/src/compiler/codegen_c.c new file mode 100644 index 0000000..5e5fe0e --- /dev/null +++ b/src/compiler/codegen_c.c @@ -0,0 +1,285 @@ +#include "codegen_c.h" +#include "fileio.h" +#include "pstrutil.h" +#include "../../external/hash/str_set.h" + +int fb_open_output_file(fb_output_t *out, const char *name, size_t len, const char *ext) +{ + char *path; + int ret; + const char *prefix = out->opts->outpath ? out->opts->outpath : ""; + size_t prefix_len = strlen(prefix); + + if (out->fp) { + return 0; + } + checkmem((path = fb_create_join_path_n(prefix, prefix_len, name, len, ext, 1))); + out->fp = fopen(path, "wb"); + ret = 0; + if (!out->fp) { + fprintf(stderr, "error opening file for write: %s\n", path); + ret = -1; + } + free(path); + return ret; +} + +void fb_close_output_file(fb_output_t *out) +{ + /* Concatenate covers either stdout or a file. */ + if (!out->opts->gen_outfile && !out->opts->gen_stdout && out->fp) { + fclose(out->fp); + out->fp = 0; + } + /* Keep out->fp open for next file. */ +} + +void fb_end_output_c(fb_output_t *out) +{ + if (out->fp != stdout && out->fp) { + fclose(out->fp); + } + out->fp = 0; +} + +/* + * If used with --stdout or concat=<file>, we assume there + * are no other language outputs at the same time. + */ +int fb_init_output_c(fb_output_t *out, fb_options_t *opts) +{ + const char *nsc; + char *path = 0; + size_t n; + const char *mode = opts->gen_append ? "ab" : "wb"; + const char *prefix = opts->outpath ? opts->outpath : ""; + int ret = -1; + + memset(out, 0, sizeof(*out)); + out->opts = opts; + nsc = opts->nsc; + if (nsc) { + n = strlen(opts->nsc); + if (n > FLATCC_NAMESPACE_MAX) { + fprintf(stderr, "common namespace argument is limited to %i characters\n", (int)FLATCC_NAMESPACE_MAX); + return -1; + } + } else { + nsc = FLATCC_DEFAULT_NAMESPACE_COMMON; + n = strlen(nsc); + } + strncpy(out->nsc, nsc, FLATCC_NAMESPACE_MAX); + out->nsc[FLATCC_NAMESPACE_MAX] = '\0'; + if (n) { + out->nsc[n] = '_'; + out->nsc[n + 1] = '\0'; + } + pstrcpyupper(out->nscup, out->nsc); + out->nscup[n] = '\0'; /* No trailing _ */ + out->spacing = opts->cgen_spacing; + if (opts->gen_stdout) { + out->fp = stdout; + return 0; + } + if (!out->opts->gen_outfile) { + /* Normal operation to multiple header filers. */ + return 0; + } + checkmem((path = fb_create_join_path(prefix, out->opts->gen_outfile, "", 1))); + out->fp = fopen(path, mode); + if (!out->fp) { + fprintf(stderr, "error opening file for write: %s\n", path); + ret = -1; + goto done; + } + ret = 0; +done: + if (path) { + free(path); + } + return ret; +} + +static void _str_set_destructor(void *context, char *item) +{ + (void)context; + + free(item); +} + +/* + * Removal of duplicate inclusions is only for a cleaner output - it is + * not stricly necessary because the preprocessor handles include + * guards. The guards are required to deal with concatenated files + * regardless unless we generate special code for concatenation. + */ +void fb_gen_c_includes(fb_output_t *out, const char *ext, const char *extup) +{ + fb_include_t *inc = out->S->includes; + char *basename, *basenameup, *s; + str_set_t set; + + fb_clear(set); + + /* Don't include our own file. */ + str_set_insert_item(&set, fb_copy_path(out->S->basenameup), ht_keep); + while (inc) { + checkmem((basename = fb_create_basename( + inc->name.s.s, (size_t)inc->name.s.len, out->opts->default_schema_ext))); + inc = inc->link; + checkmem((basenameup = fb_copy_path(basename))); + s = basenameup; + while (*s) { + *s = (char)toupper(*s); + ++s; + } + if (str_set_insert_item(&set, basenameup, ht_keep)) { + free(basenameup); + free(basename); + continue; + } + /* The include guard is needed when concatening output. */ + fprintf(out->fp, + "#ifndef %s%s\n" + "#include \"%s%s\"\n" + "#endif\n", + basenameup, extup, basename, ext); + free(basename); + /* `basenameup` stored in str_set. */ + } + str_set_destroy(&set, _str_set_destructor, 0); +} + +int fb_copy_scope(fb_scope_t *scope, char *buf) +{ + size_t n, len; + fb_ref_t *name; + + len = (size_t)scope->prefix.len; + for (name = scope->name; name; name = name->link) { + n = (size_t)name->ident->len; + len += n + 1; + } + if (len > FLATCC_NAMESPACE_MAX + 1) { + buf[0] = '\0'; + return -1; + } + len = (size_t)scope->prefix.len; + memcpy(buf, scope->prefix.s, len); + for (name = scope->name; name; name = name->link) { + n = (size_t)name->ident->len; + memcpy(buf + len, name->ident->text, n); + len += n + 1; + buf[len - 1] = '_'; + } + buf[len] = '\0'; + return (int)len; +} + +void fb_scoped_symbol_name(fb_scope_t *scope, fb_symbol_t *sym, fb_scoped_name_t *sn) +{ + fb_token_t *t = sym->ident; + + if (sn->scope != scope) { + if (0 > (sn->scope_len = fb_copy_scope(scope, sn->text))) { + sn->scope_len = 0; + fprintf(stderr, "skipping too long namespace\n"); + } + } + sn->len = (int)t->len; + sn->total_len = sn->scope_len + sn->len; + if (sn->total_len > FLATCC_NAME_BUFSIZ - 1) { + fprintf(stderr, "warning: truncating identifier: %.*s\n", sn->len, t->text); + sn->len = FLATCC_NAME_BUFSIZ - sn->scope_len - 1; + sn->total_len = sn->scope_len + sn->len; + } + memcpy(sn->text + sn->scope_len, t->text, (size_t)sn->len); + sn->text[sn->total_len] = '\0'; +} + +int fb_codegen_common_c(fb_output_t *out) +{ + size_t nsc_len; + int ret; + + nsc_len = strlen(out->nsc) - 1; + ret = 0; + if (out->opts->cgen_common_reader) { + if (fb_open_output_file(out, out->nsc, nsc_len, "_common_reader.h")) { + return -1; + } + ret = fb_gen_common_c_header(out); + fb_close_output_file(out); + } + if (!ret && out->opts->cgen_common_builder) { + if (fb_open_output_file(out, out->nsc, nsc_len, "_common_builder.h")) { + return -1; + } + fb_gen_common_c_builder_header(out); + fb_close_output_file(out); + } + return ret; +} + +int fb_codegen_c(fb_output_t *out, fb_schema_t *S) +{ + size_t basename_len; + /* OK if no files were processed. */ + int ret = 0; + + out->S = S; + out->current_scope = fb_scope_table_find(&S->root_schema->scope_index, 0, 0); + basename_len = strlen(out->S->basename); + if (out->opts->cgen_reader) { + if (fb_open_output_file(out, out->S->basename, basename_len, "_reader.h")) { + ret = -1; + goto done; + } + if ((ret = fb_gen_c_reader(out))) { + goto done; + } + fb_close_output_file(out); + } + if (out->opts->cgen_builder) { + if (fb_open_output_file(out, out->S->basename, basename_len, "_builder.h")) { + ret = -1; + goto done; + } + if ((ret = fb_gen_c_builder(out))) { + goto done; + } + fb_close_output_file(out); + } + if (out->opts->cgen_verifier) { + if (fb_open_output_file(out, out->S->basename, basename_len, "_verifier.h")) { + ret = -1; + goto done; + } + if ((ret = fb_gen_c_verifier(out))) { + goto done; + } + fb_close_output_file(out); + } + if (out->opts->cgen_json_parser) { + if (fb_open_output_file(out, out->S->basename, basename_len, "_json_parser.h")) { + ret = -1; + goto done; + } + if ((ret = fb_gen_c_json_parser(out))) { + goto done; + } + fb_close_output_file(out); + } + if (out->opts->cgen_json_printer) { + if (fb_open_output_file(out, out->S->basename, basename_len, "_json_printer.h")) { + ret = -1; + goto done; + } + if ((ret = fb_gen_c_json_printer(out))) { + goto done; + } + fb_close_output_file(out); + } +done: + return ret; +} |