diff options
-rw-r--r-- | binfile.c | 16 | ||||
-rw-r--r-- | main.c | 2 | ||||
-rw-r--r-- | package.c | 351 | ||||
-rwxr-xr-x | pkg.sh | 2 | ||||
-rw-r--r-- | std/arr.toc | 2 | ||||
-rw-r--r-- | std/io.toc | 12 | ||||
-rw-r--r-- | test.toc | 7 | ||||
-rw-r--r-- | types.c | 4 | ||||
-rw-r--r-- | types.h | 13 |
9 files changed, 211 insertions, 198 deletions
@@ -277,11 +277,14 @@ static void write_vlq(FILE *fp, U64 x) { } static U64 read_vlq(FILE *fp) { - U8 byte = read_u8(fp); - if (byte & 0x80) { - return (read_vlq(fp) << 7) + (byte & 0x7F); - } else { - return byte; + U64 q = 0; + U64 bit = 0; + while (1) { + U8 byte = read_u8(fp); + assert(!feof(fp)); + q |= (U64)(byte & 0x7F) << bit; + bit += 7; + if (!(byte & 0x80)) return q; } } @@ -291,6 +294,8 @@ static void binfile_test(void) { FILE *fp = tmpfile(); U64 a = 234873485734; write_vlq(fp, a); + U64 s = 3; + write_vlq(fp, s); I64 b = -123981232131; write_i64(fp, b); U8 c = 12; @@ -313,6 +318,7 @@ static void binfile_test(void) { } expect(U64, U64_FMT, read_vlq(fp), a); + expect(U64, U64_FMT, read_vlq(fp), s); expect(I64, I64_FMT, read_i64(fp), b); expect(U8, U8_FMT, read_u8(fp), c); expect(F32, F32_FMT, read_f32(fp), d); @@ -22,6 +22,8 @@ TODO: #builtin("target sizeof int") etc. +allow any global declaration to be used before itself + #include constants in structs #if @@ -9,11 +9,16 @@ static bool export_decl(Exporter *ex, Declaration *d); static bool export_block(Exporter *ex, Block *b); static bool export_expr(Exporter *ex, Expression *e); +static bool export_fn(Exporter *ex, FnExpr *f); static bool import_footer(Importer *i); static void import_decl(Importer *im, Declaration *d); static void import_expr(Importer *im, Expression *e); static void import_block(Importer *im, Block *b); +static FnExpr *import_fn(Importer *im); +static void export_decl_external(Exporter *ex, Declaration *d); static inline Expression *import_expr_(Importer *im); +static bool export_struct(Exporter *ex, StructDef *s); +static StructDef *import_struct(Importer *im); static void exptr_create(Exporter *ex, FILE *out, const char *filename, ErrCtx *err_ctx) { @@ -22,8 +27,8 @@ static void exptr_create(Exporter *ex, FILE *out, const char *filename, ErrCtx * ex->exporting_to.ctx = err_ctx; ex->exporting_to.filename = filename; ex->ident_id = 0; - ex->exported_fns = NULL; - ex->exported_structs = NULL; + ex->nexported_fns = 0; + ex->nexported_structs = 0; ex->exported_idents = NULL; ex->started = false; ex->code = NULL; @@ -106,7 +111,7 @@ static inline bool import_bool(Importer *i) { static inline void export_char(Exporter *ex, char c) { write_char(ex->out, c); } -static inline bool import_char(Importer *i) { +static inline char import_char(Importer *i) { return read_char(i->in); } static inline void export_vlq(Exporter *ex, U64 x) { @@ -181,7 +186,12 @@ static inline void export_ident(Exporter *ex, Identifier i) { export_vlq(ex, 0); return; } + IdentDecl *idecl = ident_decl(i); + if (idecl && idecl->scope == NULL && idecl->kind == IDECL_DECL) { + /* make sure this is exported */ + export_decl_external(ex, idecl->decl); + } if (!i->export_id) { i->export_id = ++ex->ident_id; } @@ -198,6 +208,7 @@ static const U8 toc_package_indicator[3] = {116, 111, 112}; /* writes the header */ static void exptr_start(Exporter *ex, const char *pkg_name, size_t pkg_name_len) { const char *code = ex->code; + ex->decls_to_export = NULL; ex->started = true; export_u8(ex, toc_package_indicator[0]); export_u8(ex, toc_package_indicator[1]); @@ -265,12 +276,14 @@ static bool import_pkg(Allocator *allocr, Package *p, FILE *f, const char *fname return false; fseek(f, decls_offset, SEEK_SET); /* read declarations */ + size_t ndecls = import_u32(&i); p->stmts = NULL; - while (import_u8(&i)) { + for (size_t idx = 0; idx < ndecls; ++idx) { Statement *s = arr_add(&p->stmts); s->kind = STMT_DECL; import_decl(&i, &s->decl); } + assert(ftell(i.in) == (long)footer_offset); free(i.ident_map); if (!block_enter(NULL, p->stmts, 0)) @@ -328,18 +341,7 @@ static bool export_type(Exporter *ex, Type *type, Location where) { } break; case TYPE_STRUCT: { StructDef *struc = type->struc; - if (struc->export.id == 0) { - StructDef **ptr = arr_add(&ex->exported_structs); - *ptr = struc; - size_t nexported_structs = arr_len(ex->exported_structs); - if (nexported_structs > U32_MAX) { - err_print(struc->where, "Too many exported structure definitions (the maximum is " STRINGIFY(U32_MAX) ")."); - return false; - } - - struc->export.id = (U32)nexported_structs; - } - export_vlq(ex, (U64)struc->export.id); + export_struct(ex, struc); } break; case TYPE_EXPR: if (!export_expr(ex, type->expr)) @@ -403,9 +405,7 @@ static void import_type(Importer *im, Type *type) { } else type->fn.constness = NULL; } break; case TYPE_STRUCT: { - U64 struct_id = import_vlq(im); - assert(struct_id); - type->struc = &im->structs[struct_id - 1]; + type->struc = import_struct(im); } break; case TYPE_EXPR: type->expr = import_expr_(im); @@ -413,25 +413,6 @@ static void import_type(Importer *im, Type *type) { } } -static bool export_fn_ptr(Exporter *ex, FnExpr *f) { - if (f->export.id == 0) { - FnExpr **fptr = arr_add(&ex->exported_fns); - *fptr = f; - size_t nexported_fns = arr_len(ex->exported_fns); - if (nexported_fns > U32_MAX) { - err_print(f->where, "Too many exported functions (the maximum is " STRINGIFY(U32_MAX) ")."); - return false; - } - f->export.id = (U32)nexported_fns; - } - export_vlq(ex, f->export.id); - return true; -} - -static FnExpr *import_fn_ptr(Importer *im) { - return &im->fns[import_vlq(im) - 1]; -} - static bool export_val(Exporter *ex, Value val, Type *type, Location where); static bool export_val_ptr(Exporter *ex, void *v, Type *type, Location where) { switch (type->kind) { @@ -499,7 +480,7 @@ static bool export_val_ptr(Exporter *ex, void *v, Type *type, Location where) { } } break; case TYPE_FN: - if (!export_fn_ptr(ex, *(FnExpr **)v)) + if (!export_fn(ex, *(FnExpr **)v)) return false; break; case TYPE_UNKNOWN: @@ -561,7 +542,7 @@ static void import_val_ptr(Importer *im, void *v, Type *type) { } } break; case TYPE_FN: - *(FnExpr **)v = import_fn_ptr(im); + *(FnExpr **)v = import_fn(im); break; case TYPE_SLICE: { Slice *slice = v; @@ -708,7 +689,7 @@ static bool export_expr(Exporter *ex, Expression *e) { return false; break; case EXPR_FN: - if (!export_fn_ptr(ex, e->fn)) + if (!export_fn(ex, e->fn)) return false; break; case EXPR_BLOCK: @@ -775,32 +756,38 @@ static bool export_expr(Exporter *ex, Expression *e) { } break; case EXPR_FOR: { ForExpr *fo = e->for_; + if (!for_enter(e)) + return false; possibly_static_assert(sizeof fo->flags == 1); export_u8(ex, fo->flags); if ((fo->flags & FOR_ANNOTATED_TYPE) || found_type) if (!export_type(ex, &fo->type, e->where)) - return false; + goto for_fail; export_ident(ex, fo->index); export_ident(ex, fo->value); if (fo->flags & FOR_IS_RANGE) { if (!export_expr(ex, fo->range.from)) - return false; + goto for_fail; if (!export_optional_expr(ex, fo->range.to)) - return false; + goto for_fail; if (found_type) { if (!export_optional_val(ex, fo->range.stepval, &fo->type, e->where)) - return false; + goto for_fail; } else { if (!export_optional_expr(ex, fo->range.step)) - return false; + goto for_fail; } } else { if (!export_expr(ex, fo->of)) - return false; + goto for_fail; } if (!export_block(ex, &fo->body)) - return false; + goto for_fail; + for_exit(e); } break; + for_fail: + for_exit(e); + return false; } return true; } @@ -876,7 +863,7 @@ static void import_expr(Importer *im, Expression *e) { import_type(im, &e->typeval); break; case EXPR_FN: - e->fn = import_fn_ptr(im); + e->fn = import_fn(im); break; case EXPR_BLOCK: import_block(im, &e->block); @@ -958,8 +945,13 @@ static void export_ident_name(Exporter *ex, Identifier ident) { } static bool export_decl(Exporter *ex, Declaration *d) { + if (d->flags & DECL_MARKED_FOR_EXPORTING) { + arr_foreach(d->idents, Identifier, ident) { + export_ident_name(ex, *ident); + } + } + assert(ex->started); - /* printf("EXPORT %ld\n",ftell(ex->out)); */ possibly_static_assert(sizeof d->flags == 2); export_u16(ex, d->flags); @@ -967,11 +959,6 @@ static bool export_decl(Exporter *ex, Declaration *d) { err_print(d->where, "Can't export declaration of unknown type."); return false; } - if (d->flags & DECL_EXPORT) { - arr_foreach(d->idents, Identifier, ident) { - export_ident_name(ex, *ident); - } - } export_location(ex, d->where); export_len(ex, arr_len(d->idents)); @@ -1002,7 +989,6 @@ static bool export_decl(Exporter *ex, Declaration *d) { static void import_decl(Importer *im, Declaration *d) { possibly_static_assert(sizeof d->flags == 2); - /* printf("IMPORT %ld\n",ftell(im->in)); */ d->flags = import_u16(im); d->flags &= (DeclFlags)~(DeclFlags)DECL_EXPORT; d->where = import_location(im); @@ -1032,9 +1018,11 @@ static void import_decl(Importer *im, Declaration *d) { } /* exports a declaration. to be used by other files instead of export_decl. */ -static bool export_decl_external(Exporter *ex, Declaration *d) { - export_u8(ex, 1); /* indicate that there are more declarations */ - return export_decl(ex, d); +static void export_decl_external(Exporter *ex, Declaration *d) { + if (!(d->flags & DECL_MARKED_FOR_EXPORTING)) { + d->flags |= DECL_MARKED_FOR_EXPORTING; + *(Declaration **)arr_add(&ex->decls_to_export) = d; + } } static bool export_stmt(Exporter *ex, Statement *s) { @@ -1079,21 +1067,29 @@ static void import_stmt(Importer *im, Statement *s) { import_expr(im, &s->expr); break; } - fprint_stmt(stdout, s); printf("\n"); + /* fprint_stmt(stdout, s); printf("\n"); */ } static bool export_block(Exporter *ex, Block *b) { + if (!block_enter(b, b->stmts, 0)) + return false; + possibly_static_assert(sizeof b->flags == 1); export_u8(ex, b->flags); export_location(ex, b->where); export_len(ex, arr_len(b->stmts)); arr_foreach(b->stmts, Statement, s) { if (!export_stmt(ex, s)) - return false; + goto err; } if (!export_optional_expr(ex, b->ret_expr)) - return false; + goto err; + + block_exit(b, b->stmts); return true; + err: + block_exit(b, b->stmts); + return false; } static void import_block(Importer *im, Block *b) { @@ -1107,115 +1103,135 @@ static void import_block(Importer *im, Block *b) { } static bool export_fn(Exporter *ex, FnExpr *f) { - possibly_static_assert(sizeof f->flags == 1); - export_u8(ex, f->flags); - if (f->flags & FN_EXPR_FOREIGN) { - export_cstr(ex, f->foreign.name); - export_cstr(ex, f->foreign.lib); - } else { - export_location(ex, f->where); - export_len(ex, arr_len(f->params)); - arr_foreach(f->params, Declaration, param) { - if (!export_decl(ex, param)) - return false; - arr_foreach(param->idents, Identifier, ident) { - export_ident_name(ex, *ident); + export_bool(ex, f->export.id == 0); + if (f->export.id == 0) { + f->export.id = ++ex->nexported_fns; + export_vlq(ex, f->export.id); + if (!fn_enter(f, 0)) + return false; + + possibly_static_assert(sizeof f->flags == 1); + export_u8(ex, f->flags); + if (f->flags & FN_EXPR_FOREIGN) { + export_cstr(ex, f->foreign.name); + export_cstr(ex, f->foreign.lib); + } else { + export_location(ex, f->where); + export_len(ex, arr_len(f->params)); + arr_foreach(f->params, Declaration, param) { + if (!export_decl(ex, param)) + goto err; + arr_foreach(param->idents, Identifier, ident) { + export_ident_name(ex, *ident); + } } + if (!export_type(ex, &f->ret_type, f->where)) + goto err; + export_len(ex, arr_len(f->ret_decls)); + arr_foreach(f->ret_decls, Declaration, ret_decl) + if (!export_decl(ex, ret_decl)) + goto err; + if (!export_block(ex, &f->body)) + goto err; } - if (!export_type(ex, &f->ret_type, f->where)) - return false; - export_len(ex, arr_len(f->ret_decls)); - arr_foreach(f->ret_decls, Declaration, ret_decl) - if (!export_decl(ex, ret_decl)) - return false; - if (!export_block(ex, &f->body)) - return false; + fn_exit(f); + return true; + err: + fn_exit(f); + return false; + } else { + export_vlq(ex, f->export.id); + return true; } - return true; } -static void import_fn(Importer *im, FnExpr *f) { - memset(f, 0, sizeof *f); - f->flags = import_u8(im); - if (f->flags & FN_EXPR_FOREIGN) { - f->foreign.name = import_cstr(im); - f->foreign.lib = import_cstr(im); - } else { - f->where = import_location(im); - import_arr(im, &f->params); - arr_foreach(f->params, Declaration, param) { - import_decl(im, param); +static FnExpr *import_fn(Importer *im) { + FnExpr *f; + if (import_bool(im)) { + /* import function definition */ + f = &im->fns[import_vlq(im)]; + f->flags = import_u8(im); + if (f->flags & FN_EXPR_FOREIGN) { + f->foreign.name = import_cstr(im); + f->foreign.lib = import_cstr(im); + } else { + f->where = import_location(im); + import_arr(im, &f->params); + arr_foreach(f->params, Declaration, param) { + import_decl(im, param); + } + import_type(im, &f->ret_type); + import_arr(im, &f->ret_decls); + arr_foreach(f->ret_decls, Declaration, ret_decl) + import_decl(im, ret_decl); + import_block(im, &f->body); } - import_type(im, &f->ret_type); - import_arr(im, &f->ret_decls); - arr_foreach(f->ret_decls, Declaration, ret_decl) - import_decl(im, ret_decl); - import_block(im, &f->body); + } else { + f = &im->fns[import_vlq(im)]; } + return f; } static bool export_struct(Exporter *ex, StructDef *s) { - export_ident(ex, s->name); - if (s->name) - export_ident_name(ex, s->name); - export_len(ex, arr_len(s->fields)); - arr_foreach(s->fields, Field, f) { - export_ident(ex, f->name); - export_ident_name(ex, f->name); - if (!export_type(ex, &f->type, s->where)) - return false; + export_bool(ex, s->export.id == 0); + if (s->export.id == 0) { + s->export.id = ++ex->nexported_structs; + export_ident(ex, s->name); + if (s->name) + export_ident_name(ex, s->name); + export_len(ex, arr_len(s->fields)); + arr_foreach(s->fields, Field, f) { + export_ident(ex, f->name); + export_ident_name(ex, f->name); + if (!export_type(ex, &f->type, s->where)) + return false; + } + } else { + export_vlq(ex, s->export.id); } return true; } -static void import_struct(Importer *im, StructDef *s) { - s->name = import_ident(im); - size_t nfields = import_arr(im, &s->fields); - for (size_t i = 0; i < nfields; ++i) { - s->fields[i].name = import_ident(im); - import_type(im, &s->fields[i].type); +static StructDef *import_struct(Importer *im) { + StructDef *s; + if (import_bool(im)) { + s = &im->structs[import_vlq(im)]; + s->name = import_ident(im); + size_t nfields = import_arr(im, &s->fields); + for (size_t i = 0; i < nfields; ++i) { + s->fields[i].name = import_ident(im); + import_type(im, &s->fields[i].type); + } + } else { + s = &im->structs[import_vlq(im)]; } + return s; } /* does NOT close the file */ static bool exptr_finish(Exporter *ex) { - export_u8(ex, 0); /* no more declarations */ - - long footer_offset = ftell(ex->out); - - fseek(ex->out, 7L, SEEK_SET); - export_u64(ex, (U64)footer_offset); - fseek(ex->out, 0L, SEEK_END); - - /* position in file of where the position in file of identifier info is */ - long ident_info_offset_offset = ftell(ex->out); - export_u64(ex, 0); /* identifier info offset */ - long struct_info_offset_offset = ftell(ex->out); - export_u64(ex, 0); /* struct info offset */ - - - export_len(ex, arr_len(ex->exported_fns)); - typedef FnExpr *FnExprPtr; - arr_foreach(ex->exported_fns, FnExprPtr, f) { - if (!export_fn(ex, *f)) + long ndecls_offset = ftell(ex->out); + export_u32(ex, 0); + /* NOTE: arr_len(ex->decls_to_export) may change during loop! */ + for (size_t i = 0; i < arr_len(ex->decls_to_export); ++i) { + Declaration *d = ex->decls_to_export[i]; + if (!export_decl(ex, d)) return false; } - arr_clear(&ex->exported_fns); - - long struct_info_offset = ftell(ex->out); - export_len(ex, arr_len(ex->exported_structs)); - typedef StructDef *StructDefPtr; - arr_foreach(ex->exported_structs, StructDefPtr, s) { - if (!export_struct(ex, *s)) - return false; + { + long back = ftell(ex->out); + fseek(ex->out, ndecls_offset, SEEK_SET); + export_u32(ex, (U32)arr_len(ex->decls_to_export)); + fseek(ex->out, back, SEEK_SET); } - arr_clear(&ex->exported_structs); - - long ident_info_offset = ftell(ex->out); + + long footer_offset = ftell(ex->out); + export_vlq(ex, ex->nexported_fns); + export_vlq(ex, ex->nexported_structs); + /* export total number of identifiers */ + export_vlq(ex, ex->ident_id); /* export number of identifiers *whose names matter* */ - fseek(ex->out, ident_info_offset, SEEK_SET); - export_u64(ex, ex->ident_id); export_len(ex, arr_len(ex->exported_idents)); arr_foreach(ex->exported_idents, Identifier, ident) { Identifier i = *ident; @@ -1225,14 +1241,10 @@ static bool exptr_finish(Exporter *ex) { fprint_ident(ex->out, i); } - fseek(ex->out, ident_info_offset_offset, SEEK_SET); - export_u64(ex, (U64)ident_info_offset); - + fseek(ex->out, 7L, SEEK_SET); + export_u64(ex, (U64)footer_offset); arr_clear(&ex->exported_idents); - fseek(ex->out, struct_info_offset_offset, SEEK_SET); - export_u64(ex, (U64)struct_info_offset); - if (ferror(ex->out)) { Location none = {0}; none.file = &ex->exporting_to; @@ -1244,12 +1256,14 @@ static bool exptr_finish(Exporter *ex) { static bool import_footer(Importer *im) { size_t i; - long footer_offset = ftell(im->in); - U64 ident_info_offset = import_u64(im); - fseek(im->in, (long)ident_info_offset, SEEK_SET); - - im->max_ident_id = import_u64(im); + /* +1 because indexing starts at 1 */ + U32 nfns = (U32)import_vlq(im); + im->fns = imptr_calloc(im, nfns+1, sizeof *im->fns); + U32 nstructs = (U32)import_vlq(im); + im->structs = imptr_calloc(im, nstructs+1, sizeof *im->structs); + + im->max_ident_id = import_vlq(im); im->ident_map = err_calloc(im->max_ident_id + 1, sizeof *im->ident_map); size_t n_named_idents = import_len(im); @@ -1271,27 +1285,6 @@ static bool import_footer(Importer *im) { } } - fseek(im->in, footer_offset + 8, SEEK_SET); - U64 struct_offset = import_u64(im); - fseek(im->in, (long)struct_offset, SEEK_SET); - - - import_arr(im, &im->structs); -#ifdef TOC_DEBUG - /* for debugging: so that struct names show up as "anonymous struct" if they haven't been imported yet */ - arr_zero(im->structs); -#endif - arr_foreach(im->structs, StructDef, s) - import_struct(im, s); - - fseek(im->in, footer_offset + 16, SEEK_SET); - - import_arr(im, &im->fns); - arr_zero(im->fns); - arr_foreach(im->fns, FnExpr, f) { - import_fn(im, f); - } - if (ferror(im->in)) { warn_print(im->import_location, "An error occured while reading the package. It may be incorrect."); } @@ -11,9 +11,11 @@ if [ "$CC" = "" ]; then fi for thing in $std_things; do + echo "compiling $thing" $VALGRIND ../toc $thing.toc -o $thing.c || exit 1 done cd .. +echo "compiling test.toc" $VALGRIND ./toc test.toc || exit 1 $CC out.c std/*.c -Wno-builtin-declaration-mismatch || exit 1 ./a.out diff --git a/std/arr.toc b/std/arr.toc index af89ecb..0e81f3a 100644 --- a/std/arr.toc +++ b/std/arr.toc @@ -20,7 +20,7 @@ pkg "arr"; a.len += 1; }; -#export arr_forfor ::= fn(t ::=, a : Arr(t), f : fn(&t)) { +#export arr_foreach ::= fn(t ::=, a : Arr(t), f : fn(&t)) { for i := 0..a.len-1 { f(&a.data[i]); } @@ -24,9 +24,13 @@ toc_putchar ::= fn(x: char) { c_putchar(x as c_int); }; +c_fwrite :: fn(&u8, c_size_t, c_size_t, &u8) = #foreign "fwrite", "libc.so.6"; + +stdout_fwrite ::= fn(data: &u8, size: u64, nmemb: u64) { + c_fwrite(data, size as c_size_t, nmemb as c_size_t, #builtin("stdout")); +}; + #export puts ::= fn(x: []char) { - for c := x { - toc_putchar(c); - }; - toc_putchar('\n'); + stdout_fwrite(&x[0] as &u8, 1, x.len as u64); + toc_putchar('\n'); };
\ No newline at end of file @@ -3,7 +3,10 @@ hw ::= fn() int { io.puts("Hello, world!"); 3 }; +arr ::= pkg "std/arr"; main ::= fn() { -x::=hw(); -hw(); + hw(); + x::=hw(); + x : arr.Arr(int); + arr_add(&x, 10); };
\ No newline at end of file @@ -2425,11 +2425,11 @@ static bool types_decl(Typer *tr, Declaration *d) { if (success) { if (d->flags & DECL_EXPORT) { /* export it! */ - if (!tr->exptr) { + if (!tr->pkg_name) { err_print(d->where, "Declaration marked for exporting, but no package output was specified."); success = false; } else { - success = export_decl_external(tr->exptr, d); + export_decl_external(tr->exptr, d); } } } else { @@ -612,7 +612,8 @@ typedef struct ForExpr { enum { - FN_EXPR_FOREIGN = 0x01 + FN_EXPR_FOREIGN = 0x01, + FN_EXPR_HAS_BEEN_IMPORTED = 0x02 }; typedef struct FnExpr { @@ -788,7 +789,8 @@ enum { DECL_FOUND_VAL = 0x0040, DECL_INFER = 0x0080, /* infer the value (e.g. fn(t::Type=, x:t)) */ DECL_EXPORT = 0x0100, - DECL_FOREIGN = 0x0200 + DECL_FOREIGN = 0x0200, + DECL_MARKED_FOR_EXPORTING = 0x0400 }; typedef U16 DeclFlags; @@ -917,9 +919,10 @@ typedef struct Exporter { bool started; U64 ident_id; File exporting_to; - FnExpr **exported_fns; - StructDef **exported_structs; - Identifier *exported_idents; /* (only those whose names are exported) */ + U32 nexported_structs; + U32 nexported_fns; + Identifier *exported_idents; /* (only those whose names are exported) */ + Declaration **decls_to_export; /* declarations which need to be exported at some point */ const char *code; } Exporter; |