almost working implementation
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 23 Jan 2023 17:17:47 +0000 (11:17 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 23 Jan 2023 17:17:47 +0000 (11:17 -0600)
compiler/include/astnodes.h
compiler/include/doc.h
compiler/src/doc.c
compiler/src/onyx.c
compiler/src/symres.c
compiler/src/utils.c

index f70ae309d2426a174feb7435a505a5d7d0666192..9019269d1890992896b4a7fe279fc5f6e2a8bf64 100644 (file)
@@ -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);
index b34b10312ed1ac82728d1532a2e861cf4047df99..3dd647a0bfbbe2b0edfa87537f1c1c722f78f15e 100644 (file)
@@ -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
index fb716cb9ce3fdbce27d18c9cf78851a6443f0276..0cfc5305861009f46686372eee613c0608b115fd 100644 (file)
@@ -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;
index f7ff50d753d6eddd0b4e9099ded885cc5d8b545e..b58be8030366c2b34f465bf8aad5bf98713976a3 100644 (file)
@@ -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 <target_file> Generates a symbol resolution information file. Used by onyx-lsp.\n"
     // "\t--doc <doc_file>\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;
 }
 
index 38e848953fe276558353ec41d8e1001282a5ad26..789d6e73a38abfa06ce53bb04619161929dd7a41 100644 (file)
@@ -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,
index 608a63f2d614d046fec36e352c660a5bdfd16bfa..5be11d0cf936b560160973391859f2e2cd2624a5 100644 (file)
@@ -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);
+}