diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 1f5e62f06..5bf3e45a1 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -276,6 +276,77 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 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) { + default: + case gbFileError_Invalid: + error(ce->proc, "Failed to `#load` file: %s; invalid file or cannot be found", c_str); + return false; + case gbFileError_NotExists: + error(ce->proc, "Failed to `#load` file: %s; file cannot be found", c_str); + return false; + case gbFileError_Permission: + error(ce->proc, "Failed to `#load` file: %s; file permissions problem", c_str); + return false; + case gbFileError_None: + // Okay + break; + } + + String result = {}; + isize file_size = cast(isize)gb_file_size(&f); + if (file_size > 0) { + u8 *data = cast(u8 *)gb_alloc(a, file_size+1); + gb_file_read_at(&f, data, file_size, 0); + data[file_size] = '\0'; + result.text = data; + result.len = file_size; + } + + operand->type = t_u8_slice; + operand->mode = Addressing_Constant; + operand->value = exact_value_string(result); + + } else if (name == "load_or") { + if (ce->args.count != 1) { + error(ce->args[0], "'#load' expects 1 argument, 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' expected a constant string argument"); + return false; + } + + if (!is_type_string(o.type)) { + gbString str = type_to_string(o.type); + error(arg, "'#load' expected a constant string, got %s", str); + gb_string_free(str); + return false; + } + + gbAllocator a = heap_allocator(); + + GB_ASSERT(o.value.kind == ExactValue_String); + String base_dir = dir_from_path(get_file_path_string(bd->token.pos.file_id)); + String original_string = o.value.value_string; + + + BlockingMutex *ignore_mutex = nullptr; + String path = {}; + bool ok = determine_path_from_string(ignore_mutex, call, base_dir, original_string, &path); + gb_unused(ok); + + 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)); @@ -413,7 +484,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } } } else { - GB_PANIC("Unhandled #%.*s", LIT(name)); + error(call, "Unknown directive call: #%.*s", LIT(name)); } break; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index d59c3fea9..08be7b7bc 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5872,7 +5872,11 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr operand->type = t_invalid; add_type_and_value(c->info, proc, operand->mode, operand->type, operand->value); } else { - GB_PANIC("Unhandled #%.*s", LIT(name)); + error(proc, "Unknown directive: #%.*s", LIT(name)); + operand->expr = proc; + operand->type = t_invalid; + operand->mode = Addressing_Invalid; + return Expr_Expr; } if (inlining != ProcInlining_none) { error(call, "Inlining operators are not allowed on built-in procedures"); @@ -6609,7 +6613,26 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type o->type = t_source_code_location; o->mode = Addressing_Value; } else { - GB_PANIC("Unknown basic directive"); + if (name == "location") { + init_core_source_code_location(c->checker); + error(node, "'#%.*s' must be used in a call expression", LIT(name)); + o->type = t_source_code_location; + o->mode = Addressing_Value; + } else if ( + name == "load" || + name == "assert" || + name == "defined" || + name == "config" + ) { + error(node, "'#%.*s' must be used as a call", LIT(name)); + o->type = t_invalid; + o->mode = Addressing_Invalid; + } else { + error(node, "Unknown directive: #%.*s", LIT(name)); + o->type = t_invalid; + o->mode = Addressing_Invalid; + } + } case_end; diff --git a/src/parser.cpp b/src/parser.cpp index f72d8a73c..04d1dabc8 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2018,26 +2018,6 @@ Ast *parse_operand(AstFile *f, bool lhs) { Token name = expect_token(f, Token_Ident); if (name.string == "type") { return ast_helper_type(f, token, parse_type(f)); - } else if (name.string == "file") { - return ast_basic_directive(f, token, name); - } else if (name.string == "line") { return ast_basic_directive(f, token, name); - } else if (name.string == "procedure") { return ast_basic_directive(f, token, name); - } else if (name.string == "caller_location") { return ast_basic_directive(f, token, name); - } else if (name.string == "location") { - Ast *tag = ast_basic_directive(f, token, name); - return parse_call_expr(f, tag); - } else if (name.string == "load") { - Ast *tag = ast_basic_directive(f, token, name); - return parse_call_expr(f, tag); - } else if (name.string == "assert") { - Ast *tag = ast_basic_directive(f, token, name); - return parse_call_expr(f, tag); - } else if (name.string == "defined") { - Ast *tag = ast_basic_directive(f, token, name); - return parse_call_expr(f, tag); - } else if (name.string == "config") { - Ast *tag = ast_basic_directive(f, token, name); - return parse_call_expr(f, tag); } else if (name.string == "soa" || name.string == "simd") { Ast *tag = ast_basic_directive(f, token, name); Ast *original_type = parse_type(f); @@ -2072,16 +2052,11 @@ Ast *parse_operand(AstFile *f, bool lhs) { tag = parse_call_expr(f, tag); Ast *type = parse_type(f); return ast_relative_type(f, tag, type); - } else if (name.string == "opaque") { - syntax_warning(token, "'#opaque' has been removed and will do nothing to the applied type"); - return parse_type(f); } else if (name.string == "force_inline" || name.string == "force_no_inline") { return parse_force_inlining_operand(f, name); - } else { - operand = ast_tag_expr(f, token, name, parse_expr(f, false)); } - return operand; + return ast_basic_directive(f, token, name); } // Parse Procedure Type or Literal or Group