From 458ec5922e69f105ca92d348e475693dfa252ad0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 25 May 2019 20:24:19 +0100 Subject: odin query Output .json file containing information about the program --- src/main.cpp | 469 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 460 insertions(+), 9 deletions(-) (limited to 'src/main.cpp') 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; } -- cgit v1.2.3