From c067b90403ab8493daa0bf5867b2bd92319feea5 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 26 May 2018 23:12:55 +0100 Subject: [PATCH] Add basic package support (no IR support yet) --- ...indows.odin => atomics_windows_amd64.odin} | 5 +- core/os/os_essence.odin | 284 +++---- core/os/os_windows.odin | 2 +- core/sys/win32/wgl.odin | 4 +- core/sys/win32/windows.odin | 4 +- core/thread/thread.odin | 5 +- core/unicode/utf16/utf16.odin | 2 +- src/build_settings.cpp | 115 ++- src/check_decl.cpp | 4 +- src/check_expr.cpp | 13 +- src/check_stmt.cpp | 5 +- src/checker.cpp | 747 ++++++------------ src/checker.hpp | 28 +- src/common.cpp | 20 +- src/entity.cpp | 16 + src/main.cpp | 29 +- src/parser.cpp | 203 +++-- src/parser.hpp | 18 +- src/string.cpp | 7 + src/tokenizer.cpp | 5 +- 20 files changed, 698 insertions(+), 818 deletions(-) rename core/atomics/{atomics_windows.odin => atomics_windows_amd64.odin} (95%) diff --git a/core/atomics/atomics_windows.odin b/core/atomics/atomics_windows_amd64.odin similarity index 95% rename from core/atomics/atomics_windows.odin rename to core/atomics/atomics_windows_amd64.odin index 1a510290d..d1f090419 100644 --- a/core/atomics/atomics_windows.odin +++ b/core/atomics/atomics_windows_amd64.odin @@ -3,10 +3,7 @@ package atomics // TODO(bill): Use assembly instead here to implement atomics // Inline vs external file? -when ODIN_OS == "windows" { - import win32 "core:sys/windows.odin" -} -#assert(ODIN_ARCH == "amd64"); // TODO(bill): x86 version +import "core:sys/win32" yield_thread :: proc() { win32.mm_pause(); } diff --git a/core/os/os_essence.odin b/core/os/os_essence.odin index b417ff0b1..6993e7c8f 100644 --- a/core/os/os_essence.odin +++ b/core/os/os_essence.odin @@ -1,174 +1,174 @@ package os -foreign import api "system:api" +// foreign import api "system:api" -Handle :: distinct int; -Errno :: distinct int; +// Handle :: distinct int; +// Errno :: distinct int; -O_RDONLY :: 0x00001; -O_WRONLY :: 0x00002; -O_RDWR :: 0x00003; -O_CREATE :: 0x00040; -O_EXCL :: 0x00080; -O_TRUNC :: 0x00200; -O_APPEND :: 0x00400; +// O_RDONLY :: 0x00001; +// O_WRONLY :: 0x00002; +// O_RDWR :: 0x00003; +// O_CREATE :: 0x00040; +// O_EXCL :: 0x00080; +// O_TRUNC :: 0x00200; +// O_APPEND :: 0x00400; -ERROR_NONE : Errno : -1 ; -ERROR_UNKNOWN_OPERATION_FAILURE : Errno : -7 ; -ERROR_PATH_NOT_WITHIN_MOUNTED_VOLUME : Errno : -14; -ERROR_PATH_NOT_FOUND : Errno : -15; -ERROR_FILE_EXISTS : Errno : -19; -ERROR_FILE_NOT_FOUND : Errno : -20; -ERROR_DRIVE_ERROR_FILE_DAMAGED : Errno : -21; -ERROR_ACCESS_NOT_WITHIN_FILE_BOUNDS : Errno : -22; -ERROR_ACCESS_DENIED : Errno : -23; -ERROR_FILE_IN_EXCLUSIVE_USE : Errno : -24; -ERROR_FILE_CANNOT_GET_EXCLUSIVE_USE : Errno : -25; -ERROR_INCORRECT_NODE_TYPE : Errno : -26; -ERROR_EVENT_NOT_SET : Errno : -27; -ERROR_TIMEOUT_REACHED : Errno : -29; -ERROR_REQUEST_CLOSED_BEFORE_COMPLETE : Errno : -30; -ERROR_NO_CHARACTER_AT_COORDINATE : Errno : -31; -ERROR_FILE_ON_READ_ONLY_VOLUME : Errno : -32; -ERROR_USER_CANCELED_IO : Errno : -33; -ERROR_DRIVE_CONTROLLER_REPORTED : Errno : -35; -ERROR_COULD_NOT_ISSUE_PACKET : Errno : -36; +// ERROR_NONE : Errno : -1 ; +// ERROR_UNKNOWN_OPERATION_FAILURE : Errno : -7 ; +// ERROR_PATH_NOT_WITHIN_MOUNTED_VOLUME : Errno : -14; +// ERROR_PATH_NOT_FOUND : Errno : -15; +// ERROR_FILE_EXISTS : Errno : -19; +// ERROR_FILE_NOT_FOUND : Errno : -20; +// ERROR_DRIVE_ERROR_FILE_DAMAGED : Errno : -21; +// ERROR_ACCESS_NOT_WITHIN_FILE_BOUNDS : Errno : -22; +// ERROR_ACCESS_DENIED : Errno : -23; +// ERROR_FILE_IN_EXCLUSIVE_USE : Errno : -24; +// ERROR_FILE_CANNOT_GET_EXCLUSIVE_USE : Errno : -25; +// ERROR_INCORRECT_NODE_TYPE : Errno : -26; +// ERROR_EVENT_NOT_SET : Errno : -27; +// ERROR_TIMEOUT_REACHED : Errno : -29; +// ERROR_REQUEST_CLOSED_BEFORE_COMPLETE : Errno : -30; +// ERROR_NO_CHARACTER_AT_COORDINATE : Errno : -31; +// ERROR_FILE_ON_READ_ONLY_VOLUME : Errno : -32; +// ERROR_USER_CANCELED_IO : Errno : -33; +// ERROR_DRIVE_CONTROLLER_REPORTED : Errno : -35; +// ERROR_COULD_NOT_ISSUE_PACKET : Errno : -36; -ERROR_NOT_IMPLEMENTED : Errno : 1; +// ERROR_NOT_IMPLEMENTED : Errno : 1; -OS_Node_Type :: enum i32 { - File = 0, - Directory = 1, -} +// OS_Node_Type :: enum i32 { +// File = 0, +// Directory = 1, +// } -OS_Node_Information :: struct { - handle: Handle, - id: [16]byte, - ntype: OS_Node_Type, - size: i64, +// OS_Node_Information :: struct { +// handle: Handle, +// id: [16]byte, +// ntype: OS_Node_Type, +// size: i64, - // Our additions... - position: i64, -} +// // Our additions... +// position: i64, +// } -foreign api { - @(link_name="OSPrintDirect") OSPrintDirect :: proc(str: ^u8, length: int) ---; - @(link_name="malloc") OSMalloc :: proc(bytes: int) -> rawptr ---; - @(link_name="free") OSFree :: proc(address: rawptr) ---; - @(link_name="OSOpenNode") OSOpenNode :: proc(path: ^u8, path_length: int, flags: u64, information: ^OS_Node_Information) -> Errno ---; - @(link_name="OSResizeFile") OSResizeFile :: proc(handle: Handle, new_size: u64) -> Errno ---; - @(link_name="OSCloseHandle") OSCloseHandle :: proc(handle: Handle) ---; - @(link_name="OSWriteFileSync") OSWriteFileSync :: proc(handle: Handle, offset: i64, size: i64, buffer: rawptr) -> i64 ---; - @(link_name="OSReadFileSync") OSReadFileSync :: proc(handle: Handle, offset: i64, size: i64, buffer: rawptr) -> i64 ---; - @(link_name="realloc") OSRealloc :: proc(address: rawptr, size: int) -> rawptr ---; - @(link_name="OSGetThreadID") OSGetThreadID :: proc(handle: Handle) -> int ---; - @(link_name="OSRefreshNodeInformation") OSRefreshNodeInformation :: proc(information: ^OS_Node_Information) ---; -} +// foreign api { +// @(link_name="OSPrintDirect") OSPrintDirect :: proc(str: ^u8, length: int) ---; +// @(link_name="malloc") OSMalloc :: proc(bytes: int) -> rawptr ---; +// @(link_name="free") OSFree :: proc(address: rawptr) ---; +// @(link_name="OSOpenNode") OSOpenNode :: proc(path: ^u8, path_length: int, flags: u64, information: ^OS_Node_Information) -> Errno ---; +// @(link_name="OSResizeFile") OSResizeFile :: proc(handle: Handle, new_size: u64) -> Errno ---; +// @(link_name="OSCloseHandle") OSCloseHandle :: proc(handle: Handle) ---; +// @(link_name="OSWriteFileSync") OSWriteFileSync :: proc(handle: Handle, offset: i64, size: i64, buffer: rawptr) -> i64 ---; +// @(link_name="OSReadFileSync") OSReadFileSync :: proc(handle: Handle, offset: i64, size: i64, buffer: rawptr) -> i64 ---; +// @(link_name="realloc") OSRealloc :: proc(address: rawptr, size: int) -> rawptr ---; +// @(link_name="OSGetThreadID") OSGetThreadID :: proc(handle: Handle) -> int ---; +// @(link_name="OSRefreshNodeInformation") OSRefreshNodeInformation :: proc(information: ^OS_Node_Information) ---; +// } -stdin := Handle(-1); // Not implemented -stdout := Handle(0); -stderr := Handle(0); +// stdin := Handle(-1); // Not implemented +// stdout := Handle(0); +// stderr := Handle(0); -current_thread_id :: proc() -> int { - return OSGetThreadID(Handle(0x1000)); -} +// current_thread_id :: proc() -> int { +// return OSGetThreadID(Handle(0x1000)); +// } -heap_alloc :: proc(size: int) -> rawptr { - return OSMalloc(size); -} +// heap_alloc :: proc(size: int) -> rawptr { +// return OSMalloc(size); +// } -heap_free :: proc(address: rawptr) { - OSFree(address); -} +// heap_free :: proc(address: rawptr) { +// OSFree(address); +// } -heap_resize :: proc(address: rawptr, new_size: int) -> rawptr { - return OSRealloc(address, new_size); -} +// heap_resize :: proc(address: rawptr, new_size: int) -> rawptr { +// return OSRealloc(address, new_size); +// } -open :: proc(path: string, mode: int = O_RDONLY, perm: u32 = 0) -> (Handle, Errno) { - flags : u64 = 0; +// open :: proc(path: string, mode: int = O_RDONLY, perm: u32 = 0) -> (Handle, Errno) { +// flags : u64 = 0; - if mode & O_CREATE == O_CREATE { - flags = flags | 0x9000; // Fail if found and create directories leading to the file if they don't exist - } else { - flags = flags | 0x2000; // Fail if not found - } +// if mode & O_CREATE == O_CREATE { +// flags = flags | 0x9000; // Fail if found and create directories leading to the file if they don't exist +// } else { +// flags = flags | 0x2000; // Fail if not found +// } - if mode & O_EXCL == O_EXCL { - flags = flags | 0x111; // Block opening the node for any reason - } +// if mode & O_EXCL == O_EXCL { +// flags = flags | 0x111; // Block opening the node for any reason +// } - if mode & O_RDONLY == O_RDONLY { - flags = flags | 0x2; // Read access - } +// if mode & O_RDONLY == O_RDONLY { +// flags = flags | 0x2; // Read access +// } - if mode & O_WRONLY == O_WRONLY { - flags = flags | 0x220; // Write and resize access - } +// if mode & O_WRONLY == O_WRONLY { +// flags = flags | 0x220; // Write and resize access +// } - if mode & O_TRUNC == O_TRUNC { - flags = flags | 0x200; // Resize access - } +// if mode & O_TRUNC == O_TRUNC { +// flags = flags | 0x200; // Resize access +// } - information := new(OS_Node_Information); - error := OSOpenNode(&path[0], len(path), flags, information); +// information := new(OS_Node_Information); +// error := OSOpenNode(&path[0], len(path), flags, information); - if error < ERROR_NONE { - free(information); - return 0, error; - } +// if error < ERROR_NONE { +// free(information); +// return 0, error; +// } - if mode & O_TRUNC == O_TRUNC { - error := OSResizeFile(information.handle, 0); - if error < ERROR_NONE do return 0, ERROR_UNKNOWN_OPERATION_FAILURE; - } +// if mode & O_TRUNC == O_TRUNC { +// error := OSResizeFile(information.handle, 0); +// if error < ERROR_NONE do return 0, ERROR_UNKNOWN_OPERATION_FAILURE; +// } - if mode & O_APPEND == O_APPEND { - information.position = information.size; - } else { - information.position = 0; - } +// if mode & O_APPEND == O_APPEND { +// information.position = information.size; +// } else { +// information.position = 0; +// } - return Handle(uintptr(information)), ERROR_NONE; -} +// return Handle(uintptr(information)), ERROR_NONE; +// } -close :: proc(fd: Handle) { - information := (^OS_Node_Information)(uintptr(fd)); - OSCloseHandle(information.handle); - free(information); -} +// close :: proc(fd: Handle) { +// information := (^OS_Node_Information)(uintptr(fd)); +// OSCloseHandle(information.handle); +// free(information); +// } -file_size :: proc(fd: Handle) -> (i64, Errno) { - x : OS_Node_Information; - OSRefreshNodeInformation(&x); - return x.size, ERROR_NONE; -} +// file_size :: proc(fd: Handle) -> (i64, Errno) { +// x : OS_Node_Information; +// OSRefreshNodeInformation(&x); +// return x.size, ERROR_NONE; +// } -write :: proc(fd: Handle, data: []byte) -> (int, Errno) { - if fd == 0 { - OSPrintDirect(&data[0], len(data)); - return len(data), ERROR_NONE; - } else if fd == 1 { - assert(false); - return 0, ERROR_NOT_IMPLEMENTED; - } +// write :: proc(fd: Handle, data: []byte) -> (int, Errno) { +// if fd == 0 { +// OSPrintDirect(&data[0], len(data)); +// return len(data), ERROR_NONE; +// } else if fd == 1 { +// assert(false); +// return 0, ERROR_NOT_IMPLEMENTED; +// } - information := (^OS_Node_Information)(uintptr(fd)); - count := OSWriteFileSync(information.handle, information.position, i64(len(data)), &data[0]); - if count < 0 do return 0, 1; - information.position += count; - return int(count), 0; -} +// information := (^OS_Node_Information)(uintptr(fd)); +// count := OSWriteFileSync(information.handle, information.position, i64(len(data)), &data[0]); +// if count < 0 do return 0, 1; +// information.position += count; +// return int(count), 0; +// } -read :: proc(fd: Handle, data: []byte) -> (int, Errno) { - if (fd == 0 || fd == 1) { - assert(false); - return 0, ERROR_NOT_IMPLEMENTED; - } +// read :: proc(fd: Handle, data: []byte) -> (int, Errno) { +// if (fd == 0 || fd == 1) { +// assert(false); +// return 0, ERROR_NOT_IMPLEMENTED; +// } - information := (^OS_Node_Information)(uintptr(fd)); - count := OSReadFileSync(information.handle, information.position, i64(len(data)), &data[0]); - if count < 0 do return 0, ERROR_UNKNOWN_OPERATION_FAILURE; - information.position += count; - return int(count), ERROR_NONE; -} +// information := (^OS_Node_Information)(uintptr(fd)); +// count := OSReadFileSync(information.handle, information.position, i64(len(data)), &data[0]); +// if count < 0 do return 0, ERROR_UNKNOWN_OPERATION_FAILURE; +// information.position += count; +// return int(count), ERROR_NONE; +// } diff --git a/core/os/os_windows.odin b/core/os/os_windows.odin index 4b776c7f4..aa0d73eb0 100644 --- a/core/os/os_windows.odin +++ b/core/os/os_windows.odin @@ -1,7 +1,7 @@ package os import "core:sys/win32" -import "core:mem" +// import "core:mem" Handle :: distinct uintptr; File_Time :: distinct u64; diff --git a/core/sys/win32/wgl.odin b/core/sys/win32/wgl.odin index 75d845406..1cc1e20fa 100644 --- a/core/sys/win32/wgl.odin +++ b/core/sys/win32/wgl.odin @@ -1,8 +1,8 @@ package win32 -when ODIN_OS == "windows" { +// when ODIN_OS == "windows" { foreign import "system:opengl32.lib" -} +// } CONTEXT_MAJOR_VERSION_ARB :: 0x2091; diff --git a/core/sys/win32/windows.odin b/core/sys/win32/windows.odin index 124674122..76b57979e 100644 --- a/core/sys/win32/windows.odin +++ b/core/sys/win32/windows.odin @@ -1,12 +1,12 @@ package win32 -when ODIN_OS == "windows" { +// when ODIN_OS == "windows" { foreign import "system:kernel32.lib" foreign import "system:user32.lib" foreign import "system:gdi32.lib" foreign import "system:winmm.lib" foreign import "system:shell32.lib" -} +// } Handle :: distinct rawptr; Hwnd :: distinct Handle; diff --git a/core/thread/thread.odin b/core/thread/thread.odin index 5c471cbfe..2289c6bc6 100644 --- a/core/thread/thread.odin +++ b/core/thread/thread.odin @@ -1,10 +1,7 @@ package thread #assert(ODIN_OS == "windows"); - -when ODIN_OS == "windows" { - import win32 "core:sys/windows.odin" -} +import "core:sys/win32" Thread_Proc :: #type proc(^Thread) -> int; diff --git a/core/unicode/utf16/utf16.odin b/core/unicode/utf16/utf16.odin index 44517820e..971290a1e 100644 --- a/core/unicode/utf16/utf16.odin +++ b/core/unicode/utf16/utf16.odin @@ -1,4 +1,4 @@ -package utf8 +package utf16 import "core:unicode/utf8" diff --git a/src/build_settings.cpp b/src/build_settings.cpp index c58eb9ed8..13776dedb 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1,3 +1,38 @@ +enum TargetOsKind { + TargetOs_Invalid, + + TargetOs_windows, + TargetOs_osx, + TargetOs_linux, + TargetOs_essence, + + TargetOs_COUNT, +}; + +enum TargetArchKind { + TargetArch_Invalid, + + TargetArch_amd64, + TargetArch_x64, + + TargetArch_COUNT, +}; + +String target_os_names[TargetOs_COUNT] = { + str_lit(""), + str_lit("windows"), + str_lit("osx"), + str_lit("linux"), + str_lit("essence"), +}; + +String target_arch_names[TargetArch_COUNT] = { + str_lit(""), + str_lit("amd64"), + str_lit("x86"), +}; + + // This stores the information for the specify architecture of this build struct BuildContext { // Constants @@ -15,6 +50,9 @@ struct BuildContext { String command; + TargetOsKind target_os; + TargetArchKind target_arch; + String out_filepath; String resource_filepath; bool has_resource; @@ -37,6 +75,72 @@ struct BuildContext { gb_global BuildContext build_context = {0}; +TargetOsKind get_target_os_from_string(String str) { + for (isize i = 0; i < TargetOs_COUNT; i++) { + if (target_os_names[i] == str) { + return cast(TargetOsKind)i; + } + } + return TargetOs_Invalid; +} + +TargetArchKind get_target_arch_from_string(String str) { + for (isize i = 0; i < TargetArch_COUNT; i++) { + if (target_os_names[i] == str) { + return cast(TargetArchKind)i; + } + } + return TargetArch_Invalid; +} + +bool is_excluded_target_filename(String name) { + String const ext = str_lit(".odin"); + GB_ASSERT(string_ends_with(name, ext)); + name = substring(name, 0, name.len-ext.len); + + String str1 = {}; + String str2 = {}; + isize n = 0; + + str1 = name; + n = str1.len; + for (isize i = str1.len-1; i >= 0 && str1[i] != '_'; i--) { + n -= 1; + } + str1 = substring(str1, n, str1.len); + + str2 = str1; + n = str2.len; + for (isize i = str2.len-1; i >= 0 && str2[i] != '_'; i--) { + n -= 1; + } + str2 = substring(str2, n, str2.len); + + if (str1 == name) { + return false; + } + + + TargetOsKind os1 = get_target_os_from_string(str1); + TargetArchKind arch1 = get_target_arch_from_string(str1); + TargetOsKind os2 = get_target_os_from_string(str2); + TargetArchKind arch2 = get_target_arch_from_string(str2); + + if (arch1 != TargetArch_Invalid && os2 != TargetOs_Invalid) { + return arch1 != build_context.target_arch || os2 != build_context.target_os; + } else if (arch1 != TargetArch_Invalid && os1 != TargetOs_Invalid) { + return arch2 != build_context.target_arch || os1 != build_context.target_os; + } else if (os1 != TargetOs_Invalid) { + return os1 != build_context.target_os; + } else if (arch1 != TargetArch_Invalid) { + return arch1 != build_context.target_arch; + } + + return false; +} + + + struct LibraryCollections { String name; String path; @@ -327,11 +431,14 @@ void init_build_context(void) { bc->ODIN_ROOT = odin_root_dir(); #if defined(GB_SYSTEM_WINDOWS) - bc->ODIN_OS = str_lit("windows"); + bc->ODIN_OS = str_lit("windows"); + bc->target_os = TargetOs_windows; #elif defined(GB_SYSTEM_OSX) - bc->ODIN_OS = str_lit("osx"); + bc->ODIN_OS = str_lit("osx"); + bc->target_os = TargetOs_osx; #else - bc->ODIN_OS = str_lit("linux"); + bc->ODIN_OS = str_lit("linux"); + bc->target_os = TargetOs_linux; #endif if (cross_compile_target.len) { @@ -340,8 +447,10 @@ void init_build_context(void) { #if defined(GB_ARCH_64_BIT) bc->ODIN_ARCH = str_lit("amd64"); + bc->target_arch = TargetArch_amd64; #else bc->ODIN_ARCH = str_lit("x86"); + bc->target_arch = TargetArch_x86; #endif { diff --git a/src/check_decl.cpp b/src/check_decl.cpp index f63aecf67..ff4fc6184 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -535,7 +535,7 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) { e->deprecated_message = ac.deprecated_message; ac.link_name = handle_link_name(c, e->token, ac.link_name, ac.link_prefix); - if (d->scope->file != nullptr && e->token.string == "main") { + if (d->scope->package != nullptr && e->token.string == "main") { if (pt->param_count != 0 || pt->result_count != 0) { gbString str = type_to_string(proc_type); @@ -583,7 +583,7 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) { GB_ASSERT(pl->body->kind == AstNode_BlockStmt); if (!pt->is_polymorphic) { - check_procedure_later(c, c->curr_ast_file, e->token, d, proc_type, pl->body, pl->tags); + check_procedure_later(c, c->curr_ast_package, e->token, d, proc_type, pl->body, pl->tags); } } else if (!is_foreign) { if (e->Procedure.is_export) { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index ab9a42e6f..34337ce0e 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -109,7 +109,7 @@ void error_operand_no_value(Operand *o) { void check_scope_decls(Checker *c, Array nodes, isize reserve_size) { Scope *s = c->context.scope; - GB_ASSERT(s->file == nullptr); + GB_ASSERT(s->package == nullptr); check_collect_entities(c, nodes); @@ -342,17 +342,18 @@ bool find_or_generate_polymorphic_procedure(Checker *c, Entity *base_entity, Typ // NOTE(bill): Set the scope afterwards as this is not real overloading entity->scope = scope->parent; - AstFile *file = nullptr; + AstPackage *package = nullptr; { Scope *s = entity->scope; - while (s != nullptr && s->file == nullptr) { - file = s->file; + while (s != nullptr && s->package == nullptr) { + package = s->package; s = s->parent; } } ProcedureInfo proc_info = {}; - proc_info.file = file; + // proc_info.file = file; + proc_info.package = package; proc_info.token = token; proc_info.decl = d; proc_info.type = final_proc_type; @@ -5362,7 +5363,7 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t return kind; } - check_procedure_later(c, c->curr_ast_file, empty_token, decl, type, pl->body, pl->tags); + check_procedure_later(c, c->curr_ast_package, empty_token, decl, type, pl->body, pl->tags); } check_close_scope(c); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 54226e565..c0e885664 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -627,7 +627,10 @@ void check_switch_stmt(Checker *c, AstNode *node, u32 mod_flags) { Token token = {}; token.pos = ast_node_token(ss->body).pos; token.string = str_lit("true"); - x.expr = ast_ident(c->curr_ast_file, token); + + x.expr = gb_alloc_item(c->allocator, AstNode); + x.expr->kind = AstNode_Ident; + x.expr->Ident.token = token; } // NOTE(bill): Check for multiple defaults diff --git a/src/checker.cpp b/src/checker.cpp index 0a3c09109..429468c08 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -122,9 +122,9 @@ void import_graph_node_set_remove(ImportGraphNodeSet *s, ImportGraphNode *n) { ImportGraphNode *import_graph_node_create(gbAllocator a, Scope *scope) { ImportGraphNode *n = gb_alloc_item(a, ImportGraphNode); - n->scope = scope; - n->path = scope->file->tokenizer.fullpath; - n->file_id = scope->file->id; + n->scope = scope; + n->path = scope->package->fullpath; + n->package_id = scope->package->id; return n; } @@ -145,7 +145,7 @@ int import_graph_node_cmp(ImportGraphNode **data, isize i, isize j) { bool xg = x->scope->is_global; bool yg = y->scope->is_global; if (xg != yg) return xg ? -1 : +1; - if (xg && yg) return x->file_id < y->file_id ? +1 : -1; + if (xg && yg) return x->package_id < y->package_id ? +1 : -1; if (x->dep_count < y->dep_count) return -1; if (x->dep_count > y->dep_count) return +1; return 0; @@ -222,31 +222,61 @@ Scope *create_scope(Scope *parent, gbAllocator allocator) { ptr_set_init(&s->imported, heap_allocator()); ptr_set_init(&s->exported, heap_allocator()); + s->delayed_imports.allocator = heap_allocator(); + s->delayed_asserts.allocator = heap_allocator(); + if (parent != nullptr && parent != universal_scope) { DLIST_APPEND(parent->first_child, parent->last_child, s); } return s; } -Scope *create_scope_from_file(Checker *c, AstFile *f) { - GB_ASSERT(f != nullptr); +// Scope *create_scope_from_file(Checker *c, AstFile *f) { +// GB_ASSERT(f != nullptr); + +// Scope *s = create_scope(c->global_scope, c->allocator); + + +// s->file = f; +// f->scope = s; +// s->is_file = true; + +// if (f->tokenizer.fullpath == c->parser->init_fullpath) { +// s->is_init = true; +// } else { +// s->is_init = f->package->kind == ImportedPackage_Init; +// } + +// s->is_global = f->is_global_scope; +// if (s->is_global) array_add(&c->global_scope->shared, s); + + +// if (s->is_init || s->is_global) { +// s->has_been_imported = true; +// } + +// return s; +// } + +Scope *create_scope_from_package(Checker *c, AstPackage *p) { + GB_ASSERT(p != nullptr); Scope *s = create_scope(c->global_scope, c->allocator); - array_init(&s->delayed_file_decls, heap_allocator()); + s->is_package = true; + s->package = p; + p->scope = s; - s->file = f; - f->scope = s; - s->is_file = true; - - if (f->tokenizer.fullpath == c->parser->init_fullpath) { + if (p->fullpath == c->parser->init_fullpath) { s->is_init = true; } else { - s->is_init = f->file_kind == ImportedFile_Init; + s->is_init = p->kind == ImportedPackage_Init; } - s->is_global = f->is_global_scope; - if (s->is_global) array_add(&c->global_scope->shared, s); + s->is_global = p->kind == ImportedPackage_Runtime; + if (s->is_global) { + array_add(&c->global_scope->shared, s); + } if (s->is_init || s->is_global) { @@ -274,7 +304,7 @@ void destroy_scope(Scope *scope) { map_destroy(&scope->elements); array_free(&scope->shared); - array_free(&scope->delayed_file_decls); + array_free(&scope->delayed_imports); array_free(&scope->delayed_asserts); ptr_set_destroy(&scope->implicit); ptr_set_destroy(&scope->imported); @@ -330,7 +360,7 @@ Entity *current_scope_lookup_entity(Scope *s, String name) { if (found) { Entity *e = *found; if (e->kind == Entity_Variable && - !e->scope->is_file && + !e->scope->is_package && !e->scope->is_global) { continue; } @@ -348,7 +378,7 @@ Entity *current_scope_lookup_entity(Scope *s, String name) { void scope_lookup_parent_entity(Scope *scope, String name, Scope **scope_, Entity **entity_) { bool gone_thru_proc = false; - bool gone_thru_file = false; + bool gone_thru_package = false; HashKey key = hash_string(name); for (Scope *s = scope; s != nullptr; s = s->parent) { Entity **found = map_get(&s->elements, key); @@ -360,7 +390,7 @@ void scope_lookup_parent_entity(Scope *scope, String name, Scope **scope_, Entit continue; } if (e->kind == Entity_Variable && - !e->scope->is_file && + !e->scope->is_package && !e->scope->is_global) { continue; } @@ -381,7 +411,7 @@ void scope_lookup_parent_entity(Scope *scope, String name, Scope **scope_, Entit if (found) { Entity *e = *found; if (e->kind == Entity_Variable && - !e->scope->is_file && + !e->scope->is_package && !e->scope->is_global) { continue; } @@ -393,7 +423,7 @@ void scope_lookup_parent_entity(Scope *scope, String name, Scope **scope_, Entit if ((e->kind == Entity_ImportName || e->kind == Entity_LibraryName) - && gone_thru_file) { + && gone_thru_package) { continue; } @@ -404,8 +434,8 @@ void scope_lookup_parent_entity(Scope *scope, String name, Scope **scope_, Entit } } - if (s->is_file) { - gone_thru_file = true; + if (s->is_package) { + gone_thru_package = true; } } @@ -607,6 +637,7 @@ void init_checker_info(CheckerInfo *i) { array_init(&i->type_info_types, a); map_init(&i->type_info_map, a); map_init(&i->files, a); + map_init(&i->packages, a); array_init(&i->variable_init_order, a); } @@ -621,6 +652,7 @@ void destroy_checker_info(CheckerInfo *i) { array_free(&i->type_info_types); map_destroy(&i->type_info_map); map_destroy(&i->files); + map_destroy(&i->packages); array_free(&i->variable_init_order); } @@ -640,11 +672,7 @@ void init_checker(Checker *c, Parser *parser) { // NOTE(bill): Is this big enough or too small? isize item_size = gb_max3(gb_size_of(Entity), gb_size_of(Type), gb_size_of(Scope)); - isize total_token_count = 0; - for_array(i, c->parser->files) { - AstFile *f = c->parser->files[i]; - total_token_count += f->tokens.count; - } + isize total_token_count = c->parser->total_token_count; isize arena_size = 2 * item_size * total_token_count; gb_arena_init_from_allocator(&c->tmp_arena, a, arena_size); gb_arena_init_from_allocator(&c->arena, a, arena_size); @@ -654,12 +682,15 @@ void init_checker(Checker *c, Parser *parser) { // c->allocator = gb_arena_allocator(&c->arena); c->tmp_allocator = gb_arena_allocator(&c->tmp_arena); + GB_ASSERT(universal_scope != nullptr); c->global_scope = create_scope(universal_scope, c->allocator); - map_init(&c->file_scopes, heap_allocator()); - ptr_set_init(&c->checked_files, heap_allocator()); + GB_ASSERT(scope_lookup_entity(c->global_scope, str_lit("ODIN_OS")) != nullptr); - array_init(&c->file_order, heap_allocator(), 0, c->parser->files.count); + + map_init(&c->package_scopes, heap_allocator()); + + array_init(&c->package_order, heap_allocator(), 0, c->parser->packages.count); // Init context c->context.scope = c->global_scope; @@ -678,9 +709,8 @@ void destroy_checker(Checker *c) { gb_arena_free(&c->tmp_arena); - map_destroy(&c->file_scopes); - ptr_set_destroy(&c->checked_files); - array_free(&c->file_order); + map_destroy(&c->package_scopes); + array_free(&c->package_order); destroy_checker_type_path(c->context.type_path); } @@ -1110,9 +1140,9 @@ void check_procedure_later(Checker *c, ProcedureInfo info) { array_add(&c->procs, info); } -void check_procedure_later(Checker *c, AstFile *file, Token token, DeclInfo *decl, Type *type, AstNode *body, u64 tags) { +void check_procedure_later(Checker *c, AstPackage *package, Token token, DeclInfo *decl, Type *type, AstNode *body, u64 tags) { ProcedureInfo info = {}; - info.file = file; + info.package = package; info.token = token; info.decl = decl; info.type = type; @@ -1137,14 +1167,14 @@ Type *const curr_procedure_type(Checker *c) { return nullptr; } -void add_curr_ast_file(Checker *c, AstFile *file) { - if (file != nullptr) { +void add_curr_ast_package(Checker *c, AstPackage *package) { + if (package != nullptr) { TokenPos zero_pos = {}; global_error_collector.prev = zero_pos; - c->curr_ast_file = file; - c->context.decl = file->decl_info; - c->context.scope = file->scope; - c->context.file_scope = file->scope; + c->curr_ast_package = package; + c->context.decl = package->decl_info; + c->context.scope = package->scope; + c->context.package_scope = package->scope; } } @@ -1928,7 +1958,7 @@ void check_collect_value_decl(Checker *c, AstNode *decl) { vd->been_handled = true; if (vd->is_mutable) { - if (!c->context.scope->is_file) { + if (!c->context.scope->is_package) { // NOTE(bill): local scope -> handle later and in order return; } @@ -2109,7 +2139,6 @@ void check_add_foreign_block_decl(Checker *c, AstNode *decl) { check_decl_attributes(c, fb->attributes, foreign_block_decl_attribute, nullptr); - c->context.collect_delayed_decls = true; check_collect_entities(c, fb->decls); c->context = prev_context; } @@ -2135,31 +2164,17 @@ void check_collect_entities(Checker *c, Array nodes) { case_end; case_ast_node(id, ImportDecl, decl); - if (!c->context.scope->is_file) { + if (!c->context.scope->is_package) { error(decl, "import declarations are only allowed in the file scope"); // NOTE(bill): _Should_ be caught by the parser // TODO(bill): Better error handling if it isn't continue; } - if (c->context.collect_delayed_decls) { - check_add_import_decl(c, id); - } - case_end; - - case_ast_node(ed, ExportDecl, decl); - if (!c->context.scope->is_file) { - error(decl, "export declarations are only allowed in the file scope"); - // NOTE(bill): _Should_ be caught by the parser - // TODO(bill): Better error handling if it isn't - continue; - } - if (c->context.collect_delayed_decls) { - check_add_export_decl(c, ed); - } + array_add(&c->context.scope->delayed_imports, decl); case_end; case_ast_node(fl, ForeignImportDecl, decl); - if (!c->context.scope->is_file) { + if (!c->context.scope->is_package) { error(decl, "%.*s declarations are only allowed in the file scope", LIT(fl->token.string)); // NOTE(bill): _Should_ be caught by the parser // TODO(bill): Better error handling if it isn't @@ -2172,8 +2187,20 @@ void check_collect_entities(Checker *c, Array nodes) { check_add_foreign_block_decl(c, decl); case_end; + + case_ast_node(ce, CallExpr, decl); + if (c->context.scope->is_package && + ce->proc->kind == AstNode_BasicDirective && + ce->proc->BasicDirective.name == "assert") { + array_add(&c->context.scope->delayed_asserts, decl); + } else { + goto error_case; + } + case_end; + + error_case: default: - if (c->context.scope->is_file) { + if (c->context.scope->is_package) { error(decl, "Only declarations are allowed at file scope"); } break; @@ -2182,7 +2209,7 @@ void check_collect_entities(Checker *c, Array nodes) { // NOTE(bill): 'when' stmts need to be handled after the other as the condition may refer to something // declared after this stmt in source - if (!c->context.scope->is_file || c->context.collect_delayed_decls) { + if (!c->context.scope->is_package) { for_array(i, nodes) { AstNode *node = nodes[i]; switch (node->kind) { @@ -2214,8 +2241,8 @@ void check_all_global_entities(Checker *c) { } - AstFile *file = d->scope->file; - add_curr_ast_file(c, file); + AstPackage *package = d->scope->package; + add_curr_ast_package(c, package); if (e->token.string == "main") { if (e->kind != Entity_Procedure) { @@ -2229,6 +2256,7 @@ void check_all_global_entities(Checker *c) { } } + CheckerContext prev_context = c->context; c->context.decl = d; c->context.scope = d->scope; @@ -2311,19 +2339,20 @@ String path_to_entity_name(String name, String fullpath) { +#if 1 void add_import_dependency_node(Checker *c, AstNode *decl, Map *M) { - Scope *parent_file_scope = decl->file->scope; + Scope *parent_package_scope = decl->file->package->scope; switch (decl->kind) { case_ast_node(id, ImportDecl, decl); String path = id->fullpath; HashKey key = hash_string(path); - Scope **found = map_get(&c->file_scopes, key); + Scope **found = map_get(&c->package_scopes, key); if (found == nullptr) { - for_array(scope_index, c->file_scopes.entries) { - Scope *scope = c->file_scopes.entries[scope_index].value; - gb_printf_err("%.*s\n", LIT(scope->file->tokenizer.fullpath)); + for_array(scope_index, c->package_scopes.entries) { + Scope *scope = c->package_scopes.entries[scope_index].value; + gb_printf_err("%.*s\n", LIT(scope->package->fullpath)); } Token token = ast_node_token(decl); gb_printf_err("%.*s(%td:%td)\n", LIT(token.pos.file), token.pos.line, token.pos.column); @@ -2332,7 +2361,7 @@ void add_import_dependency_node(Checker *c, AstNode *decl, Mapfile = scope->file; + id->package = scope->package; ImportGraphNode **found_node = nullptr; ImportGraphNode *m = nullptr; @@ -2342,7 +2371,7 @@ void add_import_dependency_node(Checker *c, AstNode *decl, Mapfullpath; - HashKey key = hash_string(path); - Scope **found = map_get(&c->file_scopes, key); - if (found == nullptr) { - for_array(scope_index, c->file_scopes.entries) { - Scope *scope = c->file_scopes.entries[scope_index].value; - gb_printf_err("%.*s\n", LIT(scope->file->tokenizer.fullpath)); - } - Token token = ast_node_token(decl); - gb_printf_err("%.*s(%td:%td)\n", LIT(token.pos.file), token.pos.line, token.pos.column); - GB_PANIC("Unable to find scope for file: %.*s", LIT(path)); - } - Scope *scope = *found; - GB_ASSERT(scope != nullptr); - ed->file = scope->file; + // case_ast_node(ed, ExportDecl, decl); + // String path = ed->fullpath; + // HashKey key = hash_string(path); + // Scope **found = map_get(&c->file_scopes, key); + // if (found == nullptr) { + // for_array(scope_index, c->file_scopes.entries) { + // Scope *scope = c->file_scopes.entries[scope_index].value; + // gb_printf_err("%.*s\n", LIT(scope->file->tokenizer.fullpath)); + // } + // Token token = ast_node_token(decl); + // gb_printf_err("%.*s(%td:%td)\n", LIT(token.pos.file), token.pos.line, token.pos.column); + // GB_PANIC("Unable to find scope for file: %.*s", LIT(path)); + // } + // Scope *scope = *found; + // GB_ASSERT(scope != nullptr); + // ed->file = scope->file; - ImportGraphNode **found_node = nullptr; - ImportGraphNode *m = nullptr; - ImportGraphNode *n = nullptr; + // ImportGraphNode **found_node = nullptr; + // ImportGraphNode *m = nullptr; + // ImportGraphNode *n = nullptr; - found_node = map_get(M, hash_pointer(scope)); - GB_ASSERT(found_node != nullptr); - m = *found_node; + // found_node = map_get(M, hash_pointer(scope)); + // GB_ASSERT(found_node != nullptr); + // m = *found_node; - found_node = map_get(M, hash_pointer(parent_file_scope)); - GB_ASSERT(found_node != nullptr); - n = *found_node; + // found_node = map_get(M, hash_pointer(parent_package_scope)); + // GB_ASSERT(found_node != nullptr); + // n = *found_node; - import_graph_node_set_add(&n->succ, m); - import_graph_node_set_add(&m->pred, n); - ptr_set_add(&m->scope->exported, n->scope); - case_end; + // import_graph_node_set_add(&n->succ, m); + // import_graph_node_set_add(&m->pred, n); + // ptr_set_add(&m->scope->exported, n->scope); + // case_end; case_ast_node(ws, WhenStmt, decl); if (ws->body != nullptr) { @@ -2417,7 +2446,6 @@ void add_import_dependency_node(Checker *c, AstNode *decl, Map generate_import_dependency_graph(Checker *c) { gbAllocator a = heap_allocator(); @@ -2425,19 +2453,22 @@ Array generate_import_dependency_graph(Checker *c) { map_init(&M, a); defer (map_destroy(&M)); - for_array(i, c->parser->files) { - Scope *scope = c->parser->files[i]->scope; + for_array(i, c->parser->packages) { + Scope *scope = c->parser->packages[i]->scope; ImportGraphNode *n = import_graph_node_create(heap_allocator(), scope); map_set(&M, hash_pointer(scope), n); } // Calculate edges for graph M - for_array(i, c->parser->files) { - AstFile *f = c->parser->files[i]; - for_array(j, f->decls) { - AstNode *decl = f->decls[j]; - add_import_dependency_node(c, decl, &M); + for_array(i, c->parser->packages) { + AstPackage *p = c->parser->packages[i]; + for_array(j, p->files.entries) { + AstFile *f = p->files.entries[j].value; + for_array(k, f->decls) { + AstNode *decl = f->decls[k]; + add_import_dependency_node(c, decl, &M); + } } } @@ -2472,66 +2503,68 @@ Array find_import_path(Checker *c, Scope *start, Scope *end, Ptr ptr_set_add(visited, start); - String path = start->file->tokenizer.fullpath; + String path = start->package->fullpath; HashKey key = hash_string(path); - Scope **found = map_get(&c->file_scopes, key); - if (found) { - AstFile *f = (*found)->file; - GB_ASSERT(f != nullptr); + Scope **found = map_get(&c->package_scopes, key); + // if (found) { + // AstPackage *p = (*found)->package; + // GB_ASSERT(p != nullptr); - for_array(i, f->imports_and_exports) { - Scope *s = nullptr; - AstNode *decl = f->imports_and_exports[i]; - if (decl->kind == AstNode_ExportDecl) { - s = decl->ExportDecl.file->scope; - } else if (decl->kind == AstNode_ImportDecl) { - if (!decl->ImportDecl.is_using) { - // continue; - } - s = decl->ImportDecl.file->scope; - } else { - continue; - } - GB_ASSERT(s != nullptr); + // for_array(i, f->imports_and_exports) { + // Scope *s = nullptr; + // AstNode *decl = f->imports_and_exports[i]; + // /* if (decl->kind == AstNode_ExportDecl) { + // s = decl->ExportDecl.file->scope; + // } else */ + // if (decl->kind == AstNode_ImportDecl) { + // if (!decl->ImportDecl.is_using) { + // // continue; + // } + // s = decl->ImportDecl.package->scope; + // } else { + // continue; + // } + // GB_ASSERT(s != nullptr); - ImportPathItem item = {s, decl}; - if (s == end) { - auto path = array_make(heap_allocator()); - array_add(&path, item); - return path; - } - auto next_path = find_import_path(c, s, end, visited); - if (next_path.count > 0) { - array_add(&next_path, item); - return next_path; - } - } - } + // ImportPathItem item = {s, decl}; + // if (s == end) { + // auto path = array_make(heap_allocator()); + // array_add(&path, item); + // return path; + // } + // auto next_path = find_import_path(c, s, end, visited); + // if (next_path.count > 0) { + // array_add(&next_path, item); + // return next_path; + // } + // } + // } return empty_path; } - +#endif void check_add_import_decl(Checker *c, AstNodeImportDecl *id) { if (id->been_handled) return; id->been_handled = true; Scope *parent_scope = c->context.scope; - GB_ASSERT(parent_scope->is_file); + GB_ASSERT(parent_scope->is_package); Token token = id->relpath; HashKey key = hash_string(id->fullpath); - Scope **found = map_get(&c->file_scopes, key); + Scope **found = map_get(&c->package_scopes, key); if (found == nullptr) { - for_array(scope_index, c->file_scopes.entries) { - Scope *scope = c->file_scopes.entries[scope_index].value; - gb_printf_err("%.*s\n", LIT(scope->file->tokenizer.fullpath)); + for_array(scope_index, c->package_scopes.entries) { + Scope *scope = c->package_scopes.entries[scope_index].value; + gb_printf_err("%.*s\n", LIT(scope->package->fullpath)); } gb_printf_err("%.*s(%td:%td)\n", LIT(token.pos.file), token.pos.line, token.pos.column); - GB_PANIC("Unable to find scope for file: %.*s", LIT(id->fullpath)); + GB_PANIC("Unable to find scope for package: %.*s", LIT(id->fullpath)); } Scope *scope = *found; + GB_ASSERT(scope->is_package && scope->package != nullptr); if (scope->is_global) { - error(token, "Importing a #shared_global_scope is disallowed and unnecessary"); + error(token, "Importing a runtime package is disallowed and unnecessary"); return; } @@ -2543,12 +2576,15 @@ void check_add_import_decl(Checker *c, AstNodeImportDecl *id) { if (id->using_in_list.count == 0) { - String import_name = path_to_entity_name(id->import_name.string, id->fullpath); + String import_name = id->import_name.string; + if (import_name.len == 0) { + import_name = scope->package->name; + } if (is_blank_ident(import_name)) { if (id->is_using) { // TODO(bill): Should this be a warning? } else { - error(token, "File name, %.*s, cannot be use as an import name as it is not a valid identifier", LIT(id->import_name.string)); + error(token, "Import name, %.*s, cannot be use as an import name as it is not a valid identifier", LIT(id->import_name.string)); } } else { GB_ASSERT(id->import_name.pos.line != 0); @@ -2563,12 +2599,13 @@ void check_add_import_decl(Checker *c, AstNodeImportDecl *id) { if (id->is_using) { if (parent_scope->is_global) { - error(id->import_name, "#shared_global_scope imports cannot use using"); + error(id->import_name, "runtime package imports cannot use using"); return; } // NOTE(bill): Add imported entities to this file's scope if (id->using_in_list.count > 0) { + for_array(list_index, id->using_in_list) { AstNode *node = id->using_in_list[list_index]; ast_node(ident, Ident, node); @@ -2583,7 +2620,9 @@ void check_add_import_decl(Checker *c, AstNodeImportDecl *id) { } continue; } - if (e->scope == parent_scope) continue; + if (e->scope == parent_scope) { + continue; + } bool implicit_is_found = ptr_set_exists(&scope->implicit, e); if (is_entity_exported(e) && !implicit_is_found) { @@ -2610,94 +2649,10 @@ void check_add_import_decl(Checker *c, AstNodeImportDecl *id) { } } } - ptr_set_add(&c->checked_files, scope->file); + scope->has_been_imported = true; } -void check_add_export_decl(Checker *c, AstNodeExportDecl *ed) { - if (ed->been_handled) return; - ed->been_handled = true; - - Scope *parent_scope = c->context.scope; - GB_ASSERT(parent_scope->is_file); - - Token token = ed->relpath; - HashKey key = hash_string(ed->fullpath); - Scope **found = map_get(&c->file_scopes, key); - if (found == nullptr) { - for_array(scope_index, c->file_scopes.entries) { - Scope *scope = c->file_scopes.entries[scope_index].value; - gb_printf_err("%.*s\n", LIT(scope->file->tokenizer.fullpath)); - } - gb_printf_err("%.*s(%td:%td)\n", LIT(token.pos.file), token.pos.line, token.pos.column); - GB_PANIC("Unable to find scope for file: %.*s", LIT(ed->fullpath)); - } - Scope *scope = *found; - - if (scope->is_global) { - error(token, "Exporting a #shared_global_scope is disallowed and unnecessary"); - return; - } - - if (parent_scope->is_global) { - error(ed->token, "'export' cannot be used on #shared_global_scope"); - return; - } - - if (ptr_set_exists(&parent_scope->imported, scope)) { - // error(token, "Multiple import of the same file within this scope"); - } else { - ptr_set_add(&parent_scope->imported, scope); - } - - if (ed->using_in_list.count > 0) { - for_array(list_index, ed->using_in_list) { - AstNode *node = ed->using_in_list[list_index]; - ast_node(ident, Ident, node); - String name = ident->token.string; - - Entity *e = scope_lookup_entity(scope, name); - if (e == nullptr) { - if (is_blank_ident(name)) { - error(node, "'_' cannot be used as a value"); - } else { - error(node, "Undeclared name in this importation: '%.*s'", LIT(name)); - } - continue; - } - if (e->scope == parent_scope) continue; - - if (is_entity_exported(e)) { - add_entity(c, parent_scope, e->identifier, e); - } else { - error(node, "'%.*s' is exported from this scope", LIT(name)); - continue; - } - } - } else { - // NOTE(bill): Add imported entities to this file's scope - for_array(elem_index, scope->elements.entries) { - Entity *e = scope->elements.entries[elem_index].value; - String name = e->token.string; - if (e->scope == parent_scope) continue; - DeclInfo *d = e->decl_info; - // NOTE(bill): The enum #export must be evaluated before use - if (d != nullptr && d->init_expr != nullptr) { - AstNode *expr = unparen_expr(d->init_expr); - if (expr->kind == AstNode_EnumType && expr->EnumType.is_export) { - check_entity_decl(c, e, d, nullptr); - } - } - - if (is_entity_kind_exported(e->kind)) { - add_entity(c, parent_scope, e->identifier, e); - } - } - } - - ptr_set_add(&c->checked_files, scope->file); - scope->has_been_imported = true; -} void check_add_foreign_import_decl(Checker *c, AstNode *decl) { ast_node(fl, ForeignImportDecl, decl); @@ -2706,7 +2661,7 @@ void check_add_foreign_import_decl(Checker *c, AstNode *decl) { fl->been_handled = true; Scope *parent_scope = c->context.scope; - GB_ASSERT(parent_scope->is_file); + GB_ASSERT(parent_scope->is_package); String fullpath = fl->fullpath; String library_name = path_to_entity_name(fl->library_name.string, fullpath); @@ -2743,206 +2698,6 @@ void check_add_foreign_import_decl(Checker *c, AstNode *decl) { } -bool collect_checked_files_from_import_decl_list(Checker *c, Array decls) { - bool new_files = false; - for_array(i, decls) { - AstNode *decl = decls[i]; - switch (decl->kind) { - case_ast_node(id, ImportDecl, decl); - HashKey key = hash_string(id->fullpath); - Scope **found = map_get(&c->file_scopes, key); - if (found == nullptr) continue; - Scope *s = *found; - if (!ptr_set_exists(&c->checked_files, s->file)) { - new_files = true; - ptr_set_add(&c->checked_files, s->file); - } - case_end; - - case_ast_node(ed, ExportDecl, decl); - HashKey key = hash_string(ed->fullpath); - Scope **found = map_get(&c->file_scopes, key); - if (found == nullptr) continue; - Scope *s = *found; - if (!ptr_set_exists(&c->checked_files, s->file)) { - new_files = true; - ptr_set_add(&c->checked_files, s->file); - } - case_end; - } - } - return new_files; -} - - -bool collect_checked_files_from_when_stmt(Checker *c, AstNodeWhenStmt *ws) { - Operand operand = {Addressing_Invalid}; - if (!ws->is_cond_determined) { - check_expr(c, &operand, ws->cond); - if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) { - error(ws->cond, "Non-boolean condition in 'when' statement"); - } - if (operand.mode != Addressing_Constant) { - error(ws->cond, "Non-constant condition in 'when' statement"); - } - - ws->is_cond_determined = true; - ws->determined_cond = operand.value.kind == ExactValue_Bool && operand.value.value_bool; - } - - if (ws->body == nullptr || ws->body->kind != AstNode_BlockStmt) { - error(ws->cond, "Invalid body for 'when' statement"); - } else { - if (ws->determined_cond) { - return collect_checked_files_from_import_decl_list(c, ws->body->BlockStmt.stmts); - } else if (ws->else_stmt) { - switch (ws->else_stmt->kind) { - case AstNode_BlockStmt: - return collect_checked_files_from_import_decl_list(c, ws->else_stmt->BlockStmt.stmts); - case AstNode_WhenStmt: - return collect_checked_files_from_when_stmt(c, &ws->else_stmt->WhenStmt); - default: - error(ws->else_stmt, "Invalid 'else' statement in 'when' statement"); - break; - } - } - } - - return false; -} - -void check_delayed_file_import_entity(Checker *c, AstNode *decl) { - Scope *parent_scope = c->context.scope; - GB_ASSERT(parent_scope->is_file); - - switch (decl->kind) { - case_ast_node(ws, WhenStmt, decl); - check_collect_entities_from_when_stmt(c, ws); - case_end; - - case_ast_node(id, ImportDecl, decl); - check_add_import_decl(c, id); - case_end; - - case_ast_node(ed, ExportDecl, decl); - check_add_export_decl(c, ed); - case_end; - - case_ast_node(fl, ForeignImportDecl, decl); - check_add_foreign_import_decl(c, decl); - case_end; - } -} - - -// NOTE(bill): Returns true if a new file is present -bool collect_file_decls(Checker *c, Array decls); -bool collect_file_decls_from_when_stmt(Checker *c, AstNodeWhenStmt *ws); - -bool collect_file_decls_from_when_stmt(Checker *c, AstNodeWhenStmt *ws) { - Operand operand = {Addressing_Invalid}; - if (!ws->is_cond_determined) { - check_expr(c, &operand, ws->cond); - if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) { - error(ws->cond, "Non-boolean condition in 'when' statement"); - } - if (operand.mode != Addressing_Constant) { - error(ws->cond, "Non-constant condition in 'when' statement"); - } - - ws->is_cond_determined = true; - ws->determined_cond = operand.value.kind == ExactValue_Bool && operand.value.value_bool; - } - - if (ws->body == nullptr || ws->body->kind != AstNode_BlockStmt) { - error(ws->cond, "Invalid body for 'when' statement"); - } else { - if (ws->determined_cond) { - return collect_file_decls(c, ws->body->BlockStmt.stmts); - } else if (ws->else_stmt) { - switch (ws->else_stmt->kind) { - case AstNode_BlockStmt: - return collect_file_decls(c, ws->else_stmt->BlockStmt.stmts); - case AstNode_WhenStmt: - return collect_file_decls_from_when_stmt(c, &ws->else_stmt->WhenStmt); - default: - error(ws->else_stmt, "Invalid 'else' statement in 'when' statement"); - break; - } - } - } - - return false; -} - -bool collect_file_decls(Checker *c, Array decls) { - Scope *parent_scope = c->context.scope; - GB_ASSERT(parent_scope->is_file); - - if (collect_checked_files_from_import_decl_list(c, decls)) { - return true; - } - - for_array(i, decls) { - AstNode *decl = decls[i]; - switch (decl->kind) { - case_ast_node(vd, ValueDecl, decl); - check_collect_value_decl(c, decl); - case_end; - - case_ast_node(id, ImportDecl, decl); - check_add_import_decl(c, id); - case_end; - - case_ast_node(ed, ExportDecl, decl); - check_add_export_decl(c, ed); - case_end; - - case_ast_node(fl, ForeignImportDecl, decl); - check_add_foreign_import_decl(c, decl); - case_end; - - case_ast_node(fb, ForeignBlockDecl, decl); - check_add_foreign_block_decl(c, decl); - case_end; - - case_ast_node(ws, WhenStmt, decl); - if (!ws->is_cond_determined) { - if (collect_checked_files_from_when_stmt(c, ws)) { - return true; - } - - CheckerContext prev_context = c->context; - defer (c->context = prev_context); - c->context.collect_delayed_decls = true; - - if (collect_file_decls_from_when_stmt(c, ws)) { - return true; - } - } else { - - CheckerContext prev_context = c->context; - defer (c->context = prev_context); - c->context.collect_delayed_decls = true; - - if (collect_file_decls_from_when_stmt(c, ws)) { - return true; - } - } - case_end; - - case_ast_node(ce, CallExpr, decl); - if (ce->proc->kind == AstNode_BasicDirective && - ce->proc->BasicDirective.name == "assert") { - Operand o = {}; - check_expr(c, &o, decl); - } - case_end; - } - } - - return false; -} void check_import_entities(Checker *c) { Array dep_graph = generate_import_dependency_graph(c); @@ -2960,6 +2715,7 @@ void check_import_entities(Checker *c) { ptr_set_init(&emitted, heap_allocator()); defer (ptr_set_destroy(&emitted)); + while (pq.queue.count > 0) { ImportGraphNode *n = priority_queue_pop(&pq); @@ -2976,7 +2732,8 @@ void check_import_entities(Checker *c) { // TODO(bill): This needs better TokenPos finding auto const fn = [](ImportPathItem item) -> String { Scope *s = item.scope; - return remove_directory_from_path(s->file->tokenizer.fullpath); + // return remove_directory_from_path(s->package->fullpath); + return s->package->name; }; if (path.count == 1) { @@ -3014,63 +2771,36 @@ void check_import_entities(Checker *c) { } ptr_set_add(&emitted, s); - array_add(&c->file_order, n); + array_add(&c->package_order, n); } - for_array(file_index, c->parser->files) { - AstFile *f = c->parser->files[file_index]; - Scope *s = f->scope; - if (s->is_init || s->is_global) { - ptr_set_add(&c->checked_files, f); - } - } - - // for_array(file_index, c->file_order) { - // ImportGraphNode *node = c->file_order[file_index]; - // AstFile *f = node->scope->file; - // gb_printf_err("--- %.*s -> %td\n", LIT(f->fullpath), node->succ.entries.count); - // } - - for (;;) { - bool new_files = false; - for_array(file_index, c->file_order) { - ImportGraphNode *node = c->file_order[file_index]; - AstFile *f = node->scope->file; - - if (!ptr_set_exists(&c->checked_files, f)) { - continue; - } - - CheckerContext prev_context = c->context; - defer (c->context = prev_context); - add_curr_ast_file(c, f); - - new_files |= collect_checked_files_from_import_decl_list(c, f->decls); - } - if (new_files) break; - } - - for (isize file_index = 0; file_index < c->file_order.count; file_index += 1) { - ImportGraphNode *node = c->file_order[file_index]; - AstFile *f = node->scope->file; - - if (!ptr_set_exists(&c->checked_files, f)) { - continue; - } + for_array(i, c->package_order) { + ImportGraphNode *node = c->package_order[i]; + GB_ASSERT(node->scope->is_package); + AstPackage *p = node->scope->package; CheckerContext prev_context = c->context; defer (c->context = prev_context); - c->context.collect_delayed_decls = true; - add_curr_ast_file(c, f); + add_curr_ast_package(c, p); - bool new_files = collect_file_decls(c, f->decls); - if (new_files) { - // TODO(bill): Only start from the lowest new file - file_index = -1; - continue; + for_array(i, p->files.entries) { + AstFile *f = p->files.entries[i].value; + check_collect_entities(c, f->decls); + } + + for_array(j, node->scope->delayed_imports) { + ast_node(id, ImportDecl, node->scope->delayed_imports[j]); + check_add_import_decl(c, id); + } + + for_array(j, node->scope->delayed_asserts) { + AstNode *expr = node->scope->delayed_asserts[j]; + Operand o = {}; + check_expr(c, &o, expr); } } + // gb_printf_err("End here!\n"); // gb_exit(1); } @@ -3230,13 +2960,13 @@ void check_parsed_files(Checker *c) { add_type_info_type(c, t_invalid); // Map full filepaths to Scopes - for_array(i, c->parser->files) { - AstFile *f = c->parser->files[i]; - Scope *scope = create_scope_from_file(c, f); - f->decl_info = make_decl_info(c->allocator, f->scope, c->context.decl); - HashKey key = hash_string(f->tokenizer.fullpath); - map_set(&c->file_scopes, key, scope); - map_set(&c->info.files, key, f); + for_array(i, c->parser->packages) { + AstPackage *p = c->parser->packages[i]; + Scope *scope = create_scope_from_package(c, p); + p->decl_info = make_decl_info(c->allocator, p->scope, c->context.decl); + HashKey key = hash_string(p->fullpath); + map_set(&c->package_scopes, key, scope); + map_set(&c->info.packages, key, p); if (scope->is_init) { c->info.init_scope = scope; @@ -3245,11 +2975,14 @@ void check_parsed_files(Checker *c) { TIME_SECTION("collect entities"); // Collect Entities - for_array(i, c->parser->files) { - AstFile *f = c->parser->files[i]; + for_array(i, c->parser->packages) { + AstPackage *p = c->parser->packages[i]; CheckerContext prev_context = c->context; - add_curr_ast_file(c, f); - check_collect_entities(c, f->decls); + add_curr_ast_package(c, p); + for_array(j, p->files.entries) { + AstFile *f = p->files.entries[j].value; + check_collect_entities(c, f->decls); + } c->context = prev_context; } @@ -3279,7 +3012,7 @@ void check_parsed_files(Checker *c) { GB_ASSERT_MSG(pt->is_poly_specialized, "%.*s", LIT(name)); } - add_curr_ast_file(c, pi->file); + add_curr_ast_package(c, pi->package); bool bounds_check = (pi->tags & ProcTag_bounds_check) != 0; bool no_bounds_check = (pi->tags & ProcTag_no_bounds_check) != 0; @@ -3353,12 +3086,14 @@ void check_parsed_files(Checker *c) { Entity *e = current_scope_lookup_entity(s, str_lit("main")); if (e == nullptr) { Token token = {}; - if (s->file->tokens.count > 0) { - token = s->file->tokens[0]; - } else { - token.pos.file = s->file->tokenizer.fullpath; - token.pos.line = 1; - token.pos.column = 1; + token.pos.file = s->package->fullpath; + token.pos.line = 1; + token.pos.column = 1; + if (s->package->files.entries.count > 0) { + AstFile *f = s->package->files.entries[0].value; + if (f->tokens.count > 0) { + token = f->tokens[0]; + } } error(token, "Undefined entry point procedure 'main'"); diff --git a/src/checker.hpp b/src/checker.hpp index 6b931fa62..337cb3b04 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -194,7 +194,8 @@ struct DeclInfo { // ProcedureInfo stores the information needed for checking a procedure struct ProcedureInfo { - AstFile * file; + // AstFile * file; + AstPackage * package; Token token; DeclInfo * decl; Type * type; // Type_Procedure @@ -204,6 +205,7 @@ struct ProcedureInfo { }; + struct Scope { AstNode * node; Scope * parent; @@ -214,17 +216,18 @@ struct Scope { PtrSet implicit; Array shared; - Array delayed_file_decls; Array delayed_asserts; + Array delayed_imports; PtrSet imported; PtrSet exported; // NOTE(bhall): Contains 'using import' too bool is_proc; bool is_global; - bool is_file; + bool is_package; bool is_init; bool is_struct; bool has_been_imported; // This is only applicable to file scopes - AstFile * file; + + AstPackage * package; }; @@ -250,7 +253,7 @@ typedef PtrSet ImportGraphNodeSet; struct ImportGraphNode { Scope * scope; String path; - isize file_id; + isize package_id; ImportGraphNodeSet pred; ImportGraphNodeSet succ; isize index; // Index in array/queue @@ -268,7 +271,7 @@ struct ForeignContext { typedef Array CheckerTypePath; struct CheckerContext { - Scope * file_scope; + Scope * package_scope; Scope * scope; DeclInfo * decl; u32 stmt_state_flags; @@ -282,7 +285,6 @@ struct CheckerContext { CheckerTypePath *type_path; isize type_level; // TODO(bill): Actually handle correctly - bool collect_delayed_decls; bool allow_polymorphic_types; bool no_polymorphic_errors; bool in_polymorphic_specialization; @@ -295,6 +297,7 @@ struct CheckerInfo { Map types; // Key: AstNode * | Expression -> Type (and value) Map untyped; // Key: AstNode * | Expression -> ExprInfo Map files; // Key: String (full path) + Map packages; // Key: String (full path) Map foreigns; // Key: String Array definitions; Array entities; @@ -318,12 +321,12 @@ struct Checker { CheckerInfo info; gbMutex mutex; - AstFile * curr_ast_file; + AstPackage * curr_ast_package; Scope * global_scope; // NOTE(bill): Procedures to check Array procs; - Map file_scopes; // Key: String (fullpath) - Array file_order; + Map package_scopes; // Key: String (fullpath) + Array package_order; gbAllocator allocator; gbArena arena; @@ -334,9 +337,6 @@ struct Checker { Array proc_stack; bool done_preload; - - PtrSet checked_files; - }; @@ -382,7 +382,7 @@ void add_entity_and_decl_info(Checker *c, AstNode *identifier, Entity *e, D void add_type_info_type (Checker *c, Type *t); void check_add_import_decl(Checker *c, AstNodeImportDecl *id); -void check_add_export_decl(Checker *c, AstNodeExportDecl *ed); +// void check_add_export_decl(Checker *c, AstNodeExportDecl *ed); void check_add_foreign_import_decl(Checker *c, AstNode *decl); diff --git a/src/common.cpp b/src/common.cpp index 194f1e386..15412f108 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -748,6 +748,8 @@ enum ReadDirectoryError { ReadDirectory_None, ReadDirectory_InvalidPath, + ReadDirectory_NotExists, + ReadDirectory_Permission, ReadDirectory_NotDir, ReadDirectory_EOF, ReadDirectory_Unknown, @@ -760,6 +762,8 @@ enum ReadDirectoryError { ReadDirectoryError read_directory(String path, Array *fi) { GB_ASSERT(fi != nullptr); + gbAllocator a = heap_allocator(); + while (path.len > 0) { Rune end = path[path.len-1]; if (end == '/') { @@ -774,11 +778,25 @@ ReadDirectoryError read_directory(String path, Array *fi) { if (path.len == 0) { return ReadDirectory_InvalidPath; } + { + char *c_str = alloc_cstring(a, path); + defer (gb_free(a, c_str)); + + gbFile f = {}; + gbFileError file_err = gb_file_open(&f, c_str); + defer (gb_file_close(&f)); + + switch (file_err) { + case gbFileError_Invalid: return ReadDirectory_InvalidPath; + case gbFileError_NotExists: return ReadDirectory_NotExists; + // case gbFileError_Permission: return ReadDirectory_Permission; + } + } + if (!path_is_directory(path)) { return ReadDirectory_NotDir; } - gbAllocator a = heap_allocator(); char *new_path = gb_alloc_array(a, char, path.len+3); defer (gb_free(a, new_path)); diff --git a/src/entity.cpp b/src/entity.cpp index 182df8c05..d7f7dc2df 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -6,6 +6,7 @@ struct DeclInfo; #define ENTITY_KINDS \ ENTITY_KIND(Invalid) \ + ENTITY_KIND(Package) \ ENTITY_KIND(Constant) \ ENTITY_KIND(Variable) \ ENTITY_KIND(TypeName) \ @@ -85,6 +86,12 @@ struct Entity { String deprecated_message; union { + struct { + String fullpath; + String name; + Scope *scope; + // Array imports; // Entity_Package + } Package; struct { ExactValue value; } Constant; @@ -189,6 +196,15 @@ Entity *alloc_entity(EntityKind kind, Scope *scope, Token token, Type *type) { return entity; } +Entity *alloc_entity_package(Scope *scope, Type *type, String fullpath, String name) { + Token token = empty_token; + token.string = name; + Entity *entity = alloc_entity(Entity_Package, scope, token, type); + entity->Package.fullpath = fullpath; + entity->Package.name = name; + return entity; +} + Entity *alloc_entity_variable(Scope *scope, Token token, Type *type, bool is_immutable, EntityState state = EntityState_Unresolved) { Entity *entity = alloc_entity(Entity_Variable, scope, token, type); entity->Variable.is_immutable = is_immutable; diff --git a/src/main.cpp b/src/main.cpp index 3d56d73b9..9b08fda1f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,9 +12,9 @@ #include "checker.hpp" #include "parser.cpp" -#if 0 #include "docs.cpp" #include "checker.cpp" +#if 0 #include "ir.cpp" #include "ir_opt.cpp" #include "ir_print.cpp" @@ -574,16 +574,22 @@ bool parse_build_flags(Array args) { } void show_timings(Checker *c, Timings *t) { - Parser *p = c->parser; - isize lines = p->total_line_count; - isize tokens = p->total_token_count; - isize files = p->files.count; + Parser *p = c->parser; + isize lines = p->total_line_count; + isize tokens = p->total_token_count; + isize files = 0; + isize packages = p->packages.count; + for_array(i, p->packages) { + files += p->packages[i]->files.entries.count; + } + { timings_print_all(t); gb_printf("\n"); - gb_printf("Total Lines - %td\n", lines); - gb_printf("Total Tokens - %td\n", tokens); - gb_printf("Total Files - %td\n", files); + gb_printf("Total Lines - %td\n", lines); + gb_printf("Total Tokens - %td\n", tokens); + gb_printf("Total Files - %td\n", files); + gb_printf("Total Packages - %td\n", packages); gb_printf("\n"); } { @@ -774,9 +780,8 @@ int main(int arg_count, char **arg_ptr) { print_usage_line(0, "%s 32-bit is not yet supported", args[0]); return 1; } -#if 0 + init_universal_scope(); -#endif // TODO(bill): prevent compiling without a linker timings_start_section(&timings, str_lit("parse files")); @@ -791,9 +796,8 @@ int main(int arg_count, char **arg_ptr) { return 1; } -#if 0 if (build_context.generate_docs) { - generate_documentation(&parser); + // generate_documentation(&parser); return 0; } @@ -807,6 +811,7 @@ int main(int arg_count, char **arg_ptr) { check_parsed_files(&checker); +#if 0 if (build_context.no_output_files) { if (build_context.show_timings) { show_timings(&checker, &timings); diff --git a/src/parser.cpp b/src/parser.cpp index 4321ec3f3..7e3a79613 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -61,7 +61,7 @@ Token ast_node_token(AstNode *node) { case AstNode_ValueDecl: return ast_node_token(node->ValueDecl.names[0]); case AstNode_ImportDecl: return node->ImportDecl.token; - case AstNode_ExportDecl: return node->ExportDecl.token; + // case AstNode_ExportDecl: return node->ExportDecl.token; case AstNode_ForeignImportDecl: return node->ForeignImportDecl.token; case AstNode_ForeignBlockDecl: return node->ForeignBlockDecl.token; @@ -1002,15 +1002,15 @@ AstNode *ast_import_decl(AstFile *f, Token token, bool is_using, Token relpath, return result; } -AstNode *ast_export_decl(AstFile *f, Token token, Token relpath, - CommentGroup docs, CommentGroup comment) { - AstNode *result = make_ast_node(f, AstNode_ExportDecl); - result->ExportDecl.token = token; - result->ExportDecl.relpath = relpath; - result->ExportDecl.docs = docs; - result->ExportDecl.comment = comment; - return result; -} +// AstNode *ast_export_decl(AstFile *f, Token token, Token relpath, +// CommentGroup docs, CommentGroup comment) { +// AstNode *result = make_ast_node(f, AstNode_ExportDecl); +// result->ExportDecl.token = token; +// result->ExportDecl.relpath = relpath; +// result->ExportDecl.docs = docs; +// result->ExportDecl.comment = comment; +// return result; +// } AstNode *ast_foreign_import_decl(AstFile *f, Token token, Token filepath, Token library_name, CommentGroup docs, CommentGroup comment) { @@ -1309,7 +1309,7 @@ bool is_semicolon_optional_for_node(AstFile *f, AstNode *s) { return s->ProcLit.body != nullptr; case AstNode_ImportDecl: - case AstNode_ExportDecl: + // case AstNode_ExportDecl: case AstNode_ForeignImportDecl: return true; @@ -3477,21 +3477,21 @@ AstNode *parse_import_decl(AstFile *f, ImportDeclKind kind) { return s; } -AstNode *parse_export_decl(AstFile *f) { - CommentGroup docs = f->lead_comment; - Token token = expect_token(f, Token_export); - Token file_path = expect_token_after(f, Token_String, "export"); - AstNode *s = nullptr; - if (f->curr_proc != nullptr) { - syntax_error(token, "You cannot use 'export' within a procedure. This must be done at the file scope"); - s = ast_bad_decl(f, token, file_path); - } else { - s = ast_export_decl(f, token, file_path, docs, f->line_comment); - array_add(&f->imports_and_exports, s); - } - expect_semicolon(f, s); - return s; -} +// AstNode *parse_export_decl(AstFile *f) { +// CommentGroup docs = f->lead_comment; +// Token token = expect_token(f, Token_export); +// Token file_path = expect_token_after(f, Token_String, "export"); +// AstNode *s = nullptr; +// if (f->curr_proc != nullptr) { +// syntax_error(token, "You cannot use 'export' within a procedure. This must be done at the file scope"); +// s = ast_bad_decl(f, token, file_path); +// } else { +// s = ast_export_decl(f, token, file_path, docs, f->line_comment); +// array_add(&f->imports_and_exports, s); +// } +// expect_semicolon(f, s); +// return s; +// } AstNode *parse_foreign_decl(AstFile *f) { CommentGroup docs = f->lead_comment; @@ -3584,8 +3584,8 @@ AstNode *parse_stmt(AstFile *f) { case Token_import: return parse_import_decl(f, ImportDecl_Standard); - case Token_export: - return parse_export_decl(f); + // case Token_export: + // return parse_export_decl(f); case Token_if: return parse_if_stmt(f); @@ -3632,13 +3632,13 @@ AstNode *parse_stmt(AstFile *f) { import_decl->ImportDecl.using_in_list = list; } return import_decl; - } else if (f->curr_token.kind == Token_export) { + } /* else if (f->curr_token.kind == Token_export) { AstNode *export_decl = parse_export_decl(f); if (export_decl->kind == AstNode_ExportDecl) { export_decl->ExportDecl.using_in_list = list; } return export_decl; - } + } */ AstNode *expr = parse_expr(f, true); expect_semicolon(f, expr); @@ -3883,9 +3883,9 @@ void destroy_ast_file(AstFile *f) { bool init_parser(Parser *p) { GB_ASSERT(p != nullptr); - map_init(&p->packages, heap_allocator()); + map_init(&p->imported_files, heap_allocator()); + array_init(&p->packages, heap_allocator()); array_init(&p->imports, heap_allocator()); - array_init(&p->files, heap_allocator()); gb_mutex_init(&p->file_add_mutex); gb_mutex_init(&p->file_decl_mutex); return true; @@ -3894,22 +3894,27 @@ bool init_parser(Parser *p) { void destroy_parser(Parser *p) { GB_ASSERT(p != nullptr); // TODO(bill): Fix memory leak - for_array(i, p->files) { - destroy_ast_file(p->files[i]); + for_array(i, p->packages) { + AstPackage *package = p->packages[i]; + for_array(j, package->files.entries) { + destroy_ast_file(package->files.entries[j].value); + } + map_destroy(&package->files); } #if 0 for_array(i, p->imports) { // gb_free(heap_allocator(), p->imports[i].text); } #endif - array_free(&p->files); + array_free(&p->packages); array_free(&p->imports); + map_destroy(&p->imported_files); gb_mutex_destroy(&p->file_add_mutex); gb_mutex_destroy(&p->file_decl_mutex); } // NOTE(bill): Returns true if it's added -bool try_add_import_path(Parser *p, String path, String rel_path, TokenPos pos) { +bool try_add_import_path(Parser *p, String path, String rel_path, TokenPos pos, ImportedPackageKind kind = ImportedPackage_Normal) { if (build_context.generate_docs) { return false; } @@ -3917,22 +3922,20 @@ bool try_add_import_path(Parser *p, String path, String rel_path, TokenPos pos) path = string_trim_whitespace(path); rel_path = string_trim_whitespace(rel_path); - for_array(i, p->imports) { - String import = p->imports[i].path; - if (import == path) { - return false; - } + HashKey key = hash_string(path); + if (map_get(&p->imported_files, key) != nullptr) { + return false; } + map_set(&p->imported_files, key, true); ImportedPackage item = {}; - item.kind = ImportedPackage_Normal; + item.kind = kind; item.path = path; item.rel_path = rel_path; item.pos = pos; item.index = p->imports.count; array_add(&p->imports, item); - return true; } @@ -4054,34 +4057,12 @@ bool determine_path_from_string(Parser *p, AstNode *node, String base_dir, Strin } -void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array decls); - -void parse_setup_file_when_stmt(Parser *p, AstFile *f, String base_dir, AstNodeWhenStmt *ws) { - if (ws->body != nullptr) { - auto stmts = ws->body->BlockStmt.stmts; - parse_setup_file_decls(p, f, base_dir, stmts); - } - - if (ws->else_stmt != nullptr) { - switch (ws->else_stmt->kind) { - case AstNode_BlockStmt: { - auto stmts = ws->else_stmt->BlockStmt.stmts; - parse_setup_file_decls(p, f, base_dir, stmts); - } break; - case AstNode_WhenStmt: - parse_setup_file_when_stmt(p, f, base_dir, &ws->else_stmt->WhenStmt); - break; - } - } -} - void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array decls) { for_array(i, decls) { AstNode *node = decls[i]; if (!is_ast_node_decl(node) && node->kind != AstNode_BadStmt && - node->kind != AstNode_EmptyStmt && - node->kind != AstNode_WhenStmt) { + node->kind != AstNode_EmptyStmt) { // NOTE(bill): Sanity check if (node->kind == AstNode_ExprStmt) { @@ -4108,7 +4089,7 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Arrayfullpath = import_path; try_add_import_path(p, import_path, original_string, ast_node_token(node).pos); - } else if (node->kind == AstNode_ExportDecl) { + } /* else if (node->kind == AstNode_ExportDecl) { ast_node(ed, ExportDecl, node); String original_string = ed->relpath.string; @@ -4123,7 +4104,7 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Arrayfullpath = export_path; try_add_import_path(p, export_path, original_string, ast_node_token(node).pos); - } else if (node->kind == AstNode_ForeignImportDecl) { + } */else if (node->kind == AstNode_ForeignImportDecl) { ast_node(fl, ForeignImportDecl, node); String file_str = fl->filepath.string; @@ -4140,9 +4121,6 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Arrayfullpath = foreign_path; } - } else if (node->kind == AstNode_WhenStmt) { - ast_node(ws, WhenStmt, node); - parse_setup_file_when_stmt(p, f, base_dir, ws); } } } @@ -4241,7 +4219,6 @@ skip: // file->id = imported_package.index; HashKey key = hash_string(fi->fullpath); map_set(&package->files, key, file); - array_add(&p->files, file); if (package->name.len == 0) { package->name = file->package_name; @@ -4262,41 +4239,47 @@ ParseFileError parse_import(Parser *p, ImportedPackage imported_package) { String import_rel_path = imported_package.rel_path; TokenPos pos = imported_package.pos; - HashKey path_key = hash_string(import_path); - if (map_get(&p->packages, path_key) != nullptr) { - return ParseFile_None; - } - - Array list = {}; ReadDirectoryError rd_err = read_directory(import_path, &list); defer (array_free(&list)); - if (rd_err != ReadDirectory_EOF && rd_err != ReadDirectory_None && pos.line != 0) { - gb_printf_err("%.*s(%td:%td) ", LIT(pos.file), pos.line, pos.column); + if (list.count == 1) { + GB_ASSERT(import_path != list[0].fullpath); } - switch (rd_err) { - case ReadDirectory_InvalidPath: - gb_printf_err("Invalid path: %.*s\n", LIT(import_rel_path)); + if (rd_err != ReadDirectory_EOF && rd_err != ReadDirectory_None) { + if (pos.line != 0) { + gb_printf_err("%.*s(%td:%td) ", LIT(pos.file), pos.line, pos.column); + } gb_mutex_lock(&global_error_collector.mutex); + defer (gb_mutex_unlock(&global_error_collector.mutex)); global_error_collector.count++; - gb_mutex_unlock(&global_error_collector.mutex); - return ParseFile_InvalidFile; - case ReadDirectory_NotDir: - gb_printf_err("Expected a directory for a package, got a file: %.*s\n", LIT(import_rel_path)); - gb_mutex_lock(&global_error_collector.mutex); - global_error_collector.count++; - gb_mutex_unlock(&global_error_collector.mutex); - return ParseFile_InvalidFile; - case ReadDirectory_Unknown: - gb_printf_err("Unknown error whilst reading directory"); - gb_mutex_lock(&global_error_collector.mutex); - global_error_collector.count++; - gb_mutex_unlock(&global_error_collector.mutex); - return ParseFile_InvalidFile; - case ReadDirectory_EOF: - break; + + + switch (rd_err) { + case ReadDirectory_InvalidPath: + gb_printf_err("Invalid path: %.*s\n", LIT(import_rel_path)); + return ParseFile_InvalidFile; + + case ReadDirectory_NotExists: + gb_printf_err("Path does not exist: %.*s\n", LIT(import_rel_path)); + return ParseFile_NotFound; + + case ReadDirectory_NotDir: + gb_printf_err("Expected a directory for a package, got a file: %.*s\n", LIT(import_rel_path)); + return ParseFile_InvalidFile; + + case ReadDirectory_Unknown: + gb_printf_err("Unknown error whilst reading path %.*s\n", LIT(import_rel_path)); + return ParseFile_InvalidFile; + case ReadDirectory_Permission: + gb_printf_err("Unknown error whilst reading path %.*s\n", LIT(import_rel_path)); + return ParseFile_InvalidFile; + + case ReadDirectory_EOF: + gb_printf_err("Unknown error whilst reading path %.*s\n", LIT(import_rel_path)); + return ParseFile_InvalidFile; + } } AstPackage *package = gb_alloc_item(heap_allocator(), AstPackage); @@ -4307,7 +4290,12 @@ ParseFileError parse_import(Parser *p, ImportedPackage imported_package) { // TODO(bill): Fix concurrency for_array(i, list) { FileInfo *fi = &list[i]; - if (string_ends_with(fi->name, str_lit(".odin"))) { + String name = fi->name; + String const ext = str_lit(".odin"); + if (string_ends_with(name, ext)) { + if (is_excluded_target_filename(name)) { + continue; + } ParseFileError err = parse_imported_file(p, package, fi, pos); if (err != ParseFile_None) { return err; @@ -4315,6 +4303,10 @@ ParseFileError parse_import(Parser *p, ImportedPackage imported_package) { } } + package->id = p->packages.count+1; + array_add(&p->packages, package); + + return ParseFile_None; } @@ -4342,25 +4334,24 @@ ParseFileError parse_packages(Parser *p, String init_filename) { TokenPos init_pos = {}; ImportedPackage init_imported_package = {ImportedPackage_Init, init_fullpath, init_fullpath, init_pos}; - isize shared_file_count = 0; + isize shared_package_count = 0; if (!build_context.generate_docs) { String s = get_fullpath_core(heap_allocator(), str_lit("runtime")); - ImportedPackage runtime_package = {ImportedPackage_Runtime, s, s, init_pos}; - array_add(&p->imports, runtime_package); - shared_file_count++; + try_add_import_path(p, s, s, init_pos, ImportedPackage_Runtime); + shared_package_count++; } array_add(&p->imports, init_imported_package); p->init_fullpath = init_fullpath; // IMPORTANT TODO(bill): Figure out why this doesn't work on *nix sometimes -#if defined(GB_SYSTEM_WINDOWS) +#if 0 && defined(GB_SYSTEM_WINDOWS) isize thread_count = gb_max(build_context.thread_count, 1); if (thread_count > 1) { isize volatile curr_import_index = 0; // NOTE(bill): Make sure that these are in parsed in this order - for (isize i = 0; i < shared_file_count; i++) { + for (isize i = 0; i < shared_package_count; i++) { ParseFileError err = parse_import(p, p->imports[i]); if (err != ParseFile_None) { return err; diff --git a/src/parser.hpp b/src/parser.hpp index d99562d2e..f1547847f 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -67,8 +67,8 @@ struct AstFile { AstNode * curr_proc; isize scope_level; - Scope * scope; // NOTE(bill): Created in checker - DeclInfo * decl_info; // NOTE(bill): Created in checker + // Scope * scope; // NOTE(bill): Created in checker + // DeclInfo * decl_info; // NOTE(bill): Created in checker CommentGroup lead_comment; // Comment (block) before the decl @@ -84,17 +84,21 @@ struct AstFile { struct AstPackage { + isize id; ImportedPackageKind kind; String name; String fullpath; Map files; // Key: String (names) + + Scope * scope; // NOTE(bill): Created in checker + DeclInfo *decl_info; // NOTE(bill): Created in checker }; struct Parser { String init_fullpath; - Map packages; // Key: String (fullpath) - Array files; + Map imported_files; // Key: String (fullpath) + Array packages; Array imports; isize total_token_count; isize total_line_count; @@ -351,7 +355,7 @@ AST_NODE_KIND(_DeclBegin, "", struct {}) \ bool been_handled; \ }) \ AST_NODE_KIND(ImportDecl, "import declaration", struct { \ - AstFile *file; \ + AstPackage *package; \ Token token; \ Token relpath; \ String fullpath; \ @@ -362,7 +366,7 @@ AST_NODE_KIND(_DeclBegin, "", struct {}) \ bool is_using; \ bool been_handled; \ }) \ - AST_NODE_KIND(ExportDecl, "export declaration", struct { \ + /* AST_NODE_KIND(ExportDecl, "export declaration", struct { \ AstFile *file; \ Token token; \ Token relpath; \ @@ -371,7 +375,7 @@ AST_NODE_KIND(_DeclBegin, "", struct {}) \ CommentGroup docs; \ CommentGroup comment; \ bool been_handled; \ - }) \ + }) */ \ AST_NODE_KIND(ForeignImportDecl, "foreign import declaration", struct { \ Token token; \ Token filepath; \ diff --git a/src/string.cpp b/src/string.cpp index 16ba3c951..b63de5c76 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -94,6 +94,13 @@ String substring(String const &s, isize lo, isize hi) { } +char *alloc_cstring(gbAllocator a, String s) { + char *c_str = gb_alloc_array(a, char, s.len+1); + gb_memcopy(c_str, s.text, s.len); + c_str[s.len] = '\0'; + return c_str; +} + gb_inline bool str_eq_ignore_case(String const &a, String const &b) { diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index ca8a2ba9f..c89b1ca6d 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -448,12 +448,9 @@ void advance_to_next_rune(Tokenizer *t) { TokenizerInitError init_tokenizer(Tokenizer *t, String fullpath) { TokenizerInitError err = TokenizerInit_None; - char *c_str = gb_alloc_array(heap_allocator(), char, fullpath.len+1); + char *c_str = alloc_cstring(heap_allocator(), fullpath); defer (gb_free(heap_allocator(), c_str)); - gb_memcopy(c_str, fullpath.text, fullpath.len); - c_str[fullpath.len] = '\0'; - // TODO(bill): Memory map rather than copy contents gbFileContents fc = gb_file_read_contents(heap_allocator(), true, c_str); gb_zero_item(t);