From eb424bb315a880bf52fe843733445dfb502c1525 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Fri, 16 Sep 2016 19:46:48 +0100 Subject: [PATCH] #import and #load #import - imported entities will not get exported #load - loaded entities will get exported --- code/demo.odin | 15 +--- code/win32.odin | 2 +- src/checker/checker.cpp | 21 +++++- src/checker/expr.cpp | 21 ++++-- src/codegen/codegen.cpp | 22 +++++- src/codegen/print_llvm.cpp | 104 ++++++++++++++++++++++----- src/codegen/ssa.cpp | 140 ++++++++++++++++++++++++++++++++++--- src/main.cpp | 4 +- src/parser.cpp | 17 ++++- 9 files changed, 290 insertions(+), 56 deletions(-) diff --git a/code/demo.odin b/code/demo.odin index 4909a3e72..f1667bd66 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -1,19 +1,6 @@ #import "punity.odin" as pn -#import "fmt.odin" as fmt - -test :: proc() { - thing :: proc() { - thing :: proc() { - fmt.println("Hello1") - } - - fmt.println("Hello") - } -} main :: proc() { - test() - init :: proc(c: ^pn.Core) { } @@ -24,5 +11,5 @@ main :: proc() { } } - pn.run(init, step) + // pn.run(init, step) } diff --git a/code/win32.odin b/code/win32.odin index 0ccc934aa..a1fac63f0 100644 --- a/code/win32.odin +++ b/code/win32.odin @@ -96,7 +96,7 @@ SetWindowTextA :: proc(hwnd: HWND, c_string: ^u8) -> BOOL #foreign #dll_import QueryPerformanceFrequency :: proc(result: ^i64) -> i32 #foreign #dll_import QueryPerformanceCounter :: proc(result: ^i64) -> i32 #foreign #dll_import -Sleep :: proc(ms: i32) -> i32 #foreign +Sleep :: proc(ms: i32) -> i32 #foreign #dll_import OutputDebugStringA :: proc(c_str: ^u8) #foreign #dll_import diff --git a/src/checker/checker.cpp b/src/checker/checker.cpp index f6fb6a381..0f2ccc8b1 100644 --- a/src/checker/checker.cpp +++ b/src/checker/checker.cpp @@ -111,6 +111,7 @@ struct Scope { Scope *prev, *next; Scope *first_child, *last_child; Map elements; // Key: String + Map implicit; // Key: String gbArray(Scope *) shared; gbArray(Scope *) imported; @@ -216,6 +217,7 @@ struct CheckerInfo { Map entities; // Key: Entity * Map foreign_procs; // Key: String Map type_info_map; // Key: Type * + Map files; // Key: String isize type_info_index; }; @@ -261,8 +263,10 @@ Scope *make_scope(Scope *parent, gbAllocator allocator) { Scope *s = gb_alloc_item(allocator, Scope); s->parent = parent; map_init(&s->elements, gb_heap_allocator()); + map_init(&s->implicit, gb_heap_allocator()); gb_array_init(s->shared, gb_heap_allocator()); gb_array_init(s->imported, gb_heap_allocator()); + if (parent != NULL && parent != universal_scope) { DLIST_APPEND(parent->first_child, parent->last_child, s); } @@ -286,6 +290,7 @@ void destroy_scope(Scope *scope) { } map_destroy(&scope->elements); + map_destroy(&scope->implicit); gb_array_free(scope->shared); gb_array_free(scope->imported); @@ -397,8 +402,9 @@ Entity *scope_insert_entity(Scope *s, Entity *entity) { if (found) return *found; map_set(&s->elements, key, entity); - if (entity->scope == NULL) + if (entity->scope == NULL) { entity->scope = s; + } return NULL; } @@ -506,6 +512,7 @@ void init_checker_info(CheckerInfo *i) { map_init(&i->untyped, a); map_init(&i->foreign_procs, a); map_init(&i->type_info_map, a); + map_init(&i->files, a); i->type_info_index = 0; } @@ -519,6 +526,8 @@ void destroy_checker_info(CheckerInfo *i) { map_destroy(&i->untyped); map_destroy(&i->foreign_procs); map_destroy(&i->type_info_map); + map_destroy(&i->files); + } @@ -889,6 +898,7 @@ void check_parsed_files(Checker *c) { f->scope = scope; HashKey key = hash_string(f->tokenizer.fullpath); map_set(&file_scopes, key, scope); + map_set(&c->info.files, key, f); } // Collect Entities @@ -1032,10 +1042,17 @@ void check_parsed_files(Checker *c) { // NOTE(bill): Add imported entities to this file's scope gb_for_array(elem_index, scope->elements.entries) { Entity *e = scope->elements.entries[elem_index].value; + if (e->scope == file_scope) { + continue; + } // NOTE(bill): Do not add other imported entities - if (e->scope == scope && e->kind != Entity_ImportName) { + if (e->kind != Entity_ImportName) { if (is_entity_exported(e)) { add_entity(c, file_scope, NULL, e); + if (!id->is_load) { + HashKey key = hash_string(e->token.string); + map_set(&file_scope->implicit, key, e); + } } } } diff --git a/src/checker/expr.cpp b/src/checker/expr.cpp index 8b207b373..83fe981be 100644 --- a/src/checker/expr.cpp +++ b/src/checker/expr.cpp @@ -2000,19 +2000,28 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node) { Entity *e = scope_lookup_entity(c->context.scope, name); add_entity_use(&c->info, op_expr, e); if (e != NULL && e->kind == Entity_ImportName) { + String sel_name = selector->Ident.string; check_op_expr = false; - entity = scope_lookup_entity(e->ImportName.scope, selector->Ident.string); + entity = scope_lookup_entity(e->ImportName.scope, sel_name); if (entity == NULL) { - gbString sel_str = expr_to_string(selector); - defer (gb_string_free(sel_str)); - error(&c->error_collector, ast_node_token(op_expr), "`%s` is not declared by `%.*s`", sel_str, LIT(name)); + error(&c->error_collector, ast_node_token(op_expr), "`%.*s` is not declared by `%.*s`", LIT(sel_name), LIT(name)); goto error; } if (entity->type == NULL) { // Not setup yet check_entity_decl(c, entity, NULL, NULL); } GB_ASSERT(entity->type != NULL); - if (!is_entity_exported(entity)) { + // if (!is_entity_exported(entity)) { + b32 is_not_exported = !((e->ImportName.scope == entity->scope) && (entity->kind != Entity_ImportName)); + + if (is_not_exported) { + auto found = map_get(&e->ImportName.scope->implicit, hash_string(sel_name)); + if (!found) { + is_not_exported = false; + } + } + + if (is_not_exported) { gbString sel_str = expr_to_string(selector); defer (gb_string_free(sel_str)); error(&c->error_collector, ast_node_token(op_expr), "`%s` is not exported by `%.*s`", sel_str, LIT(name)); @@ -3044,8 +3053,8 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint case_ast_node(pl, ProcLit, node); check_open_scope(c, pl->type); - c->context.decl = make_declaration_info(c->allocator, c->context.scope); defer (check_close_scope(c)); + c->context.decl = make_declaration_info(c->allocator, c->context.scope); Type *proc_type = check_type(c, pl->type); if (proc_type != NULL) { check_proc_body(c, empty_token, c->context.decl, proc_type, pl->body); diff --git a/src/codegen/codegen.cpp b/src/codegen/codegen.cpp index f45c852c8..04f1f88ea 100644 --- a/src/codegen/codegen.cpp +++ b/src/codegen/codegen.cpp @@ -150,7 +150,7 @@ void ssa_gen_tree(ssaGen *s) { name = pd->foreign_name; } - ssaValue *p = ssa_make_value_procedure(a, m, e->type, decl->type_expr, body, name); + ssaValue *p = ssa_make_value_procedure(a, m, e, e->type, decl->type_expr, body, name); p->Proc.tags = pd->tags; map_set(&m->values, hash_pointer(e), p); @@ -169,6 +169,22 @@ void ssa_gen_tree(ssaGen *s) { ssa_build_proc(v, NULL); } + ssaDebugInfo *compile_unit = m->debug_info.entries[0].value; + GB_ASSERT(compile_unit->kind == ssaDebugInfo_CompileUnit); + ssaDebugInfo *all_procs = ssa_alloc_debug_info(m->allocator, ssaDebugInfo_AllProcs); + gb_array_init(all_procs->AllProcs.procs, gb_heap_allocator()); + map_set(&m->debug_info, hash_pointer(all_procs), all_procs); // NOTE(bill): This doesn't need to be mapped + compile_unit->CompileUnit.all_procs = all_procs; + + + gb_for_array(i, m->debug_info.entries) { + auto *entry = &m->debug_info.entries[i]; + ssaDebugInfo *di = entry->value; + di->id = i; + if (di->kind == ssaDebugInfo_Proc) { + gb_array_append(all_procs->AllProcs.procs, di); + } + } { // Startup Runtime @@ -178,7 +194,7 @@ void ssa_gen_tree(ssaGen *s) { NULL, 0, NULL, 0, false); AstNode *body = gb_alloc_item(a, AstNode); - ssaValue *p = ssa_make_value_procedure(a, m, proc_type, NULL, body, name); + ssaValue *p = ssa_make_value_procedure(a, m, NULL, proc_type, NULL, body, name); Token token = {}; token.string = name; Entity *e = make_entity_procedure(a, NULL, token, proc_type); @@ -523,6 +539,8 @@ void ssa_gen_tree(ssaGen *s) { } + + // m->layout = make_string("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"); diff --git a/src/codegen/print_llvm.cpp b/src/codegen/print_llvm.cpp index d0debf173..a621af244 100644 --- a/src/codegen/print_llvm.cpp +++ b/src/codegen/print_llvm.cpp @@ -766,8 +766,7 @@ void ssa_print_proc(ssaFileBuffer *f, ssaModule *m, ssaProcedure *proc) { if (proc->tags & ProcTag_stdcall) { ssa_fprintf(f, "cc 64 "); - } - if (proc->tags & ProcTag_fastcall) { + } else if (proc->tags & ProcTag_fastcall) { ssa_fprintf(f, "cc 65 "); } @@ -803,28 +802,34 @@ void ssa_print_proc(ssaFileBuffer *f, ssaModule *m, ssaProcedure *proc) { ssa_fprintf(f, ") "); - if (proc->tags != 0) { - if (proc->tags & ProcTag_inline) { - ssa_fprintf(f, "alwaysinline "); - } - if (proc->tags & ProcTag_no_inline) { - ssa_fprintf(f, "noinline "); - } + if (proc->tags & ProcTag_inline) { + ssa_fprintf(f, "alwaysinline "); + } + if (proc->tags & ProcTag_no_inline) { + ssa_fprintf(f, "noinline "); + } - // if (proc->tags & ProcTag_stdcall) { - // ssa_fprintf(f, "\"cc\"=\"64\" "); - // } - // if (proc->tags & ProcTag_fastcall) { - // ssa_fprintf(f, "\"cc\"=\"65\" "); - // } + // if (proc->tags & ProcTag_stdcall) { + // ssa_fprintf(f, "\"cc\"=\"64\" "); + // } + // if (proc->tags & ProcTag_fastcall) { + // ssa_fprintf(f, "\"cc\"=\"65\" "); + // } - if (proc->tags & ProcTag_foreign) { - ssa_fprintf(f, "; foreign\n"); - } + if (proc->module->generate_debug_info && proc->entity != NULL) { + ssaDebugInfo *di = *map_get(&proc->module->debug_info, hash_pointer(proc->entity)); + GB_ASSERT(di->kind == ssaDebugInfo_Proc); + ssa_fprintf(f, "!dbg !%d ", di->id); + } + + + if (proc->tags & ProcTag_foreign) { + ssa_fprintf(f, "; foreign\n"); } if (proc->body != NULL) { // ssa_fprintf(f, "nounwind uwtable {\n"); + ssa_fprintf(f, "{\n"); gb_for_array(i, proc->blocks) { ssaBlock *block = proc->blocks[i]; @@ -934,4 +939,67 @@ void ssa_print_llvm_ir(ssaFileBuffer *f, ssaModule *m) { } ssa_fprintf(f, "\n"); } + + + if (m->generate_debug_info) { + ssa_fprintf(f, "\n"); + ssa_fprintf(f, "!llvm.dbg.cu = !{!0}\n"); + + gb_for_array(di_index, m->debug_info.entries) { + auto *entry = &m->debug_info.entries[di_index]; + ssaDebugInfo *di = entry->value; + ssa_fprintf(f, "!%d = ", di->id); + defer (ssa_fprintf(f, "\n")); + + switch (di->kind) { + case ssaDebugInfo_CompileUnit: { + auto *cu = &di->CompileUnit; + ssaDebugInfo *file = *map_get(&m->debug_info, hash_pointer(cu->file)); + ssa_fprintf(f, + "distinct !DICompileUnit(" + "language: DW_LANG_Go, " // Is this good enough? + "file: !%d, " + "producer: \"%.*s\", " + "flags: \"\", " + "runtimeVersion: 0, " + "isOptimized: false, " + "emissionKind: FullDebug" + ")", + file->id, LIT(cu->producer)); + + } break; + case ssaDebugInfo_File: + ssa_fprintf(f, "!DIFile(filename: \""); + ssa_print_escape_string(f, di->File.filename, false); + ssa_fprintf(f, "\", directory: \""); + ssa_print_escape_string(f, di->File.directory, false); + ssa_fprintf(f, "\")"); + break; + case ssaDebugInfo_Proc: + ssa_fprintf(f, "distinct !DISubprogram(" + "name: \"%.*s\", " + // "linkageName: \"\", " + "file: !%d, " + "line: %td, " + "isDefinition: true, " + "isLocal: false, " + "unit: !0" + ")", + LIT(di->Proc.name), + di->Proc.file->id, + di->Proc.pos.line); + break; + + case ssaDebugInfo_AllProcs: + ssa_fprintf(f, "!{"); + gb_for_array(proc_index, di->AllProcs.procs) { + ssaDebugInfo *p = di->AllProcs.procs[proc_index]; + if (proc_index > 0) {ssa_fprintf(f, ",");} + ssa_fprintf(f, "!%d", p->id); + } + ssa_fprintf(f, "}"); + break; + } + } + } } diff --git a/src/codegen/ssa.cpp b/src/codegen/ssa.cpp index f1c888fa7..fac8423b0 100644 --- a/src/codegen/ssa.cpp +++ b/src/codegen/ssa.cpp @@ -4,19 +4,61 @@ struct ssaBlock; struct ssaValue; +enum ssaDebugInfoKind { + ssaDebugInfo_Invalid, + + ssaDebugInfo_CompileUnit, + ssaDebugInfo_File, + ssaDebugInfo_Proc, + ssaDebugInfo_AllProcs, + + ssaDebugInfo_Count, +}; + +struct ssaDebugInfo { + ssaDebugInfoKind kind; + i32 id; + + union { + struct { + AstFile *file; + String producer; + ssaDebugInfo *all_procs; + } CompileUnit; + struct { + AstFile *file; + String filename; + String directory; + } File; + struct { + Entity * entity; + String name; + ssaDebugInfo *file; + TokenPos pos; + } Proc; + struct { + gbArray(ssaDebugInfo *) procs; + } AllProcs; + }; +}; + struct ssaModule { CheckerInfo * info; BaseTypeSizes sizes; gbArena arena; gbAllocator allocator; + b32 generate_debug_info; u32 stmt_state_flags; + // String source_filename; String layout; + // String triple; - Map values; // Key: Entity * - Map members; // Key: String - i32 global_string_index; + Map values; // Key: Entity * + Map members; // Key: String + Map debug_info; // Key: Unique pointer + i32 global_string_index; }; @@ -55,6 +97,7 @@ struct ssaProcedure { ssaProcedure *parent; gbArray(ssaProcedure *) children; + Entity * entity; ssaModule * module; String name; Type * type; @@ -310,6 +353,12 @@ void ssa_module_add_value(ssaModule *m, Entity *e, ssaValue *v) { map_set(&m->values, hash_pointer(e), v); } +ssaDebugInfo *ssa_alloc_debug_info(gbAllocator a, ssaDebugInfoKind kind) { + ssaDebugInfo *di = gb_alloc_item(a, ssaDebugInfo); + di->kind = kind; + return di; +} + void ssa_init_module(ssaModule *m, Checker *c) { // TODO(bill): Determine a decent size for the arena isize token_count = c->parser->total_token_count; @@ -319,8 +368,9 @@ void ssa_init_module(ssaModule *m, Checker *c) { m->info = &c->info; m->sizes = c->sizes; - map_init(&m->values, gb_heap_allocator()); - map_init(&m->members, gb_heap_allocator()); + map_init(&m->values, gb_heap_allocator()); + map_init(&m->members, gb_heap_allocator()); + map_init(&m->debug_info, gb_heap_allocator()); // Default states m->stmt_state_flags = 0; @@ -376,11 +426,20 @@ void ssa_init_module(ssaModule *m, Checker *c) { map_set(&m->members, hash_string(name), g); } } + + { + ssaDebugInfo *di = ssa_alloc_debug_info(m->allocator, ssaDebugInfo_CompileUnit); + di->CompileUnit.file = m->info->files.entries[0].value; // Zeroth is the init file + di->CompileUnit.producer = make_string("odin"); + + map_set(&m->debug_info, hash_pointer(m), di); + } } void ssa_destroy_module(ssaModule *m) { map_destroy(&m->values); map_destroy(&m->members); + map_destroy(&m->debug_info); gb_arena_free(&m->arena); } @@ -445,6 +504,46 @@ Type *ssa_type(ssaValue *value) { return NULL; } +ssaDebugInfo *ssa_add_debug_info_file(ssaProcedure *proc, AstFile *file) { + GB_ASSERT(file != NULL); + ssaDebugInfo *di = ssa_alloc_debug_info(proc->module->allocator, ssaDebugInfo_File); + di->File.file = file; + + String filename = file->tokenizer.fullpath; + String directory = filename; + isize slash_index = 0; + for (isize i = filename.len-1; i >= 0; i--) { + if (filename.text[i] == '\\' || + filename.text[i] == '/') { + break; + } + slash_index = i; + } + directory.len = slash_index-1; + filename.text = filename.text + slash_index; + filename.len -= slash_index; + + + di->File.filename = filename; + di->File.directory = directory; + + map_set(&proc->module->debug_info, hash_pointer(file), di); + return di; +} + + +ssaDebugInfo *ssa_add_debug_info_proc(ssaProcedure *proc, Entity *entity, String name, ssaDebugInfo *file) { + GB_ASSERT(entity != NULL); + ssaDebugInfo *di = ssa_alloc_debug_info(proc->module->allocator, ssaDebugInfo_Proc); + di->Proc.entity = entity; + di->Proc.name = name; + di->Proc.file = file; + di->Proc.pos = entity->token.pos; + + map_set(&proc->module->debug_info, hash_pointer(entity), di); + return di; +} + @@ -674,9 +773,10 @@ ssaValue *ssa_make_const_bool(gbAllocator a, b32 b) { } -ssaValue *ssa_make_value_procedure(gbAllocator a, ssaModule *m, Type *type, AstNode *type_expr, AstNode *body, String name) { +ssaValue *ssa_make_value_procedure(gbAllocator a, ssaModule *m, Entity *entity, Type *type, AstNode *type_expr, AstNode *body, String name) { ssaValue *v = ssa_alloc_value(a, ssaValue_Proc); v->Proc.module = m; + v->Proc.entity = entity; v->Proc.type = type; v->Proc.type_expr = type_expr; v->Proc.body = body; @@ -1919,7 +2019,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue Type *type = type_of_expr(proc->module->info, expr); ssaValue *value = ssa_make_value_procedure(proc->module->allocator, - proc->module, type, pl->type, pl->body, name); + proc->module, NULL, type, pl->type, pl->body, name); value->Proc.tags = pl->tags; @@ -3053,7 +3153,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { GB_ASSERT_MSG(found != NULL, "Unable to find: %.*s", LIT(pd->name->Ident.string)); Entity *e = *found; ssaValue *value = ssa_make_value_procedure(proc->module->allocator, - proc->module, e->type, pd->type, pd->body, name); + proc->module, e, e->type, pd->type, pd->body, name); value->Proc.tags = pd->tags; @@ -3072,7 +3172,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { Entity *e = *map_get(&info->definitions, hash_pointer(pd->name)); Entity *f = *map_get(&info->foreign_procs, hash_string(name)); ssaValue *value = ssa_make_value_procedure(proc->module->allocator, - proc->module, e->type, pd->type, pd->body, name); + proc->module, e, e->type, pd->type, pd->body, name); value->Proc.tags = pd->tags; @@ -3594,6 +3694,28 @@ void ssa_build_proc(ssaValue *value, ssaProcedure *parent) { proc->parent = parent; + if (proc->entity != NULL) { + ssaModule *m = proc->module; + CheckerInfo *info = m->info; + Entity *e = proc->entity; + String filename = e->token.pos.file; + AstFile **found = map_get(&info->files, hash_string(filename)); + GB_ASSERT(found != NULL); + AstFile *f = *found; + ssaDebugInfo *di_file = NULL; + + + ssaDebugInfo **di_file_found = map_get(&m->debug_info, hash_pointer(f)); + if (di_file_found) { + di_file = *di_file_found; + GB_ASSERT(di_file->kind == ssaDebugInfo_File); + } else { + di_file = ssa_add_debug_info_file(proc, f); + } + + ssa_add_debug_info_proc(proc, e, proc->name, di_file); + } + if (proc->body != NULL) { u32 prev_stmt_state_flags = proc->module->stmt_state_flags; defer (proc->module->stmt_state_flags = prev_stmt_state_flags); diff --git a/src/main.cpp b/src/main.cpp index 578b59121..2191aaa5d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -148,13 +148,13 @@ int main(int argc, char **argv) { gb_for_array(i, parser.system_libraries) { String lib = parser.system_libraries[i]; isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf), - " -l%.*s", LIT(lib)); + " -l%.*s.lib", LIT(lib)); lib_str = gb_string_appendc(lib_str, lib_str_buf); } exit_code = win32_exec_command_line_app( "clang %.*s.bc -o %.*s.exe " - "-O0 " + "-O0 -g " // "-O2 " "-Wno-override-module " "%s", diff --git a/src/parser.cpp b/src/parser.cpp index 61c4b397a..128d944e1 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -246,6 +246,7 @@ AST_NODE_KIND(_DeclBegin, "", struct{}) \ Token token, relpath; \ String fullpath; \ Token import_name; \ + b32 is_load; \ }) \ AST_NODE_KIND(ForeignSystemLibrary, "foreign system library", struct { Token token, filepath; }) \ AST_NODE_KIND(_DeclEnd, "", struct{}) \ @@ -896,11 +897,12 @@ gb_inline AstNode *make_type_decl(AstFile *f, Token token, AstNode *name, AstNod return result; } -gb_inline AstNode *make_import_decl(AstFile *f, Token token, Token relpath, Token import_name) { +gb_inline AstNode *make_import_decl(AstFile *f, Token token, Token relpath, Token import_name, b32 is_load) { AstNode *result = make_node(f, AstNode_ImportDecl); result->ImportDecl.token = token; result->ImportDecl.relpath = relpath; result->ImportDecl.import_name = import_name; + result->ImportDecl.is_load = is_load; return result; } @@ -2585,10 +2587,21 @@ AstNode *parse_stmt(AstFile *f) { import_name = expect_token(f, Token_Identifier); if (f->curr_proc == NULL) { - return make_import_decl(f, s->TagStmt.token, file_path, import_name); + return make_import_decl(f, s->TagStmt.token, file_path, import_name, false); } ast_file_err(f, token, "You cannot use #import within a procedure. This must be done at the file scope."); return make_bad_decl(f, token, file_path); + } else if (are_strings_equal(tag, make_string("load"))) { + // TODO(bill): better error messages + Token file_path = expect_token(f, Token_String); + Token import_name = file_path; + import_name.string = make_string("_"); + + if (f->curr_proc == NULL) { + return make_import_decl(f, s->TagStmt.token, file_path, import_name, true); + } + ast_file_err(f, token, "You cannot use #load within a procedure. This must be done at the file scope."); + return make_bad_decl(f, token, file_path); } else if (are_strings_equal(tag, make_string("foreign_system_library"))) { Token file_path = expect_token(f, Token_String); if (f->curr_proc == NULL) {