From 8ec0fc7462955fa481f16cc6a865516782702b11 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Mon, 23 Jan 2023 11:17:47 -0600 Subject: [PATCH] almost working implementation --- compiler/include/astnodes.h | 9 ++++- compiler/include/doc.h | 42 +++++++++++++++++++++++ compiler/src/doc.c | 68 +++++++++++++++++++++++++++++++++++++ compiler/src/onyx.c | 18 ++++++++++ compiler/src/symres.c | 12 ++++--- compiler/src/utils.c | 66 +++++++++++++++++++++++++++++++++-- 6 files changed, 208 insertions(+), 7 deletions(-) diff --git a/compiler/include/astnodes.h b/compiler/include/astnodes.h index f70ae309..9019269d 100644 --- a/compiler/include/astnodes.h +++ b/compiler/include/astnodes.h @@ -1601,7 +1601,8 @@ struct CompileOptions { b32 generate_foreign_info : 1; b32 no_std : 1; - b32 generate_tag_file : 1; + b32 generate_tag_file : 1; + b32 generate_symbol_info_file : 1; Runtime runtime; @@ -1609,6 +1610,7 @@ struct CompileOptions { bh_arr(const char *) files; const char* target_file; const char* documentation_file; + const char* symbol_info_file; b32 debug_enabled; @@ -1637,6 +1639,8 @@ struct Context { // present in this list when generating CTags. bh_arr(AstNode *) tag_locations; + struct SymbolInfoTable *symbol_info; + u32 cycle_almost_detected : 2; b32 cycle_detected : 1; }; @@ -1828,6 +1832,9 @@ b32 resolve_intrinsic_interface_constraint(AstConstraint *constraint); void track_declaration_for_tags(AstNode *); +void track_declaration_for_symbol_info(OnyxFilePos, AstNode *); +void track_resolution_for_symbol_info(AstNode *original, AstNode *resolved); + // NOTE: Useful inlined functions static inline b32 is_lval(AstNode* node) { node = strip_aliases(node); diff --git a/compiler/include/doc.h b/compiler/include/doc.h index b34b1031..3dd647a0 100644 --- a/compiler/include/doc.h +++ b/compiler/include/doc.h @@ -35,6 +35,48 @@ typedef struct OnyxDocumentation { OnyxDocumentation onyx_docs_generate(); void onyx_docs_emit(OnyxDocumentation* doc, const char* filename); + +// Tag generation + void onyx_docs_emit_tags(char *dest); + + +// Symbol info file generation + +typedef struct SymbolInfo SymbolInfo; +struct SymbolInfo { + u32 id; + u32 file_id; + u32 line; + u32 column; +}; + +typedef struct SymbolResolution SymbolResolution; +struct SymbolResolution { + u32 file_id; + u32 line; + u32 column; + u32 length; + u32 symbol_id; +}; + +typedef struct SymbolInfoTable SymbolInfoTable; +struct SymbolInfoTable { + // File name -> file id + Table(u32) files; + u32 next_file_id; + + // Symbol id -> symbol info + bh_arr(SymbolInfo) symbols; + u32 next_symbol_id; + + bh_arr(SymbolResolution) symbols_resolutions; + + // Node * -> symbol_id + bh_imap node_to_id; +}; + +void onyx_docs_emit_symbol_info(const char *dest); + #endif diff --git a/compiler/src/doc.c b/compiler/src/doc.c index fb716cb9..0cfc5305 100644 --- a/compiler/src/doc.c +++ b/compiler/src/doc.c @@ -51,6 +51,74 @@ void onyx_docs_emit_tags(char *dest) { bh_file_close(&tags_file); } +void onyx_docs_emit_symbol_info(const char *dest) { + bh_file sym_file; + if (bh_file_create(&sym_file, dest) != BH_FILE_ERROR_NONE) { + bh_printf("Cannot create '%s'.\n", dest); + return; + } + + SymbolInfoTable *syminfo = context.symbol_info; + + bh_buffer file_section; + bh_buffer_init(&file_section, global_heap_allocator, 2048); + fori (i, 0, shlen(syminfo->files)) { + char *filename = syminfo->files[i].key; + u32 file_id = syminfo->files[i].value; + bh_buffer_write_u32(&file_section, file_id); + bh_buffer_write_u32(&file_section, strlen(filename)); + bh_buffer_write_string(&file_section, filename); + } + + bh_buffer sym_def_section; + bh_buffer_init(&sym_def_section, global_heap_allocator, 2048); + bh_arr_each(SymbolInfo, sym, syminfo->symbols) { + bh_buffer_write_u32(&sym_def_section, sym->id); + bh_buffer_write_u32(&sym_def_section, sym->file_id); + bh_buffer_write_u32(&sym_def_section, sym->line); + bh_buffer_write_u32(&sym_def_section, sym->column); + } + + bh_buffer sym_res_section; + bh_buffer_init(&sym_res_section, global_heap_allocator, 2048); + bh_arr_each(SymbolResolution, sym, syminfo->symbols_resolutions) { + bh_buffer_write_u32(&sym_res_section, sym->file_id); + bh_buffer_write_u32(&sym_res_section, sym->line); + bh_buffer_write_u32(&sym_res_section, sym->column); + bh_buffer_write_u32(&sym_res_section, sym->length); + bh_buffer_write_u32(&sym_res_section, sym->symbol_id); + } + + bh_buffer header_section; + bh_buffer_init(&header_section, global_heap_allocator, 16); + bh_buffer_append(&header_section, "OSYM", 4); + bh_buffer_write_u32(&header_section, 1); + bh_buffer_write_u32(&header_section, 32); + bh_buffer_write_u32(&header_section, shlenu(syminfo->files)); + bh_buffer_write_u32(&header_section, 32 + file_section.length); + bh_buffer_write_u32(&header_section, bh_arr_length(syminfo->symbols)); + bh_buffer_write_u32(&header_section, 32 + file_section.length + sym_def_section.length); + bh_buffer_write_u32(&header_section, bh_arr_length(syminfo->symbols_resolutions)); + + bh_file_write(&sym_file, header_section.data, header_section.length); + bh_file_write(&sym_file, file_section.data, file_section.length); + bh_file_write(&sym_file, sym_def_section.data, sym_def_section.length); + bh_file_write(&sym_file, sym_res_section.data, sym_res_section.length); + + bh_file_close(&sym_file); + + bh_buffer_free(&header_section); + bh_buffer_free(&file_section); + bh_buffer_free(&sym_def_section); + bh_buffer_free(&sym_res_section); + + + bh_arr_free(syminfo->symbols); + bh_arr_free(syminfo->symbols_resolutions); + shfree(syminfo->files); + bh_imap_free(&syminfo->node_to_id); +} + // static i32 cmp_doc_entry(const void * a, const void * b) { // DocEntry* d1 = (DocEntry *) a; // DocEntry* d2 = (DocEntry *) b; diff --git a/compiler/src/onyx.c b/compiler/src/onyx.c index f7ff50d7..b58be803 100644 --- a/compiler/src/onyx.c +++ b/compiler/src/onyx.c @@ -51,6 +51,7 @@ static const char* docstring = "Onyx compiler version " VERSION "\n" "\t--wasm-mvp Use only WebAssembly MVP features.\n" "\t--multi-threaded Enables multi-threading for this compilation.\n" "\t--tag Generates a C-Tag file.\n" + "\t--syminfo Generates a symbol resolution information file. Used by onyx-lsp.\n" // "\t--doc \n" "\t--generate-foreign-info\n" "\n" @@ -90,6 +91,7 @@ static CompileOptions compile_opts_parse(bh_allocator alloc, int argc, char *arg .passthrough_argument_data = NULL, .generate_tag_file = 0, + .generate_symbol_info_file = 0, }; bh_arr_new(alloc, options.files, 2); @@ -189,6 +191,10 @@ static CompileOptions compile_opts_parse(bh_allocator alloc, int argc, char *arg else if (!strcmp(argv[i], "--tag")) { options.generate_tag_file = 1; } + else if (!strcmp(argv[i], "--syminfo")) { + options.generate_symbol_info_file = 1; + options.symbol_info_file = argv[++i]; + } else if (!strcmp(argv[i], "--debug")) { options.debug_enabled = 1; } @@ -336,6 +342,14 @@ static void context_init(CompileOptions* opts) { .include = create_load(context.ast_alloc, "core/std"), })); } + + if (context.options->generate_symbol_info_file) { + context.symbol_info = bh_alloc_item(global_heap_allocator, SymbolInfoTable); + bh_imap_init(&context.symbol_info->node_to_id, global_heap_allocator, 512); + bh_arr_new(global_heap_allocator, context.symbol_info->symbols, 128); + bh_arr_new(global_heap_allocator, context.symbol_info->symbols_resolutions, 128); + sh_new_arena(context.symbol_info->files); + } } static void context_free() { @@ -716,6 +730,10 @@ static i32 onyx_compile() { onyx_docs_emit_tags("./tags"); } + if (context.options->generate_symbol_info_file) { + onyx_docs_emit_symbol_info(context.options->symbol_info_file); + } + return ONYX_COMPILER_PROGRESS_SUCCESS; } diff --git a/compiler/src/symres.c b/compiler/src/symres.c index 38e84895..789d6e73 100644 --- a/compiler/src/symres.c +++ b/compiler/src/symres.c @@ -99,6 +99,7 @@ static SymresStatus symres_symbol(AstNode** symbol_node) { } } else { + track_resolution_for_symbol_info(*symbol_node, res); *symbol_node = res; resolved_a_symbol = 1; } @@ -334,8 +335,11 @@ static SymresStatus symres_field_access(AstFieldAccess** fa) { } AstNode* resolution = try_symbol_resolve_from_node((AstNode *) expr, (*fa)->token); - if (resolution) *((AstNode **) fa) = resolution; - else if (expr->kind == Ast_Kind_Package) { + if (resolution) { + track_resolution_for_symbol_info((AstNode *) *fa, resolution); + *((AstNode **) fa) = resolution; + + } else if (expr->kind == Ast_Kind_Package) { if (report_unresolved_symbols) { token_toggle_end((*fa)->token); char *closest = find_closest_symbol_in_node((AstNode *) expr, (*fa)->token->text); @@ -360,8 +364,8 @@ static SymresStatus symres_field_access(AstFieldAccess** fa) { } else { return Symres_Yield_Macro; } - } - else if (force_a_lookup) { + + } else if (force_a_lookup) { if (context.cycle_detected || context.cycle_almost_detected >= 2) { onyx_report_error((*fa)->token->pos, Error_Critical, "'%b' does not exist here. This is a bad error message.", (*fa)->token->text, diff --git a/compiler/src/utils.c b/compiler/src/utils.c index 608a63f2..5be11d0c 100644 --- a/compiler/src/utils.c +++ b/compiler/src/utils.c @@ -7,6 +7,7 @@ #include "parser.h" #include "astnodes.h" #include "errors.h" +#include "doc.h" bh_scratch global_scratch; bh_allocator global_scratch_allocator; @@ -144,6 +145,7 @@ b32 symbol_raw_introduce(Scope* scope, char* name, OnyxFilePos pos, AstNode* sym } shput(scope->symbols, name, symbol); + track_declaration_for_symbol_info(pos, symbol); return 1; } @@ -1077,7 +1079,9 @@ type_checking_done: if (arg_pos < func_type->needed_param_count) { if (error != NULL) { error->pos = location->pos; - error->text = "Too few arguments to function call."; + error->text = bh_aprintf(global_heap_allocator, + "Too few arguments to function call. Expected at least %d argument%s, but only got %d.", + func_type->needed_param_count, bh_num_plural(func_type->needed_param_count), arg_pos); } return TYPE_MATCH_FAILED; } @@ -1085,7 +1089,9 @@ type_checking_done: if (arg_pos < (u32) arg_count) { if (error != NULL) { error->pos = location->pos; - error->text = bh_aprintf(global_heap_allocator, "Too many arguments to function call. %d %d", arg_pos, arg_count); + error->text = bh_aprintf(global_heap_allocator, + "Too many arguments to function call. Expected at most %d argument%s, but got %d.", + arg_pos, bh_num_plural(arg_pos), arg_count); } return TYPE_MATCH_FAILED; } @@ -1324,3 +1330,59 @@ void track_declaration_for_tags(AstNode *node) { bh_arr_push(context.tag_locations, node); } } + + +static u32 symbol_info_get_file_id(SymbolInfoTable *syminfo, const char *filename) { + u32 file_id; + if (shgeti(syminfo->files, filename) == -1) { + file_id = syminfo->next_file_id++; + shput(syminfo->files, filename, file_id); + + } else { + file_id = shget(syminfo->files, filename); + } + + return file_id; +} + +void track_declaration_for_symbol_info(OnyxFilePos pos, AstNode *node) { + if (!context.options->generate_symbol_info_file) return; + if (pos.filename == NULL) return; + + SymbolInfoTable *syminfo = context.symbol_info; + assert(syminfo); + + u32 symbol_id = syminfo->next_symbol_id++; + u32 file_id = symbol_info_get_file_id(syminfo, pos.filename); + + SymbolInfo symbol; + symbol.id = symbol_id; + symbol.file_id = file_id; + symbol.line = pos.line; + symbol.column = pos.column; + bh_arr_push(syminfo->symbols, symbol); + + bh_imap_put(&syminfo->node_to_id, (u64) node, (u64) symbol_id); +} + +void track_resolution_for_symbol_info(AstNode *original, AstNode *resolved) { + if (!context.options->generate_symbol_info_file) return; + + SymbolInfoTable *syminfo = context.symbol_info; + assert(syminfo); + + if (!bh_imap_has(&syminfo->node_to_id, (u64) resolved)) return; + + u32 symbol_id = (u32) bh_imap_get(&syminfo->node_to_id, (u64) resolved); + + u32 file_id = symbol_info_get_file_id(syminfo, original->token->pos.filename); + + SymbolResolution res; + res.symbol_id = symbol_id; + res.file_id = file_id; + res.line = original->token->pos.line; + res.column = original->token->pos.column; + res.length = original->token->length; + + bh_arr_push(syminfo->symbols_resolutions, res); +} -- 2.25.1