From a60e6bedd963b9de4207e5bf8dba13e5c596dd1b Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Wed, 14 Sep 2016 12:00:04 +0100 Subject: [PATCH] Begin work on modules - No codegen!!! --- code/demo.odin | 5 +- code/file.odin | 2 +- code/print.odin | 6 +- code/runtime.odin | 8 +- code/win32.odin | 37 +++--- src/checker/checker.cpp | 281 +++++++++++++++++++++++----------------- src/checker/stmt.cpp | 24 +++- src/main.cpp | 2 +- src/parser.cpp | 97 ++++++++++---- 9 files changed, 288 insertions(+), 174 deletions(-) diff --git a/code/demo.odin b/code/demo.odin index b15888012..24da86658 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -1,4 +1,7 @@ -#load "basic.odin" +// #load "basic.odin" +#import "runtime.odin" +#import "print.odin" + main :: proc() { println("% % % %", "Hellope", true, 6.28, {4}int{1, 2, 3, 4}) diff --git a/code/file.odin b/code/file.odin index a424e6d73..816672fdf 100644 --- a/code/file.odin +++ b/code/file.odin @@ -1,4 +1,4 @@ -#load "win32.odin" +#import "win32.odin" File :: type struct { Handle :: type HANDLE diff --git a/code/print.odin b/code/print.odin index 01a972ebb..6095730a6 100644 --- a/code/print.odin +++ b/code/print.odin @@ -1,6 +1,6 @@ -#load "runtime.odin" -#load "win32.odin" -#load "file.odin" +#import "runtime.odin" +#import "win32.odin" +#import "file.odin" print_byte_buffer :: proc(buf: ^[]byte, b: []byte) { if buf.count < buf.capacity { diff --git a/code/runtime.odin b/code/runtime.odin index 4e79039d1..89d3f7861 100644 --- a/code/runtime.odin +++ b/code/runtime.odin @@ -1,5 +1,9 @@ -#load "win32.odin" -#load "print.odin" +#global_scope + +// TODO(bill): Remove #import in runtime.odin +#import "win32.odin" +#import "file.odin" +#import "print.odin" // IMPORTANT NOTE(bill): Do not change the order of any of this data // The compiler relies upon this _exact_ order diff --git a/code/win32.odin b/code/win32.odin index 5083d6d47..2bc927ecb 100644 --- a/code/win32.odin +++ b/code/win32.odin @@ -1,6 +1,24 @@ #foreign_system_library "user32" #foreign_system_library "gdi32" +HANDLE :: type rawptr +HWND :: type HANDLE +HDC :: type HANDLE +HINSTANCE :: type HANDLE +HICON :: type HANDLE +HCURSOR :: type HANDLE +HMENU :: type HANDLE +HBRUSH :: type HANDLE +WPARAM :: type uint +LPARAM :: type int +LRESULT :: type int +ATOM :: type i16 +BOOL :: type i32 +POINT :: type struct { x, y: i32 } +WNDPROC :: type proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT + +INVALID_HANDLE_VALUE :: (-1 as int) as HANDLE + CS_VREDRAW :: 1 CS_HREDRAW :: 2 CW_USEDEFAULT :: 0x80000000 @@ -23,25 +41,6 @@ PM_REMOVE :: 1 COLOR_BACKGROUND :: 1 as HBRUSH -HANDLE :: type rawptr -HWND :: type HANDLE -HDC :: type HANDLE -HINSTANCE :: type HANDLE -HICON :: type HANDLE -HCURSOR :: type HANDLE -HMENU :: type HANDLE -HBRUSH :: type HANDLE -WPARAM :: type uint -LPARAM :: type int -LRESULT :: type int -ATOM :: type i16 -BOOL :: type i32 -POINT :: type struct { x, y: i32 } - -INVALID_HANDLE_VALUE :: (-1 as int) as HANDLE - -WNDPROC :: type proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT - WNDCLASSEXA :: struct #ordered { size, style: u32 wnd_proc: WNDPROC diff --git a/src/checker/checker.cpp b/src/checker/checker.cpp index eb1ab9674..c09e3f78d 100644 --- a/src/checker/checker.cpp +++ b/src/checker/checker.cpp @@ -107,11 +107,16 @@ struct ProcedureInfo { }; struct Scope { - b32 is_proc; Scope *parent; Scope *prev, *next; Scope *first_child, *last_child; Map elements; // Key: String + + gbArray(Scope *) shared; + gbArray(Scope *) imported; + b32 is_proc; + b32 is_global; + b32 is_file; }; enum ExprKind { @@ -253,7 +258,9 @@ CycleChecker *cycle_checker_add(CycleChecker *cc, Entity *e) { 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->elements, 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); } @@ -277,6 +284,9 @@ void destroy_scope(Scope *scope) { } map_destroy(&scope->elements); + gb_array_free(scope->shared); + gb_array_free(scope->imported); + // NOTE(bill): No need to free scope as it "should" be allocated in an arena (except for the global scope) } @@ -287,11 +297,14 @@ void add_scope(Checker *c, AstNode *node, Scope *scope) { } -void check_open_scope(Checker *c, AstNode *stmt) { - GB_ASSERT(is_ast_node_stmt(stmt) || is_ast_node_type(stmt)); +void check_open_scope(Checker *c, AstNode *node) { + GB_ASSERT(node != NULL); + GB_ASSERT(node->kind == AstNode_Invalid || + is_ast_node_stmt(node) || + is_ast_node_type(node)); Scope *scope = make_scope(c->context.scope, c->allocator); - add_scope(c, stmt, scope); - if (stmt->kind == AstNode_ProcType) { + add_scope(c, node, scope); + if (node->kind == AstNode_ProcType) { scope->is_proc = true; } c->context.scope = scope; @@ -301,31 +314,55 @@ void check_close_scope(Checker *c) { c->context.scope = c->context.scope->parent; } -void scope_lookup_parent_entity(Checker *c, Scope *s, String name, Scope **scope, Entity **entity) { +void scope_lookup_parent_entity(Checker *c, Scope *scope, String name, Scope **scope_, Entity **entity_) { b32 gone_thru_proc = false; HashKey key = hash_string(name); - for (; s != NULL; s = s->parent) { - + for (Scope *s = scope; s != NULL; s = s->parent) { Entity **found = map_get(&s->elements, key); if (found) { Entity *e = *found; if (gone_thru_proc) { - if (e->kind == Entity_Variable && e->scope != c->global_scope) { + if (e->kind == Entity_Variable && + !e->scope->is_file && + !e->scope->is_global) { continue; } } - if (entity) *entity = e; - if (scope) *scope = s; + if (entity_) *entity_ = e; + if (scope_) *scope_ = s; return; } if (s->is_proc) { gone_thru_proc = true; + } else { + // Check shared scopes - i.e. other files @ global scope + gb_for_array(i, s->shared) { + Scope *shared = s->shared[i]; + Entity **found = map_get(&shared->elements, key); + if (found) { + Entity *e = *found; + if (e->kind == Entity_Variable && + !e->scope->is_file && + !e->scope->is_global) { + continue; + } + + if (e->scope == shared) { + // Do not return imported entities + if (entity_) *entity_ = e; + if (scope_) *scope_ = shared; + return; + } + } + } } } - if (entity) *entity = NULL; - if (scope) *scope = NULL; + + + if (entity_) *entity_ = NULL; + if (scope_) *scope_ = NULL; } Entity *scope_lookup_entity(Checker *c, Scope *s, String name) { @@ -337,8 +374,15 @@ Entity *scope_lookup_entity(Checker *c, Scope *s, String name) { Entity *current_scope_lookup_entity(Scope *s, String name) { HashKey key = hash_string(name); Entity **found = map_get(&s->elements, key); - if (found) + if (found) { return *found; + } + gb_for_array(i, s->shared) { + Entity **found = map_get(&s->shared[i]->elements, key); + if (found) { + return *found; + } + } return NULL; } @@ -570,7 +614,6 @@ b32 add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) { LIT(up->token.pos.file), up->token.pos.line, up->token.pos.column); return false; } else { - gb_printf_err("!!Here\n"); error(&c->error_collector, entity->token, "Redeclararation of `%.*s` in this scope\n" "\tat %.*s(%td:%td)", @@ -580,83 +623,12 @@ b32 add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) { } } } - if (identifier != NULL) + if (identifier != NULL) { add_entity_definition(&c->info, identifier, entity); + } return true; } - -/* -b32 add_proc_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) { - GB_ASSERT(entity->kind == Entity_Procedure); - - auto error_proc_redecl = [](Checker *c, Token token, Entity *other_entity, char *extra_msg) { - error(&c->error_collector, token, - "Redeclararation of `%.*s` in this scope %s\n" - "\tat %.*s(%td:%td)", - LIT(other_entity->token.string), - extra_msg, - LIT(other_entity->token.pos.file), other_entity->token.pos.line, other_entity->token.pos.column); - }; - - String name = entity->token.string; - HashKey key = hash_string(name); - - b32 insert_overload = false; - - if (!are_strings_equal(name, make_string("_"))) { - Entity *insert_entity = scope_insert_entity(scope, entity); - if (insert_entity != NULL) { - if (insert_entity != entity) { - isize count = multi_map_count(&scope->elements, key); - GB_ASSERT(count > 0); - Entity **entities = gb_alloc_array(gb_heap_allocator(), Entity *, count); - defer (gb_free(gb_heap_allocator(), entities)); - multi_map_get_all(&scope->elements, key, entities); - - for (isize i = 0; i < count; i++) { - Entity *e = entities[i]; - if (e == entity) { - continue; - } - if (e->kind == Entity_Procedure) { - Type *proc_type = entity->type; - Type *other_proc_type = e->type; - // gb_printf_err("%s == %s\n", type_to_string(proc_type), type_to_string(other_proc_type)); - if (are_types_identical(proc_type, other_proc_type)) { - error_proc_redecl(c, entity->token, e, "with identical types"); - return false; - } - - if (proc_type != NULL && other_proc_type != NULL) { - Type *params = proc_type->Proc.params; - Type *other_params = other_proc_type->Proc.params; - - if (are_types_identical(params, other_params)) { - error_proc_redecl(c, entity->token, e, "with 2identical parameters"); - return false; - } - } - } else { - error_proc_redecl(c, entity->token, e, ""); - return false; - } - } - insert_overload = true; - } - } - } - - if (insert_overload) { - multi_map_insert(&scope->elements, key, entity); - } - - if (identifier != NULL) - add_entity_definition(&c->info, identifier, entity); - return true; -} -*/ - void add_entity_use(CheckerInfo *i, AstNode *identifier, Entity *entity) { GB_ASSERT(identifier != NULL); GB_ASSERT(identifier->kind == AstNode_Ident); @@ -665,10 +637,9 @@ void add_entity_use(CheckerInfo *i, AstNode *identifier, Entity *entity) { } -void add_file_entity(Checker *c, AstNode *identifier, Entity *e, DeclInfo *d) { +void add_file_entity(Checker *c, Scope *file_scope, AstNode *identifier, Entity *e, DeclInfo *d) { GB_ASSERT(are_strings_equal(identifier->Ident.string, e->token.string)); - - add_entity(c, c->global_scope, identifier, e); + add_entity(c, file_scope, identifier, e); map_set(&c->info.entities, hash_pointer(e), d); } @@ -763,12 +734,9 @@ void add_type_info_type(Checker *c, Type *t) { add_type_info_type(c, bt->Proc.results); break; } - } - - void check_procedure_later(Checker *c, AstFile *file, Token token, DeclInfo *decl, Type *type, AstNode *body) { ProcedureInfo info = {}; info.file = file; @@ -835,18 +803,56 @@ void check_type_name_cycles(Checker *c, CycleCheck *cc, Entity *e) { void check_parsed_files(Checker *c) { + + gbArray(AstNode *) import_decls; + gb_array_init(import_decls, gb_heap_allocator()); + defer (gb_array_free(import_decls)); + + Map file_scopes; // Key: String (fullpath) + map_init(&file_scopes, gb_heap_allocator()); + defer (map_destroy(&file_scopes)); + + // Map full filepaths to Scopes + gb_for_array(i, c->parser->files) { + AstFile *f = &c->parser->files[i]; + Scope *scope = NULL; + scope = make_scope(c->global_scope, c->allocator); + scope->is_global = f->is_global_scope; + scope->is_file = true; + + if (scope->is_global) { + gb_array_append(c->global_scope->shared, scope); + } + + f->scope = scope; + map_set(&file_scopes, hash_string(f->tokenizer.fullpath), scope); + } + // Collect Entities gb_for_array(i, c->parser->files) { AstFile *f = &c->parser->files[i]; add_curr_ast_file(c, f); + + Scope *file_scope = f->scope; + gb_for_array(decl_index, f->decls) { AstNode *decl = f->decls[decl_index]; - if (!is_ast_node_decl(decl)) + if (!is_ast_node_decl(decl)) { continue; + } switch (decl->kind) { case_ast_node(bd, BadDecl, decl); case_end; + case_ast_node(ld, LoadDecl, decl); + // NOTE(bill): ignore + case_end; + case_ast_node(id, ImportDecl, decl); + // NOTE(bill): Handle later + case_end; + case_ast_node(fsl, ForeignSystemLibrary, decl); + // NOTE(bill): ignore + case_end; case_ast_node(vd, VarDecl, decl); switch (vd->kind) { @@ -855,11 +861,11 @@ void check_parsed_files(Checker *c) { AstNode *name = vd->names[i]; AstNode *value = vd->values[i]; ExactValue v = {ExactValue_Invalid}; - Entity *e = make_entity_constant(c->allocator, c->context.scope, name->Ident, NULL, v); - DeclInfo *di = make_declaration_info(c->allocator, c->global_scope); + Entity *e = make_entity_constant(c->allocator, file_scope, name->Ident, NULL, v); + DeclInfo *di = make_declaration_info(c->allocator, file_scope); di->type_expr = vd->type; di->init_expr = value; - add_file_entity(c, name, e, di); + add_file_entity(c, file_scope, name, e, di); } isize lhs_count = gb_array_count(vd->names); @@ -878,7 +884,7 @@ void check_parsed_files(Checker *c) { Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count); DeclInfo *di = NULL; if (gb_array_count(vd->values) > 0) { - di = make_declaration_info(gb_heap_allocator(), c->global_scope); + di = make_declaration_info(gb_heap_allocator(), file_scope); di->entities = entities; di->entity_count = entity_count; di->type_expr = vd->type; @@ -891,19 +897,19 @@ void check_parsed_files(Checker *c) { if (i < gb_array_count(vd->values)) { value = vd->values[i]; } - Entity *e = make_entity_variable(c->allocator, c->global_scope, name->Ident, NULL); + Entity *e = make_entity_variable(c->allocator, file_scope, name->Ident, NULL); entities[entity_index++] = e; DeclInfo *d = di; if (d == NULL) { AstNode *init_expr = value; - d = make_declaration_info(gb_heap_allocator(), c->global_scope); + d = make_declaration_info(gb_heap_allocator(), file_scope); d->type_expr = vd->type; d->init_expr = init_expr; d->var_decl_tags = vd->tags; } - add_file_entity(c, name, e, d); + add_file_entity(c, file_scope, name, e, d); } } break; } @@ -911,29 +917,21 @@ void check_parsed_files(Checker *c) { case_ast_node(td, TypeDecl, decl); ast_node(n, Ident, td->name); - Entity *e = make_entity_type_name(c->allocator, c->global_scope, *n, NULL); + Entity *e = make_entity_type_name(c->allocator, file_scope, *n, NULL); DeclInfo *d = make_declaration_info(c->allocator, e->scope); d->type_expr = td->type; - add_file_entity(c, td->name, e, d); + add_file_entity(c, file_scope, td->name, e, d); case_end; case_ast_node(pd, ProcDecl, decl); ast_node(n, Ident, pd->name); Token token = *n; - Entity *e = make_entity_procedure(c->allocator, c->global_scope, token, NULL); + Entity *e = make_entity_procedure(c->allocator, file_scope, token, NULL); DeclInfo *d = make_declaration_info(c->allocator, e->scope); d->proc_decl = decl; - map_set(&c->info.entities, hash_pointer(e), d); + add_file_entity(c, file_scope, pd->name, e, d); case_end; - case_ast_node(ld, LoadDecl, decl); - // NOTE(bill): ignore - case_end; - case_ast_node(fsl, ForeignSystemLibrary, decl); - // NOTE(bill): ignore - case_end; - - default: error(&c->error_collector, ast_node_token(decl), "Only declarations are allowed at file scope"); break; @@ -941,6 +939,47 @@ void check_parsed_files(Checker *c) { } } + gb_for_array(i, c->parser->files) { + AstFile *f = &c->parser->files[i]; + add_curr_ast_file(c, f); + + Scope *file_scope = f->scope; + + gb_for_array(decl_index, f->decls) { + AstNode *decl = f->decls[decl_index]; + if (decl->kind != AstNode_ImportDecl) { + continue; + } + #if 1 + ast_node(id, ImportDecl, decl); + + HashKey key = hash_string(id->fullpath); + auto found = map_get(&file_scopes, key); + GB_ASSERT_MSG(found != NULL, "Unable to find scope for file: %.*s", LIT(id->fullpath)); + Scope *scope = *found; + b32 previously_added = false; + gb_for_array(import_index, file_scope->imported) { + Scope *prev = file_scope->imported[import_index]; + if (prev == scope) { + previously_added = true; + break; + } + } + if (!previously_added) { + gb_array_append(file_scope->imported, scope); + } + + // 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; + // NOTE(bill): Do not add other imported entities + if (e->scope == scope) { + add_entity(c, file_scope, NULL, e); + } + } + #endif + } + } auto check_global_entity = [](Checker *c, EntityKind kind) { gb_for_array(i, c->info.entities.entries) { @@ -948,6 +987,11 @@ void check_parsed_files(Checker *c) { Entity *e = cast(Entity *)cast(uintptr)entry->key.key; if (e->kind == kind) { DeclInfo *d = entry->value; + + Scope *prev_scope = c->context.scope; + c->context.scope = d->scope; + defer (c->context.scope = prev_scope); + check_entity_decl(c, e, d, NULL); } } @@ -955,11 +999,11 @@ void check_parsed_files(Checker *c) { check_global_entity(c, Entity_TypeName); +#if 1 if (t_type_info == NULL) { String type_info_str = make_string("Type_Info"); - Entity **found = map_get(&c->global_scope->elements, hash_string(type_info_str)); - GB_ASSERT_MSG(found != NULL, "Internal Compiler Error: Could not find type declaration for `Type_Info`"); - Entity *e = *found; + Entity *e = current_scope_lookup_entity(c->global_scope, type_info_str); + GB_ASSERT_MSG(e != NULL, "Internal Compiler Error: Could not find type declaration for `Type_Info`"); t_type_info = e->type; t_type_info_ptr = make_type_pointer(c->allocator, t_type_info); @@ -1011,6 +1055,7 @@ void check_parsed_files(Checker *c) { add_type_and_value(&c->info, expr, info->mode, info->type, info->value); } } +#endif } diff --git a/src/checker/stmt.cpp b/src/checker/stmt.cpp index 540804129..79d4266fd 100644 --- a/src/checker/stmt.cpp +++ b/src/checker/stmt.cpp @@ -351,8 +351,9 @@ void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init_e } Operand operand = {}; - if (init_expr) + if (init_expr) { check_expr(c, &operand, init_expr); + } check_init_constant(c, e, &operand); } @@ -378,7 +379,7 @@ void check_type_decl(Checker *c, Entity *e, AstNode *type_expr, Type *def, Cycle named->Named.base = get_base_type(named->Named.base); if (named->Named.base == t_invalid) { - gb_printf("%s\n", type_to_string(named)); + // gb_printf("check_type_decl: %s\n", type_to_string(named)); } } @@ -444,7 +445,10 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d, b32 check_body_later) { defer (check_close_scope(c)); check_procedure_type(c, proc_type, pd->type); // add_proc_entity(c, d->scope, pd->name, e); - add_entity(c, d->scope, pd->name, e); + if (d->scope->is_proc) { + // Nested procedures + add_entity(c, d->scope, pd->name, e); + } @@ -561,14 +565,20 @@ void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type, Cyc if (e->type != NULL) return; switch (e->kind) { - case Entity_Constant: + case Entity_Constant: { + Scope *prev = c->context.scope; + c->context.scope = d->scope; + defer (c->context.scope = prev); c->context.decl = d; check_const_decl(c, e, d->type_expr, d->init_expr); - break; - case Entity_Variable: + } break; + case Entity_Variable: { + Scope *prev = c->context.scope; + c->context.scope = d->scope; + defer (c->context.scope = prev); c->context.decl = d; check_var_decl(c, e, d->entities, d->entity_count, d->type_expr, d->init_expr); - break; + } break; case Entity_TypeName: { CycleChecker local_cycle_checker = {}; if (cycle_checker == NULL) { diff --git a/src/main.cpp b/src/main.cpp index e3d819ffb..6c599c5c3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -103,7 +103,7 @@ int main(int argc, char **argv) { PRINT_TIMER("Semantic Checker"); #endif -#if 1 +#if 0 ssaGen ssa = {}; if (!ssa_gen_init(&ssa, &checker)) return 1; diff --git a/src/parser.cpp b/src/parser.cpp index bf2cda8fe..0136b2cfe 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1,4 +1,5 @@ struct AstNode; +struct Scope; enum ParseFileError { ParseFile_None, @@ -27,9 +28,11 @@ struct AstFile { isize expr_level; AstNodeArray decls; + b32 is_global_scope; AstNode *curr_proc; - isize scope_level; + isize scope_level; + Scope * scope; // NOTE(bill): Created in checker ErrorCollector error_collector; @@ -44,7 +47,7 @@ struct AstFile { struct Parser { String init_fullpath; gbArray(AstFile) files; - gbArray(String) loads; + gbArray(String) imports; gbArray(String) libraries; gbArray(String) system_libraries; isize load_index; @@ -226,8 +229,12 @@ AST_NODE_KIND(_DeclBegin, "", struct{}) \ u64 tags; \ String foreign_name; \ }) \ - AST_NODE_KIND(TypeDecl, "type declaration", struct { Token token; AstNode *name, *type; }) \ - AST_NODE_KIND(LoadDecl, "load declaration", struct { Token token, filepath; }) \ + AST_NODE_KIND(TypeDecl, "type declaration", struct { Token token; AstNode *name, *type; }) \ + AST_NODE_KIND(LoadDecl, "load declaration", struct { Token token, filepath; }) \ + AST_NODE_KIND(ImportDecl, "import declaration", struct { \ + Token token, relpath; \ + String fullpath; \ + }) \ AST_NODE_KIND(ForeignSystemLibrary, "foreign system library", struct { Token token, filepath; }) \ AST_NODE_KIND(_DeclEnd, "", struct{}) \ AST_NODE_KIND(_TypeBegin, "", struct{}) \ @@ -405,6 +412,8 @@ Token ast_node_token(AstNode *node) { return node->TypeDecl.token; case AstNode_LoadDecl: return node->LoadDecl.token; + case AstNode_ImportDecl: + return node->ImportDecl.token; case AstNode_ForeignSystemLibrary: return node->ForeignSystemLibrary.token; case AstNode_Field: { @@ -884,6 +893,14 @@ gb_inline AstNode *make_load_decl(AstFile *f, Token token, Token filepath) { return result; } + +gb_inline AstNode *make_import_decl(AstFile *f, Token token, Token relpath) { + AstNode *result = make_node(f, AstNode_ImportDecl); + result->ImportDecl.token = token; + result->ImportDecl.relpath = relpath; + return result; +} + gb_inline AstNode *make_foreign_system_library(AstFile *f, Token token, Token filepath) { AstNode *result = make_node(f, AstNode_ForeignSystemLibrary); result->ForeignSystemLibrary.token = token; @@ -2527,14 +2544,27 @@ AstNode *parse_stmt(AstFile *f) { case Token_Hash: { s = parse_tag_stmt(f, NULL); String tag = s->TagStmt.name.string; - - if (are_strings_equal(tag, make_string("load"))) { + if (are_strings_equal(tag, make_string("global_scope"))) { + if (f->curr_proc == NULL) { + f->is_global_scope = true; + return make_empty_stmt(f, f->cursor[0]); + } + ast_file_err(f, token, "You cannot use #global_scope within a procedure. This must be done at the file scope."); + return make_bad_decl(f, token, f->cursor[0]); + } else if (are_strings_equal(tag, make_string("load"))) { Token file_path = expect_token(f, Token_String); if (f->curr_proc == NULL) { return make_load_decl(f, s->TagStmt.token, file_path); } 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("import"))) { + Token file_path = expect_token(f, Token_String); + if (f->curr_proc == NULL) { + return make_import_decl(f, s->TagStmt.token, file_path); + } + 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("foreign_system_library"))) { Token file_path = expect_token(f, Token_String); if (f->curr_proc == NULL) { @@ -2660,7 +2690,7 @@ void destroy_ast_file(AstFile *f) { b32 init_parser(Parser *p) { gb_array_init(p->files, gb_heap_allocator()); - gb_array_init(p->loads, gb_heap_allocator()); + gb_array_init(p->imports, gb_heap_allocator()); gb_array_init(p->libraries, gb_heap_allocator()); gb_array_init(p->system_libraries, gb_heap_allocator()); return true; @@ -2672,26 +2702,26 @@ void destroy_parser(Parser *p) { destroy_ast_file(&p->files[i]); } #if 1 - gb_for_array(i, p->loads) { - // gb_free(gb_heap_allocator(), p->loads[i].text); + gb_for_array(i, p->imports) { + // gb_free(gb_heap_allocator(), p->imports[i].text); } #endif gb_array_free(p->files); - gb_array_free(p->loads); + gb_array_free(p->imports); gb_array_free(p->libraries); gb_array_free(p->system_libraries); } // NOTE(bill): Returns true if it's added -b32 try_add_load_path(Parser *p, String import_file) { - gb_for_array(i, p->loads) { - String import = p->loads[i]; +b32 try_add_import_path(Parser *p, String import_file, AstNode *node) { + gb_for_array(i, p->imports) { + String import = p->imports[i]; if (are_strings_equal(import, import_file)) { return false; } } - gb_array_append(p->loads, import_file); + gb_array_append(p->imports, import_file); return true; } @@ -2716,7 +2746,7 @@ gb_global Rune illegal_import_runes[] = { '|', ',', '<', '>', '?', }; -b32 is_load_path_valid(String path) { +b32 is_import_path_valid(String path) { if (path.len > 0) { u8 *start = path.text; u8 *end = path.text + path.len; @@ -2770,12 +2800,11 @@ void parse_file(Parser *p, AstFile *f) { auto *id = &node->LoadDecl; String file_str = id->filepath.string; - if (!is_load_path_valid(file_str)) { + if (!is_import_path_valid(file_str)) { ast_file_err(f, ast_node_token(node), "Invalid `load` path"); continue; } - isize str_len = base_dir.len+file_str.len; u8 *str = gb_alloc_array(gb_heap_allocator(), u8, str_len+1); defer (gb_free(gb_heap_allocator(), str)); @@ -2786,14 +2815,38 @@ void parse_file(Parser *p, AstFile *f) { char *path_str = gb_path_get_full_name(gb_heap_allocator(), cast(char *)str); String import_file = make_string(path_str); - if (!try_add_load_path(p, import_file)) { + if (!try_add_import_path(p, import_file, node)) { gb_free(gb_heap_allocator(), import_file.text); } + } else if (node->kind == AstNode_ImportDecl) { + auto *id = &node->ImportDecl; + String file_str = id->relpath.string; + + if (!is_import_path_valid(file_str)) { + ast_file_err(f, ast_node_token(node), "Invalid `load` path"); + continue; + } + + isize str_len = base_dir.len+file_str.len; + u8 *str = gb_alloc_array(gb_heap_allocator(), u8, str_len+1); + defer (gb_free(gb_heap_allocator(), str)); + + gb_memcopy(str, base_dir.text, base_dir.len); + gb_memcopy(str+base_dir.len, file_str.text, file_str.len); + str[str_len] = '\0'; + // HACK(bill): memory leak + char *path_str = gb_path_get_full_name(gb_heap_allocator(), cast(char *)str); + String import_file = make_string(path_str); + + id->fullpath = import_file; + if (!try_add_import_path(p, import_file, node)) { + // gb_free(gb_heap_allocator(), import_file.text); + } } else if (node->kind == AstNode_ForeignSystemLibrary) { auto *id = &node->ForeignSystemLibrary; String file_str = id->filepath.string; - if (!is_load_path_valid(file_str)) { + if (!is_import_path_valid(file_str)) { ast_file_err(f, ast_node_token(node), "Invalid `foreign_system_library` path"); continue; } @@ -2808,11 +2861,11 @@ void parse_file(Parser *p, AstFile *f) { ParseFileError parse_files(Parser *p, char *init_filename) { char *fullpath_str = gb_path_get_full_name(gb_heap_allocator(), init_filename); String init_fullpath = make_string(fullpath_str); - gb_array_append(p->loads, init_fullpath); + gb_array_append(p->imports, init_fullpath); p->init_fullpath = init_fullpath; - gb_for_array(i, p->loads) { - String import_path = p->loads[i]; + gb_for_array(i, p->imports) { + String import_path = p->imports[i]; AstFile file = {}; ParseFileError err = init_ast_file(&file, import_path); if (err != ParseFile_None) {