diff --git a/src/build_settings.cpp b/src/build_settings.cpp index d3e892175..fc6702d36 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -105,6 +105,7 @@ struct BuildContext { bool ignore_unknown_attributes; bool no_bounds_check; bool no_output_files; + bool print_query_data; bool no_crt; bool use_lld; bool vet; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index e3a1b2575..d97601444 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -905,6 +905,9 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d) continue; } + begin_error_block(); + defer (end_error_block()); + ProcTypeOverloadKind kind = are_proc_types_overload_safe(p->type, q->type); switch (kind) { case ProcOverload_Identical: @@ -938,7 +941,7 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d) } if (is_invalid) { - gb_printf_err("\tprevious procedure at %.*s(%td:%td)\n", LIT(pos.file), pos.line, pos.column); + error_line("\tprevious procedure at %.*s(%td:%td)\n", LIT(pos.file), pos.line, pos.column); q->type = t_invalid; } } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 5542e9e18..3acc369a0 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2658,47 +2658,53 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) { target_type = t->Union.variants[first_success_index]; break; } else if (valid_count > 1) { + begin_error_block(); + defer (end_error_block()); + GB_ASSERT(first_success_index >= 0); operand->mode = Addressing_Invalid; convert_untyped_error(c, operand, target_type); - gb_printf_err("Ambiguous type conversion to '%s', which variant did you mean:\n\t", type_str); + error_line("Ambiguous type conversion to '%s', which variant did you mean:\n\t", type_str); i32 j = 0; for (i32 i = 0; i < valid_count; i++) { ValidIndexAndScore valid = valids[i]; - if (j > 0 && valid_count > 2) gb_printf_err(", "); + if (j > 0 && valid_count > 2) error_line(", "); if (j == valid_count-1) { - if (valid_count == 2) gb_printf_err(" "); - gb_printf_err("or "); + if (valid_count == 2) error_line(" "); + error_line("or "); } gbString str = type_to_string(t->Union.variants[valid.index]); - gb_printf_err("'%s'", str); + error_line("'%s'", str); gb_string_free(str); j++; } - gb_printf_err("\n\n"); + error_line("\n\n"); return; } else if (is_type_untyped_undef(operand->type) && type_has_undef(target_type)) { target_type = t_untyped_undef; } else if (!is_type_untyped_nil(operand->type) || !type_has_nil(target_type)) { + begin_error_block(); + defer (end_error_block()); + operand->mode = Addressing_Invalid; convert_untyped_error(c, operand, target_type); if (count > 0) { - gb_printf_err("'%s' is a union which only excepts the following types:\n", type_str); - gb_printf_err("\t"); + error_line("'%s' is a union which only excepts the following types:\n", type_str); + error_line("\t"); for (i32 i = 0; i < count; i++) { Type *v = t->Union.variants[i]; - if (i > 0 && count > 2) gb_printf_err(", "); + if (i > 0 && count > 2) error_line(", "); if (i == count-1) { - if (count == 2) gb_printf_err(" "); - gb_printf_err("or "); + if (count == 2) error_line(" "); + error_line("or "); } gbString str = type_to_string(v); - gb_printf_err("'%s'", str); + error_line("'%s'", str); gb_string_free(str); } - gb_printf_err("\n\n"); + error_line("\n\n"); } return; @@ -5099,19 +5105,22 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type } } if (!all_invalid_type) { + begin_error_block(); + defer (end_error_block()); + error(operand->expr, "No procedures or ambiguous call for procedure group '%s' that match with the given arguments", expr_name); - gb_printf_err("\tGiven argument types: ("); + error_line("\tGiven argument types: ("); for_array(i, operands) { Operand o = operands[i]; - if (i > 0) gb_printf_err(", "); + if (i > 0) error_line(", "); gbString type = type_to_string(o.type); defer (gb_string_free(type)); - gb_printf_err("%s", type); + error_line("%s", type); } - gb_printf_err(")\n"); + error_line(")\n"); if (procs.count > 0) { - gb_printf_err("Did you mean to use one of the following:\n"); + error_line("Did you mean to use one of the following:\n"); } for_array(i, procs) { Entity *proc = procs[i]; @@ -5138,25 +5147,28 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type if (proc->kind == Entity_Variable) { sep = ":="; } - // gb_printf_err("\t%.*s %s %s at %.*s(%td:%td) with score %lld\n", LIT(name), sep, pt, LIT(pos.file), pos.line, pos.column, cast(long long)valids[i].score); - gb_printf_err("\t%.*s%.*s%.*s %s %s at %.*s(%td:%td)\n", LIT(prefix), LIT(prefix_sep), LIT(name), sep, pt, LIT(pos.file), pos.line, pos.column); + // error_line("\t%.*s %s %s at %.*s(%td:%td) with score %lld\n", LIT(name), sep, pt, LIT(pos.file), pos.line, pos.column, cast(long long)valids[i].score); + error_line("\t%.*s%.*s%.*s %s %s at %.*s(%td:%td)\n", LIT(prefix), LIT(prefix_sep), LIT(name), sep, pt, LIT(pos.file), pos.line, pos.column); } if (procs.count > 0) { - gb_printf_err("\n"); + error_line("\n"); } } result_type = t_invalid; } else if (valid_count > 1) { + begin_error_block(); + defer (end_error_block()); + error(operand->expr, "Ambiguous procedure group call '%s' that match with the given arguments", expr_name); - gb_printf_err("\tGiven argument types: ("); + error_line("\tGiven argument types: ("); for_array(i, operands) { Operand o = operands[i]; - if (i > 0) gb_printf_err(", "); + if (i > 0) error_line(", "); gbString type = type_to_string(o.type); defer (gb_string_free(type)); - gb_printf_err("%s", type); + error_line("%s", type); } - gb_printf_err(")\n"); + error_line(")\n"); for (isize i = 0; i < valid_count; i++) { Entity *proc = procs[valids[i].index]; @@ -5174,8 +5186,8 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type if (proc->kind == Entity_Variable) { sep = ":="; } - gb_printf_err("\t%.*s %s %s at %.*s(%td:%td)\n", LIT(name), sep, pt, LIT(pos.file), pos.line, pos.column); - // gb_printf_err("\t%.*s %s %s at %.*s(%td:%td) %lld\n", LIT(name), sep, pt, LIT(pos.file), pos.line, pos.column, valids[i].score); + error_line("\t%.*s %s %s at %.*s(%td:%td)\n", LIT(name), sep, pt, LIT(pos.file), pos.line, pos.column); + // error_line("\t%.*s %s %s at %.*s(%td:%td) %lld\n", LIT(name), sep, pt, LIT(pos.file), pos.line, pos.column, valids[i].score); } result_type = t_invalid; } else { diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 82fc3bc7b..88f1574e8 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -809,6 +809,9 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { } if (unhandled.count > 0) { + begin_error_block(); + defer (begin_error_block()); + if (unhandled.count == 1) { error_no_newline(node, "Unhandled switch case: "); } else { @@ -817,11 +820,11 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { for_array(i, unhandled) { Entity *f = unhandled[i]; if (i > 0) { - gb_printf_err(", "); + error_line(", "); } - gb_printf_err("%.*s", LIT(f->token.string)); + error_line("%.*s", LIT(f->token.string)); } - gb_printf_err("\n"); + error_line("\n"); } } } @@ -1040,13 +1043,13 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { for_array(i, unhandled) { Type *t = unhandled[i]; if (i > 0) { - gb_printf_err(", "); + error_line(", "); } gbString s = type_to_string(t); - gb_printf_err("%s", s); + error_line("%s", s); gb_string_free(s); } - gb_printf_err("\n"); + error_line("\n"); } } } diff --git a/src/checker.cpp b/src/checker.cpp index 693ed98c5..5c18e09e9 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -826,13 +826,14 @@ void destroy_checker_context(CheckerContext *ctx) { destroy_checker_poly_path(ctx->poly_path); } -void init_checker(Checker *c, Parser *parser) { +bool init_checker(Checker *c, Parser *parser) { + c->parser = parser; + if (global_error_collector.count > 0) { - gb_exit(1); + return false; } gbAllocator a = heap_allocator(); - c->parser = parser; init_checker_info(&c->info); array_init(&c->procs_to_check, a); @@ -846,6 +847,7 @@ void init_checker(Checker *c, Parser *parser) { c->allocator = heap_allocator(); c->init_ctx = make_checker_context(c); + return true; } void destroy_checker(Checker *c) { @@ -2189,7 +2191,11 @@ DECL_ATTRIBUTE_PROC(var_decl_attribute) { model == "localexec") { ac->thread_local_model = model; } else { - error(elem, "Invalid thread local model '%.*s'", LIT(model)); + error(elem, "Invalid thread local model '%.*s'. Valid models:", LIT(model)); + error_line("\tdefault\n"); + error_line("\tlocaldynamic\n"); + error_line("\tinitialexec\n"); + error_line("\tlocalexec\n"); } } else { error(elem, "Expected either no value or a string for '%.*s'", LIT(name)); @@ -2597,10 +2603,13 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { if (e->kind != Entity_Procedure) { if (fl != nullptr) { + begin_error_block(); + defer (end_error_block()); + AstKind kind = init->kind; error(name, "Only procedures and variables are allowed to be in a foreign block, got %.*s", LIT(ast_strings[kind])); if (kind == Ast_ProcType) { - gb_printf_err("\tDid you forget to append '---' to the procedure?\n"); + error_line("\tDid you forget to append '---' to the procedure?\n"); } } } diff --git a/src/common.cpp b/src/common.cpp index ed1fd16e2..8085e895c 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -73,16 +73,18 @@ GB_ALLOCATOR_PROC(heap_allocator_proc) { ptr = _aligned_realloc(old_memory, size, alignment); break; #else - case gbAllocation_Alloc: + case gbAllocation_Alloc: { + isize aligned_size = align_formula_isize(size, alignment); // TODO(bill): Make sure this is aligned correctly - ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, align_formula_isize(size, alignment)); - break; + ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, aligned_size); + } break; case gbAllocation_Free: HeapFree(GetProcessHeap(), 0, old_memory); break; - case gbAllocation_Resize: - ptr = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, old_memory, align_formula_isize(size, alignment)); - break; + case gbAllocation_Resize: { + isize aligned_size = align_formula_isize(size, alignment); + ptr = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, old_memory, aligned_size); + } break; #endif #elif defined(GB_SYSTEM_LINUX) diff --git a/src/main.cpp b/src/main.cpp index 8bfc68bad..49284b9d6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,6 +16,7 @@ #include "ir.cpp" #include "ir_opt.cpp" #include "ir_print.cpp" +#include "query_data.cpp" // NOTE(bill): 'name' is used in debugging and profiling modes i32 system_exec_command_line_app(char *name, char *fmt, ...) { @@ -162,6 +163,7 @@ void usage(String argv0) { print_usage_line(1, "build compile .odin file as executable"); print_usage_line(1, "run compile and run .odin file"); print_usage_line(1, "check parse and type check .odin file"); + print_usage_line(1, "query parse, type check, and output a .json file containing information about the program"); print_usage_line(1, "docs generate documentation for a .odin file"); print_usage_line(1, "version print version"); } @@ -813,6 +815,437 @@ void remove_temp_files(String output_base) { #undef EXT_REMOVE } + + +int query_data_package_compare(void const *a, void const *b) { + AstPackage *x = *cast(AstPackage *const *)a; + AstPackage *y = *cast(AstPackage *const *)b; + + if (x == y) { + return 0; + } + + if (x != nullptr && y != nullptr) { + return string_compare(x->name, y->name); + } else if (x != nullptr && y == nullptr) { + return -1; + } else if (x == nullptr && y != nullptr) { + return +1; + } + return 0; +} + +int query_data_definition_compare(void const *a, void const *b) { + Entity *x = *cast(Entity *const *)a; + Entity *y = *cast(Entity *const *)b; + + if (x == y) { + return 0; + } else if (x != nullptr && y == nullptr) { + return -1; + } else if (x == nullptr && y != nullptr) { + return +1; + } + + if (x->pkg != y->pkg) { + i32 res = query_data_package_compare(&x->pkg, &y->pkg); + if (res != 0) { + return res; + } + } + + return string_compare(x->token.string, y->token.string); +} + +int entity_name_compare(void const *a, void const *b) { + Entity *x = *cast(Entity *const *)a; + Entity *y = *cast(Entity *const *)b; + if (x == y) { + return 0; + } else if (x != nullptr && y == nullptr) { + return -1; + } else if (x == nullptr && y != nullptr) { + return +1; + } + return string_compare(x->token.string, y->token.string); +} + +void generate_and_print_query_data(Checker *c, Timings *timings) { + query_value_allocator = heap_allocator(); + + + auto *root = query_value_map(); + + if (global_error_collector.errors.count > 0) { + auto *errors = query_value_array(); + root->add("errors", errors); + for_array(i, global_error_collector.errors) { + String err = string_trim_whitespace(global_error_collector.errors[i]); + errors->add(err); + } + + } + + { // Packages + auto *packages = query_value_array(); + root->add("packages", packages); + + auto sorted_packages = array_make(query_value_allocator, 0, c->info.packages.entries.count); + defer (array_free(&sorted_packages)); + + for_array(i, c->info.packages.entries) { + AstPackage *pkg = c->info.packages.entries[i].value; + if (pkg != nullptr) { + array_add(&sorted_packages, pkg); + } + } + gb_sort_array(sorted_packages.data, sorted_packages.count, query_data_package_compare); + packages->reserve(sorted_packages.count); + + for_array(i, sorted_packages) { + AstPackage *pkg = sorted_packages[i]; + String name = pkg->name; + String fullpath = pkg->fullpath; + + auto *files = query_value_array(); + files->reserve(pkg->files.count); + for_array(j, pkg->files) { + AstFile *f = pkg->files[j]; + files->add(f->fullpath); + } + + auto *package = query_value_map(); + package->reserve(3); + packages->add(package); + + package->add("name", pkg->name); + package->add("fullpath", pkg->fullpath); + package->add("files", files); + } + } + + if (c->info.definitions.count > 0) { + auto *definitions = query_value_array(); + root->add("definitions", definitions); + + auto sorted_definitions = array_make(query_value_allocator, 0, c->info.definitions.count); + defer (array_free(&sorted_definitions)); + + for_array(i, c->info.definitions) { + Entity *e = c->info.definitions[i]; + String name = e->token.string; + if (is_blank_ident(name)) { + continue; + } + if ((e->scope->flags & (ScopeFlag_Pkg|ScopeFlag_File)) == 0) { + continue; + } + if (e->parent_proc_decl != nullptr) { + continue; + } + switch (e->kind) { + case Entity_Builtin: + case Entity_Nil: + case Entity_Label: + continue; + } + if (e->pkg == nullptr) { + continue; + } + if (e->token.pos.line == 0) { + continue; + } + if (e->kind == Entity_Procedure) { + Type *t = base_type(e->type); + if (t->kind != Type_Proc) { + continue; + } + if (t->Proc.is_poly_specialized) { + continue; + } + } + if (e->kind == Entity_TypeName) { + Type *t = base_type(e->type); + if (t->kind == Type_Struct) { + if (t->Struct.is_poly_specialized) { + continue; + } + } + if (t->kind == Type_Union) { + if (t->Union.is_poly_specialized) { + continue; + } + } + } + + array_add(&sorted_definitions, e); + } + + gb_sort_array(sorted_definitions.data, sorted_definitions.count, query_data_definition_compare); + definitions->reserve(sorted_definitions.count); + + for_array(i, sorted_definitions) { + Entity *e = sorted_definitions[i]; + String name = e->token.string; + + auto *def = query_value_map(); + def->reserve(16); + definitions->add(def); + + def->add("package", e->pkg->name); + def->add("name", name); + def->add("filepath", e->token.pos.file); + def->add("line", e->token.pos.line); + def->add("column", e->token.pos.column); + def->add("file_offset", e->token.pos.offset); + + switch (e->kind) { + case Entity_Constant: def->add("kind", str_lit("constant")); break; + case Entity_Variable: def->add("kind", str_lit("variable")); break; + case Entity_TypeName: def->add("kind", str_lit("type name")); break; + case Entity_Procedure: def->add("kind", str_lit("procedure")); break; + case Entity_ProcGroup: def->add("kind", str_lit("procedure group")); break; + case Entity_ImportName: def->add("kind", str_lit("import name")); break; + case Entity_LibraryName: def->add("kind", str_lit("library name")); break; + default: GB_PANIC("Invalid entity kind to be added"); + } + + + if (e->type != nullptr && e->type != t_invalid) { + Type *t = e->type; + Type *bt = t; + + switch (e->kind) { + case Entity_TypeName: + if (!e->TypeName.is_type_alias) { + bt = base_type(t); + } + break; + } + + { + gbString str = type_to_string(t); + String type_str = make_string(cast(u8 *)str, gb_string_length(str)); + def->add("type", type_str); + } + if (t != bt) { + gbString str = type_to_string(bt); + String type_str = make_string(cast(u8 *)str, gb_string_length(str)); + def->add("base_type", type_str); + } + { + String type_kind = {}; + Type *bt = base_type(t); + switch (bt->kind) { + case Type_Pointer: type_kind = str_lit("pointer"); break; + case Type_Opaque: type_kind = str_lit("opaque"); break; + case Type_Array: type_kind = str_lit("array"); break; + case Type_Slice: type_kind = str_lit("slice"); break; + case Type_DynamicArray: type_kind = str_lit("dynamic array"); break; + case Type_Map: type_kind = str_lit("map"); break; + case Type_Struct: type_kind = str_lit("struct"); break; + case Type_Union: type_kind = str_lit("union"); break; + case Type_Enum: type_kind = str_lit("enum"); break; + case Type_Proc: type_kind = str_lit("procedure"); break; + case Type_BitField: type_kind = str_lit("bit field"); break; + case Type_BitSet: type_kind = str_lit("bit set"); break; + case Type_SimdVector: type_kind = str_lit("simd vector"); break; + + case Type_Generic: + case Type_Tuple: + case Type_BitFieldValue: + GB_PANIC("Invalid definition type"); + break; + } + if (type_kind.len > 0) { + def->add("type_kind", type_kind); + } + } + } + + if (e->kind == Entity_TypeName) { + def->add("size", type_size_of(e->type)); + def->add("align", type_align_of(e->type)); + + + if (is_type_struct(e->type)) { + auto *data = query_value_map(); + data->reserve(6); + + def->add("data", data); + + Type *t = base_type(e->type); + GB_ASSERT(t->kind == Type_Struct); + + if (t->Struct.is_polymorphic) { + data->add("polymorphic", t->Struct.is_polymorphic); + } + if (t->Struct.is_poly_specialized) { + data->add("polymorphic_specialized", t->Struct.is_poly_specialized); + } + if (t->Struct.is_packed) { + data->add("packed", t->Struct.is_packed); + } + if (t->Struct.is_raw_union) { + data->add("raw_union", t->Struct.is_raw_union); + } + + auto *fields = query_value_array(); + data->add("fields", fields); + fields->reserve(t->Struct.fields.count); + fields->packed = true; + + for_array(j, t->Struct.fields) { + Entity *e = t->Struct.fields[j]; + String name = e->token.string; + if (is_blank_ident(name)) { + continue; + } + + fields->add(name); + } + } else if (is_type_union(e->type)) { + auto *data = query_value_map(); + data->reserve(4); + + def->add("data", data); + Type *t = base_type(e->type); + GB_ASSERT(t->kind == Type_Union); + + if (t->Union.is_polymorphic) { + data->add("polymorphic", t->Union.is_polymorphic); + } + if (t->Union.is_poly_specialized) { + data->add("polymorphic_specialized", t->Union.is_poly_specialized); + } + + auto *variants = query_value_array(); + variants->reserve(t->Union.variants.count); + data->add("variants", variants); + + for_array(j, t->Union.variants) { + Type *vt = t->Union.variants[j]; + + gbString str = type_to_string(vt); + String type_str = make_string(cast(u8 *)str, gb_string_length(str)); + variants->add(type_str); + } + } + } + + if (e->kind == Entity_Procedure) { + Type *t = base_type(e->type); + GB_ASSERT(t->kind == Type_Proc); + + bool is_polymorphic = t->Proc.is_polymorphic; + bool is_poly_specialized = t->Proc.is_poly_specialized; + bool ok = is_polymorphic || is_poly_specialized; + if (ok) { + auto *data = query_value_map(); + data->reserve(4); + + def->add("data", data); + if (is_polymorphic) { + data->add("polymorphic", is_polymorphic); + } + if (is_poly_specialized) { + data->add("polymorphic_specialized", is_poly_specialized); + } + } + } + + if (e->kind == Entity_ProcGroup) { + auto *procedures = query_value_array(); + procedures->reserve(e->ProcGroup.entities.count); + + for_array(j, e->ProcGroup.entities) { + Entity *p = e->ProcGroup.entities[j]; + + auto *procedure = query_value_map(); + procedure->reserve(2); + procedure->packed = true; + + procedures->add(procedure); + + procedure->add("package", p->pkg->name); + procedure->add("name", p->token.string); + } + def->add("procedures", procedures); + } + + DeclInfo *di = e->decl_info; + if (di != nullptr) { + if (di->is_using) { + def->add("using", query_value_boolean(true)); + } + } + } + } + + if (build_context.show_timings) { + Timings *t = timings; + timings__stop_current_section(t); + t->total.finish = time_stamp_time_now(); + isize max_len = gb_min(36, t->total.label.len); + for_array(i, t->sections) { + TimeStamp ts = t->sections[i]; + max_len = gb_max(max_len, ts.label.len); + } + t->total_time_seconds = time_stamp_as_s(t->total, t->freq); + + auto *tims = query_value_map(); + tims->reserve(8); + root->add("timings", tims); + tims->add("time_unit", str_lit("s")); + + tims->add(t->total.label, cast(f64)t->total_time_seconds); + + + Parser *p = c->parser; + if (p != nullptr) { + isize lines = p->total_line_count; + isize tokens = p->total_token_count; + isize files = 0; + isize packages = p->packages.count; + isize total_file_size = 0; + for_array(i, p->packages) { + files += p->packages[i]->files.count; + for_array(j, p->packages[i]->files) { + AstFile *file = p->packages[i]->files[j]; + total_file_size += file->tokenizer.end - file->tokenizer.start; + } + } + + tims->add("total_lines", lines); + tims->add("total_tokens", tokens); + tims->add("total_files", files); + tims->add("total_packages", packages); + tims->add("total_file_size", total_file_size); + + auto *sections = query_value_map(); + sections->reserve(t->sections.count); + tims->add("sections", sections); + for_array(i, t->sections) { + TimeStamp ts = t->sections[i]; + f64 section_time = time_stamp_as_s(ts, t->freq); + + auto *section = query_value_map(); + section->reserve(2); + sections->add(ts.label, section); + section->add("time", section_time); + section->add("total_fraction", section_time/t->total_time_seconds); + } + } + } + + + print_query_data_as_json(root, true); + gb_printf("\n"); +} + + + + i32 exec_llvm_opt(String output_base) { #if defined(GB_SYSTEM_WINDOWS) // For more passes arguments: http://llvm.org/docs/Passes.html @@ -928,6 +1361,14 @@ int main(int arg_count, char **arg_ptr) { } build_context.no_output_files = true; init_filename = args[2]; + } else if (command == "query") { + if (args.count < 3) { + usage(args[0]); + return 1; + } + build_context.no_output_files = true; + build_context.print_query_data = true; + init_filename = args[2]; } else if (command == "docs") { if (args.count < 3) { usage(args[0]); @@ -988,21 +1429,27 @@ int main(int arg_count, char **arg_ptr) { // generate_documentation(&parser); return 0; } - - timings_start_section(&timings, str_lit("type check")); Checker checker = {0}; - init_checker(&checker, &parser); - defer (destroy_checker(&checker)); + bool checked_inited = init_checker(&checker, &parser); + defer (if (checked_inited) { + destroy_checker(&checker); + }); + + if (checked_inited) { + check_parsed_files(&checker); + } - check_parsed_files(&checker); -#if 1 if (build_context.no_output_files) { - if (build_context.show_timings) { - show_timings(&checker, &timings); + if (build_context.print_query_data) { + generate_and_print_query_data(&checker, &timings); + } else { + if (build_context.show_timings) { + show_timings(&checker, &timings); + } } if (global_error_collector.count != 0) { @@ -1012,6 +1459,10 @@ int main(int arg_count, char **arg_ptr) { return 0; } + if (!checked_inited) { + return 1; + } + irGen ir_gen = {0}; if (!ir_gen_init(&ir_gen, &checker)) { return 1; @@ -1296,6 +1747,6 @@ int main(int arg_count, char **arg_ptr) { system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(complete_path), LIT(run_args_string)); } #endif -#endif + return 0; } diff --git a/src/query_data.cpp b/src/query_data.cpp new file mode 100644 index 000000000..5b97512ee --- /dev/null +++ b/src/query_data.cpp @@ -0,0 +1,361 @@ +struct QueryValue; +struct QueryValuePair; + +gbAllocator query_value_allocator = {}; + +enum QueryKind { + Query_Invalid, + Query_String, + Query_Boolean, + Query_Integer, + Query_Float, + Query_Array, + Query_Map, +}; + +struct QueryValuePair { + String key; + QueryValue *value; +}; + + +struct QueryValue { + QueryKind kind; + bool packed; +}; + +struct QueryValueString : QueryValue { + QueryValueString(String const &v) { + kind = Query_String; + value = v; + packed = false; + } + String value; +}; + +struct QueryValueBoolean : QueryValue { + QueryValueBoolean(bool v) { + kind = Query_Boolean; + value = v; + packed = false; + } + bool value; +}; + +struct QueryValueInteger : QueryValue { + QueryValueInteger(i64 v) { + kind = Query_Integer; + value = v; + packed = false; + } + i64 value; +}; + +struct QueryValueFloat : QueryValue { + QueryValueFloat(f64 v) { + kind = Query_Float; + value = v; + packed = false; + } + f64 value; +}; + +struct QueryValueArray : QueryValue { + QueryValueArray() { + kind = Query_Array; + array_init(&value, query_value_allocator); + packed = false; + } + QueryValueArray(Array const &v) { + kind = Query_Array; + value = v; + packed = false; + } + Array value; + + void reserve(isize cap) { + array_reserve(&value, cap); + } + void add(QueryValue *v) { + array_add(&value, v); + } + void add(char const *v) { + add(make_string_c(cast(char *)v)); + } + void add(String const &v) { + auto val = gb_alloc_item(query_value_allocator, QueryValueString); + *val = QueryValueString(v); + add(val); + } + void add(bool v) { + auto val = gb_alloc_item(query_value_allocator, QueryValueBoolean); + *val = QueryValueBoolean(v); + add(val); + } + void add(i64 v) { + auto val = gb_alloc_item(query_value_allocator, QueryValueInteger); + *val = QueryValueInteger(v); + add(val); + } + void add(f64 v) { + auto val = gb_alloc_item(query_value_allocator, QueryValueFloat); + *val = QueryValueFloat(v); + add(val); + } +}; + +struct QueryValueMap : QueryValue { + QueryValueMap() { + kind = Query_Map; + array_init(&value, query_value_allocator); + packed = false; + } + QueryValueMap(Array const &v) { + kind = Query_Map; + value = v; + packed = false; + } + Array value; + + + void reserve(isize cap) { + array_reserve(&value, cap); + } + void add(char const *k, QueryValue *v) { + add(make_string_c(cast(char *)k), v); + } + void add(String const &k, QueryValue *v) { + QueryValuePair kv = {k, v}; + array_add(&value, kv); + } + + void add(char const *k, String const &v) { + auto val = gb_alloc_item(query_value_allocator, QueryValueString); + *val = QueryValueString(v); + add(k, val); + } + void add(char const *k, char const *v) { + add(k, make_string_c(cast(char *)v)); + } + void add(char const *k, bool v) { + auto val = gb_alloc_item(query_value_allocator, QueryValueBoolean); + *val = QueryValueBoolean(v); + add(k, val); + } + void add(char const *k, i64 v) { + auto val = gb_alloc_item(query_value_allocator, QueryValueInteger); + *val = QueryValueInteger(v); + add(k, val); + } + void add(char const *k, f64 v) { + auto val = gb_alloc_item(query_value_allocator, QueryValueFloat); + *val = QueryValueFloat(v); + add(k, val); + } + void add(String const &k, String const &v) { + auto val = gb_alloc_item(query_value_allocator, QueryValueString); + *val = QueryValueString(v); + add(k, val); + } + void add(String const &k, char const *v) { + add(k, make_string_c(cast(char *)v)); + } + void add(String const &k, bool v) { + auto val = gb_alloc_item(query_value_allocator, QueryValueBoolean); + *val = QueryValueBoolean(v); + add(k, val); + } + void add(String const &k, i64 v) { + auto val = gb_alloc_item(query_value_allocator, QueryValueInteger); + *val = QueryValueInteger(v); + add(k, val); + } + void add(String const &k, f64 v) { + auto val = gb_alloc_item(query_value_allocator, QueryValueFloat); + *val = QueryValueFloat(v); + add(k, val); + } +}; + + +#define DEF_QUERY_PROC(TYPE, VALUETYPE, NAME) TYPE *NAME(VALUETYPE value) { \ + auto v = gb_alloc_item(query_value_allocator, TYPE); \ + *v = TYPE(value); \ + return v; \ +} +#define DEF_QUERY_PROC0(TYPE, NAME) TYPE *NAME() { \ + auto v = gb_alloc_item(query_value_allocator, TYPE); \ + *v = TYPE(); \ + return v; \ +} + +DEF_QUERY_PROC(QueryValueString, String const &, query_value_string); +DEF_QUERY_PROC(QueryValueBoolean, bool, query_value_boolean); +DEF_QUERY_PROC(QueryValueInteger, i64, query_value_integer); +DEF_QUERY_PROC(QueryValueFloat, f64, query_value_float); +DEF_QUERY_PROC(QueryValueArray, Array const &, query_value_array); +DEF_QUERY_PROC(QueryValueMap, Array const &, query_value_map); +DEF_QUERY_PROC0(QueryValueArray, query_value_array); +DEF_QUERY_PROC0(QueryValueMap, query_value_map); + +isize qprintf(bool format, isize indent, char const *fmt, ...) { + if (format) while (indent --> 0) { + gb_printf("\t"); + } + va_list va; + va_start(va, fmt); + isize res = gb_printf_va(fmt, va); + va_end(va); + return res; +} + +bool qv_valid_char(u8 c) { + if (c >= 0x80) { + return false; + } + + switch (c) { + case '\"': + case '\n': + case '\r': + case '\t': + case '\v': + case '\f': + return false; + } + + return true; +} + +void print_query_data_as_json(QueryValue *value, bool format = true, isize indent = 0) { + if (value == nullptr) { + gb_printf("null"); + return; + } + switch (value->kind) { + case Query_String: { + auto v = cast(QueryValueString *)value; + String name = v->value; + isize extra = 0; + for (isize i = 0; i < name.len; i++) { + u8 c = name[i]; + if (!qv_valid_char(c)) { + extra += 5; + } + } + + if (extra == 0) { + gb_printf("\"%.*s\"", LIT(name)); + return; + } + + char const hex_table[] = "0123456789ABCDEF"; + isize buf_len = name.len + extra + 2 + 1; + + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena); + defer (gb_temp_arena_memory_end(tmp)); + + u8 *buf = gb_alloc_array(string_buffer_allocator, u8, buf_len); + + isize j = 0; + + for (isize i = 0; i < name.len; i++) { + u8 c = name[i]; + if (qv_valid_char(c)) { + buf[j+0] = c; + j += 1; + } else if (c == '"') { + buf[j+0] = '\\'; + buf[j+1] = '\"'; + j += 2; + } else { + switch (c) { + case '\n': buf[j+0] = '\\'; buf[j+1] = 'n'; j += 2; break; + case '\r': buf[j+0] = '\\'; buf[j+1] = 'r'; j += 2; break; + case '\t': buf[j+0] = '\\'; buf[j+1] = 't'; j += 2; break; + case '\v': buf[j+0] = '\\'; buf[j+1] = 'v'; j += 2; break; + case '\f': + default: + buf[j+0] = '\\'; + buf[j+1] = hex_table[0]; + buf[j+2] = hex_table[0]; + buf[j+3] = hex_table[c >> 4]; + buf[j+4] = hex_table[c & 0x0f]; + j += 5; + break; + } + } + } + + gb_printf("\"%s\"", buf); + return; + } + case Query_Boolean: { + auto v = cast(QueryValueBoolean *)value; + if (v->value) { + gb_printf("true"); + } else { + gb_printf("false"); + } + return; + } + case Query_Integer: { + auto v = cast(QueryValueInteger *)value; + gb_printf("%lld", v->value); + return; + } + case Query_Float: { + auto v = cast(QueryValueFloat *)value; + gb_printf("%f", v->value); + return; + } + case Query_Array: { + auto v = cast(QueryValueArray *)value; + if (v->value.count > 0) { + bool ff = format && !v->packed; + gb_printf("["); + if (ff) gb_printf("\n"); + for_array(i, v->value) { + qprintf(ff, indent+1, ""); + print_query_data_as_json(v->value[i], ff, indent+1); + if (i < v->value.count-1) { + gb_printf(","); + if (!ff && format) { + gb_printf(" "); + } + } + if (ff) gb_printf("\n"); + } + qprintf(ff, indent, "]"); + } else { + gb_printf("[]"); + } + return; + } + case Query_Map: { + auto v = cast(QueryValueMap *)value; + if (v->value.count > 0) { + bool ff = format && !v->packed; + gb_printf("{"); + if (ff) gb_printf("\n"); + for_array(i, v->value) { + auto kv = v->value[i]; + qprintf(ff, indent+1, "\"%.*s\":", LIT(kv.key)); + if (format) gb_printf(" "); + print_query_data_as_json(kv.value, ff, indent+1); + if (i < v->value.count-1) { + gb_printf(","); + if (!ff && format) { + gb_printf(" "); + } + } + if (ff) gb_printf("\n"); + } + qprintf(ff, indent, "}"); + } else { + gb_printf("{}"); + } + return; + } + } +} diff --git a/src/string.cpp b/src/string.cpp index 39219789f..774061edf 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -562,19 +562,20 @@ bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_bytes, String * // 0 == failure // 1 == original memory // 2 == new allocation -i32 unquote_string(gbAllocator a, String *s_) { +i32 unquote_string(gbAllocator a, String *s_, u8 quote=0) { String s = *s_; isize n = s.len; - u8 quote; - if (n < 2) { - return 0; + if (quote == 0) { + if (n < 2) { + return 0; + } + quote = s[0]; + if (quote != s[n-1]) { + return 0; + } + s.text += 1; + s.len -= 2; } - quote = s[0]; - if (quote != s[n-1]) { - return 0; - } - s.text += 1; - s.len -= 2; if (quote == '`') { if (string_contains_char(s, '`')) { diff --git a/src/timings.cpp b/src/timings.cpp index 3aea7a0fa..31de1ce8c 100644 --- a/src/timings.cpp +++ b/src/timings.cpp @@ -165,8 +165,7 @@ void timings_print_all(Timings *t, TimingUnit unit = TimingUnit_Millisecond) { timings__stop_current_section(t); t->total.finish = time_stamp_time_now(); - max_len = t->total.label.len; - max_len = 36; + max_len = gb_min(36, t->total.label.len); for_array(i, t->sections) { TimeStamp ts = t->sections[i]; max_len = gb_max(max_len, ts.label.len); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index c47511285..e496165a0 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -183,13 +183,85 @@ struct ErrorCollector { TokenPos prev; i64 count; i64 warning_count; + bool in_block; gbMutex mutex; + + Array error_buffer; + Array errors; }; gb_global ErrorCollector global_error_collector; +#define MAX_ERROR_COLLECTOR_COUNT (36) + + void init_global_error_collector(void) { gb_mutex_init(&global_error_collector.mutex); + array_init(&global_error_collector.errors, heap_allocator()); + array_init(&global_error_collector.error_buffer, heap_allocator()); +} + + +void begin_error_block(void) { + gb_mutex_lock(&global_error_collector.mutex); + global_error_collector.in_block = true; +} + +void end_error_block(void) { + if (global_error_collector.error_buffer.count > 0) { + isize n = global_error_collector.error_buffer.count; + u8 *text = gb_alloc_array(heap_allocator(), u8, n+1); + gb_memmove(text, global_error_collector.error_buffer.data, n); + text[n] = 0; + array_add(&global_error_collector.errors, make_string(text, n)); + global_error_collector.error_buffer.count = 0; + + // gbFile *f = gb_file_get_standard(gbFileStandard_Error); + // gb_file_write(f, text, n); + } + + global_error_collector.in_block = false; + gb_mutex_unlock(&global_error_collector.mutex); +} + + +#define ERROR_OUT_PROC(name) void name(char *fmt, va_list va) +typedef ERROR_OUT_PROC(ErrorOutProc); + +ERROR_OUT_PROC(default_error_out_va) { + gbFile *f = gb_file_get_standard(gbFileStandard_Error); + + char buf[4096] = {}; + isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va); + isize n = len-1; + if (global_error_collector.in_block) { + isize cap = global_error_collector.error_buffer.count + n; + array_reserve(&global_error_collector.error_buffer, cap); + u8 *data = global_error_collector.error_buffer.data + global_error_collector.error_buffer.count; + gb_memmove(data, buf, n); + global_error_collector.error_buffer.count += n; + } else { + gb_mutex_lock(&global_error_collector.mutex); + { + u8 *text = gb_alloc_array(heap_allocator(), u8, n+1); + gb_memmove(text, buf, n); + text[n] = 0; + array_add(&global_error_collector.errors, make_string(text, n)); + } + gb_mutex_unlock(&global_error_collector.mutex); + + } + gb_file_write(f, buf, n); +} + + +ErrorOutProc *error_out_va = default_error_out_va; + +void error_out(char *fmt, ...) { + va_list va; + va_start(va, fmt); + error_out_va(fmt, va); + va_end(va); } void warning_va(Token token, char *fmt, va_list va) { @@ -197,30 +269,29 @@ void warning_va(Token token, char *fmt, va_list va) { global_error_collector.warning_count++; // NOTE(bill): Duplicate error, skip it if (token.pos.line == 0) { - gb_printf_err("Error: %s\n", gb_bprintf_va(fmt, va)); + error_out("Warning: %s\n", gb_bprintf_va(fmt, va)); } else if (global_error_collector.prev != token.pos) { global_error_collector.prev = token.pos; - gb_printf_err("%.*s(%td:%td) Warning: %s\n", - LIT(token.pos.file), token.pos.line, token.pos.column, - gb_bprintf_va(fmt, va)); + error_out("%.*s(%td:%td) Warning: %s\n", + LIT(token.pos.file), token.pos.line, token.pos.column, + gb_bprintf_va(fmt, va)); } gb_mutex_unlock(&global_error_collector.mutex); } -#define MAX_ERROR_COLLECTOR_COUNT (36) void error_va(Token token, char *fmt, va_list va) { gb_mutex_lock(&global_error_collector.mutex); global_error_collector.count++; // NOTE(bill): Duplicate error, skip it if (token.pos.line == 0) { - gb_printf_err("Error: %s\n", gb_bprintf_va(fmt, va)); + error_out("Error: %s\n", gb_bprintf_va(fmt, va)); } else if (global_error_collector.prev != token.pos) { global_error_collector.prev = token.pos; - gb_printf_err("%.*s(%td:%td) %s\n", - LIT(token.pos.file), token.pos.line, token.pos.column, - gb_bprintf_va(fmt, va)); + error_out("%.*s(%td:%td) %s\n", + LIT(token.pos.file), token.pos.line, token.pos.column, + gb_bprintf_va(fmt, va)); } gb_mutex_unlock(&global_error_collector.mutex); if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) { @@ -228,17 +299,23 @@ void error_va(Token token, char *fmt, va_list va) { } } +void error_line_va(char *fmt, va_list va) { + gb_mutex_lock(&global_error_collector.mutex); + error_out_va(fmt, va); + gb_mutex_unlock(&global_error_collector.mutex); +} + void error_no_newline_va(Token token, char *fmt, va_list va) { gb_mutex_lock(&global_error_collector.mutex); global_error_collector.count++; // NOTE(bill): Duplicate error, skip it if (token.pos.line == 0) { - gb_printf_err("Error: %s", gb_bprintf_va(fmt, va)); + error_out("Error: %s", gb_bprintf_va(fmt, va)); } else if (global_error_collector.prev != token.pos) { global_error_collector.prev = token.pos; - gb_printf_err("%.*s(%td:%td) %s", - LIT(token.pos.file), token.pos.line, token.pos.column, - gb_bprintf_va(fmt, va)); + error_out("%.*s(%td:%td) %s", + LIT(token.pos.file), token.pos.line, token.pos.column, + gb_bprintf_va(fmt, va)); } gb_mutex_unlock(&global_error_collector.mutex); if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) { @@ -253,11 +330,11 @@ void syntax_error_va(Token token, char *fmt, va_list va) { // NOTE(bill): Duplicate error, skip it if (global_error_collector.prev != token.pos) { global_error_collector.prev = token.pos; - gb_printf_err("%.*s(%td:%td) Syntax Error: %s\n", + error_out("%.*s(%td:%td) Syntax Error: %s\n", LIT(token.pos.file), token.pos.line, token.pos.column, gb_bprintf_va(fmt, va)); } else if (token.pos.line == 0) { - gb_printf_err("Syntax Error: %s\n", gb_bprintf_va(fmt, va)); + error_out("Syntax Error: %s\n", gb_bprintf_va(fmt, va)); } gb_mutex_unlock(&global_error_collector.mutex); @@ -272,11 +349,11 @@ void syntax_warning_va(Token token, char *fmt, va_list va) { // NOTE(bill): Duplicate error, skip it if (global_error_collector.prev != token.pos) { global_error_collector.prev = token.pos; - gb_printf_err("%.*s(%td:%td) Syntax Warning: %s\n", - LIT(token.pos.file), token.pos.line, token.pos.column, - gb_bprintf_va(fmt, va)); + error_out("%.*s(%td:%td) Syntax Warning: %s\n", + LIT(token.pos.file), token.pos.line, token.pos.column, + gb_bprintf_va(fmt, va)); } else if (token.pos.line == 0) { - gb_printf_err("Warning: %s\n", gb_bprintf_va(fmt, va)); + error_out("Warning: %s\n", gb_bprintf_va(fmt, va)); } gb_mutex_unlock(&global_error_collector.mutex); @@ -307,6 +384,13 @@ void error(TokenPos pos, char *fmt, ...) { va_end(va); } +void error_line(char *fmt, ...) { + va_list va; + va_start(va, fmt); + error_line_va(fmt, va); + va_end(va); +} + void syntax_error(Token token, char *fmt, ...) { va_list va; diff --git a/src/types.cpp b/src/types.cpp index 77db00a7e..7643f366d 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2765,7 +2765,13 @@ gbString write_type_to_string(gbString str, Type *type) { case Type_Generic: if (type->Generic.name.len == 0) { - str = gb_string_appendc(str, "type"); + if (type->Generic.entity != nullptr) { + String name = type->Generic.entity->token.string; + str = gb_string_append_rune(str, '$'); + str = gb_string_append_length(str, name.text, name.len); + } else { + str = gb_string_appendc(str, "type"); + } } else { String name = type->Generic.name; str = gb_string_append_rune(str, '$');