diff options
| author | gingerBill <bill@gingerbill.org> | 2024-02-09 15:18:29 +0000 |
|---|---|---|
| committer | gingerBill <bill@gingerbill.org> | 2024-02-09 15:18:29 +0000 |
| commit | 5c4485f65767366c14dfd9a98945a5479ae0e449 (patch) | |
| tree | 94ee4b5181c381f45332b27fb37d78b5464c6bde /src/check_builtin.cpp | |
| parent | bae2a6fc1e0d2207e51a66b5ea6e8ae511a3da6a (diff) | |
Add `#load_directory(path: string) > []runtime.Load_Directory_File`
Diffstat (limited to 'src/check_builtin.cpp')
| -rw-r--r-- | src/check_builtin.cpp | 187 |
1 files changed, 135 insertions, 52 deletions
diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 4e374add6..d39be37a9 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1264,6 +1264,139 @@ gb_internal LoadDirectiveResult check_load_directive(CheckerContext *c, Operand } +gb_internal int file_cache_sort_cmp(void const *x, void const *y) { + LoadFileCache const *a = *(LoadFileCache const **)(x); + LoadFileCache const *b = *(LoadFileCache const **)(y); + return string_compare(a->path, b->path); +} + +gb_internal LoadDirectiveResult check_load_directory_directive(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint, bool err_on_not_found) { + ast_node(ce, CallExpr, call); + ast_node(bd, BasicDirective, ce->proc); + String name = bd->name.string; + GB_ASSERT(name == "load_directory"); + + if (ce->args.count != 1) { + error(ce->args[0], "'#%.*s' expects 1 argument, got %td", LIT(name), ce->args.count); + return LoadDirective_Error; + } + + Ast *arg = ce->args[0]; + Operand o = {}; + check_expr(c, &o, arg); + if (o.mode != Addressing_Constant) { + error(arg, "'#%.*s' expected a constant string argument", LIT(name)); + return LoadDirective_Error; + } + + if (!is_type_string(o.type)) { + gbString str = type_to_string(o.type); + error(arg, "'#%.*s' expected a constant string, got %s", LIT(name), str); + gb_string_free(str); + return LoadDirective_Error; + } + + GB_ASSERT(o.value.kind == ExactValue_String); + + init_core_load_directory_file(c->checker); + + operand->type = t_load_directory_file_slice; + operand->mode = Addressing_Value; + + + String original_string = o.value.value_string; + String path; + if (gb_path_is_absolute((char*)original_string.text)) { + path = original_string; + } else { + String base_dir = dir_from_path(get_file_path_string(call->file_id)); + + BlockingMutex *ignore_mutex = nullptr; + bool ok = determine_path_from_string(ignore_mutex, call, base_dir, original_string, &path); + gb_unused(ok); + } + MUTEX_GUARD(&c->info->load_directory_mutex); + + + gbFileError file_error = gbFileError_None; + + Array<LoadFileCache *> file_caches = {}; + + LoadDirectoryCache **cache_ptr = string_map_get(&c->info->load_directory_cache, path); + LoadDirectoryCache *cache = cache_ptr ? *cache_ptr : nullptr; + if (cache) { + file_error = cache->file_error; + } + defer ({ + if (cache == nullptr) { + LoadDirectoryCache *new_cache = gb_alloc_item(permanent_allocator(), LoadDirectoryCache); + new_cache->path = path; + new_cache->files = file_caches; + new_cache->file_error = file_error; + string_map_set(&c->info->load_directory_cache, path, new_cache); + + map_set(&c->info->load_directory_map, call, new_cache); + } else { + cache->file_error = file_error; + } + }); + + + LoadDirectiveResult result = LoadDirective_Success; + + + if (cache == nullptr) { + Array<FileInfo> list = {}; + ReadDirectoryError rd_err = read_directory(path, &list); + defer (array_free(&list)); + + if (list.count == 1) { + GB_ASSERT(path != list[0].fullpath); + } + + + switch (rd_err) { + case ReadDirectory_InvalidPath: + error(call, "%.*s error - invalid path: %.*s", LIT(name), LIT(original_string)); + return LoadDirective_NotFound; + case ReadDirectory_NotExists: + error(call, "%.*s error - path does not exist: %.*s", LIT(name), LIT(original_string)); + return LoadDirective_NotFound; + case ReadDirectory_Permission: + error(call, "%.*s error - unknown error whilst reading path, %.*s", LIT(name), LIT(original_string)); + return LoadDirective_Error; + case ReadDirectory_NotDir: + error(call, "%.*s error - expected a directory, got a file: %.*s", LIT(name), LIT(original_string)); + return LoadDirective_Error; + case ReadDirectory_Empty: + error(call, "%.*s error - empty directory: %.*s", LIT(name), LIT(original_string)); + return LoadDirective_NotFound; + case ReadDirectory_Unknown: + error(call, "%.*s error - unknown error whilst reading path %.*s", LIT(name), LIT(original_string)); + return LoadDirective_Error; + } + + isize files_to_reserve = list.count+1; // always reserve 1 + + file_caches = array_make<LoadFileCache *>(heap_allocator(), 0, files_to_reserve); + + for (FileInfo fi : list) { + LoadFileCache *cache = nullptr; + if (cache_load_file_directive(c, call, fi.fullpath, err_on_not_found, &cache)) { + array_add(&file_caches, cache); + } else { + result = LoadDirective_Error; + } + } + + gb_sort_array(file_caches.data, file_caches.count, file_cache_sort_cmp); + + } + + return result; +} + + gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint) { ast_node(ce, CallExpr, call); @@ -1291,6 +1424,8 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o operand->mode = Addressing_Value; } else if (name == "load") { return check_load_directive(c, operand, call, type_hint, true) == LoadDirective_Success; + } else if (name == "load_directory") { + return check_load_directory_directive(c, operand, call, type_hint, true) == LoadDirective_Success; } else if (name == "load_hash") { if (ce->args.count != 2) { if (ce->args.count == 0) { @@ -1408,58 +1543,6 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o return true; } return false; - } else if (name == "load_or") { - error(call, "'#load_or' has now been removed in favour of '#load(path) or_else default'"); - - if (ce->args.count != 2) { - if (ce->args.count == 0) { - error(ce->close, "'#load_or' expects 2 arguments, got 0"); - } else { - error(ce->args[0], "'#load_or' expects 2 arguments, got %td", ce->args.count); - } - return false; - } - - Ast *arg = ce->args[0]; - Operand o = {}; - check_expr(c, &o, arg); - if (o.mode != Addressing_Constant) { - error(arg, "'#load_or' expected a constant string argument"); - return false; - } - - if (!is_type_string(o.type)) { - gbString str = type_to_string(o.type); - error(arg, "'#load_or' expected a constant string, got %s", str); - gb_string_free(str); - return false; - } - - Ast *default_arg = ce->args[1]; - Operand default_op = {}; - check_expr_with_type_hint(c, &default_op, default_arg, t_u8_slice); - if (default_op.mode != Addressing_Constant) { - error(arg, "'#load_or' expected a constant '[]byte' argument"); - return false; - } - - if (!are_types_identical(base_type(default_op.type), t_u8_slice)) { - gbString str = type_to_string(default_op.type); - error(arg, "'#load_or' expected a constant '[]byte', got %s", str); - gb_string_free(str); - return false; - } - GB_ASSERT(o.value.kind == ExactValue_String); - String original_string = o.value.value_string; - - operand->type = t_u8_slice; - operand->mode = Addressing_Constant; - LoadFileCache *cache = nullptr; - if (cache_load_file_directive(c, call, original_string, false, &cache)) { - operand->value = exact_value_string(cache->data); - } else { - operand->value = default_op.value; - } } else if (name == "assert") { if (ce->args.count != 1 && ce->args.count != 2) { error(call, "'#assert' expects either 1 or 2 arguments, got %td", ce->args.count); |