aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorgingerBill <bill@gingerbill.org>2019-05-25 20:24:19 +0100
committergingerBill <bill@gingerbill.org>2019-05-25 20:24:19 +0100
commit458ec5922e69f105ca92d348e475693dfa252ad0 (patch)
tree0c7db7614a6a3f9d05816e54adc03d82118f06c3 /src
parentf5fdd031f9b763a2b6a86f2fc536735fc8d7ed5d (diff)
odin query
Output .json file containing information about the program
Diffstat (limited to 'src')
-rw-r--r--src/build_settings.cpp1
-rw-r--r--src/check_decl.cpp5
-rw-r--r--src/check_expr.cpp66
-rw-r--r--src/check_stmt.cpp15
-rw-r--r--src/checker.cpp19
-rw-r--r--src/common.cpp14
-rw-r--r--src/main.cpp469
-rw-r--r--src/query_data.cpp361
-rw-r--r--src/string.cpp21
-rw-r--r--src/timings.cpp3
-rw-r--r--src/tokenizer.cpp122
-rw-r--r--src/types.cpp8
12 files changed, 1018 insertions, 86 deletions
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<AstPackage *>(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<Entity *>(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<QueryValue *> const &v) {
+ kind = Query_Array;
+ value = v;
+ packed = false;
+ }
+ Array<QueryValue *> 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<QueryValuePair> const &v) {
+ kind = Query_Map;
+ value = v;
+ packed = false;
+ }
+ Array<QueryValuePair> 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<QueryValue *> const &, query_value_array);
+DEF_QUERY_PROC(QueryValueMap, Array<QueryValuePair> 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;
- }
- quote = s[0];
- if (quote != s[n-1]) {
- 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;
}
- 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<u8> error_buffer;
+ Array<String> 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, '$');