diff --git a/build.bat b/build.bat index 9aa3715a6..c68f2e40d 100644 --- a/build.bat +++ b/build.bat @@ -46,9 +46,6 @@ rem pushd %build_dir% del *.pdb > NUL 2> NUL del *.ilk > NUL 2> NUL - del ..\misc\*.pdb > NUL 2> NUL - del ..\misc\*.ilk > NUL 2> NUL - cl %compiler_settings% "src\main.cpp" ^ /link %linker_settings% -OUT:%exe_name% ^ && odin run code/demo.odin diff --git a/code/demo.odin b/code/demo.odin index d527e8c97..08f3dedde 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -1,5 +1,6 @@ -#import "fmt.odin" as fmt -#import "os.odin" as os +#import "fmt.odin" +#import "os.odin" + main :: proc() { Fruit :: enum { diff --git a/code/game.odin b/code/game.odin index dfa05750b..fd1bcfa70 100644 --- a/code/game.odin +++ b/code/game.odin @@ -1,7 +1,7 @@ -#import "win32.odin" as win32 -#import "fmt.odin" as fmt +#import "win32.odin" +#import "fmt.odin" +#import "math.odin" #import "opengl.odin" as gl -#import "math.odin" as math TWO_HEARTS :: #rune "💕" diff --git a/code/punity.odin b/code/punity.odin index e09fd60c5..de29555fd 100644 --- a/code/punity.odin +++ b/code/punity.odin @@ -1,6 +1,6 @@ -#import "win32.odin" as win32 -#import "fmt.odin" as fmt -#import "os.odin" as os +#import "win32.odin" +#import "fmt.odin" +#import "os.odin" CANVAS_WIDTH :: 128 CANVAS_HEIGHT :: 128 diff --git a/code/test.odin b/code/test.odin index a5df7c4d0..b99a832c1 100644 --- a/code/test.odin +++ b/code/test.odin @@ -1,4 +1,4 @@ -#import "fmt.odin" as fmt +#import "fmt.odin" thing :: proc() #link_name "frankerooney" { fmt.println("Hello!") diff --git a/core/fmt.odin b/core/fmt.odin index 5c4345d98..98aa2c227 100644 --- a/core/fmt.odin +++ b/core/fmt.odin @@ -1,4 +1,4 @@ -#import "os.odin" as os +#import "os.odin" PRINT_BUF_SIZE :: 1<<12 diff --git a/core/os.odin b/core/os.odin index 6f60119f4..137ae7113 100644 --- a/core/os.odin +++ b/core/os.odin @@ -1,4 +1,4 @@ -#import "win32.odin" as win32 +#import "win32.odin" File :: type struct { Handle :: type win32.HANDLE @@ -50,7 +50,6 @@ stdin := ^__std_files[File_Standard.INPUT] stdout := ^__std_files[File_Standard.OUTPUT] stderr := ^__std_files[File_Standard.ERROR] - __set_file_standards :: proc() -> [File_Standard.COUNT as int]File { return [File_Standard.COUNT as int]File{ File{handle = win32.GetStdHandle(win32.STD_INPUT_HANDLE)}, diff --git a/core/runtime.odin b/core/runtime.odin index 814458b1a..a23464e18 100644 --- a/core/runtime.odin +++ b/core/runtime.odin @@ -1,7 +1,7 @@ #shared_global_scope -#import "os.odin" as os -#import "fmt.odin" as fmt +#import "os.odin" +#import "fmt.odin" // IMPORTANT NOTE(bill): Do not change the order of any of this data // The compiler relies upon this _exact_ order @@ -134,93 +134,6 @@ memory_copy :: proc(dst, src: rawptr, len: int) #inline { llvm_memmove_64bit(dst, src, len, 1, false) } -__string_eq :: proc(a, b: string) -> bool { - if a.count != b.count { - return false - } - if ^a[0] == ^b[0] { - return true - } - return memory_compare(^a[0], ^b[0], a.count) == 0 -} - -__string_cmp :: proc(a, b : string) -> int { - // Translation of http://mgronhol.github.io/fast-strcmp/ - n := min(a.count, b.count) - - fast := n/size_of(int) + 1 - offset := (fast-1)*size_of(int) - curr_block := 0 - if n <= size_of(int) { - fast = 0 - } - - la := slice_ptr(^a[0] as ^int, fast) - lb := slice_ptr(^b[0] as ^int, fast) - - for ; curr_block < fast; curr_block++ { - if (la[curr_block] ~ lb[curr_block]) != 0 { - for pos := curr_block*size_of(int); pos < n; pos++ { - if (a[pos] ~ b[pos]) != 0 { - return a[pos] as int - b[pos] as int - } - } - } - - } - - for ; offset < n; offset++ { - if (a[offset] ~ b[offset]) != 0 { - return a[offset] as int - b[offset] as int - } - } - - return 0 -} - -__string_ne :: proc(a, b : string) -> bool #inline { return !__string_eq(a, b) } -__string_lt :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) < 0 } -__string_gt :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) > 0 } -__string_le :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) <= 0 } -__string_ge :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) >= 0 } - - -__assert :: proc(msg: string) { - fmt.fprintln(os.stderr, msg) - __debug_trap() -} - -__bounds_check_error :: proc(file: string, line, column: int, - index, count: int) { - if 0 <= index && index < count { - return - } - fmt.fprintf(os.stderr, "%(%:%) Index % is out of bounds range [0, %)\n", - file, line, column, index, count) - __debug_trap() -} - -__slice_expr_error :: proc(file: string, line, column: int, - low, high, max: int) { - if 0 <= low && low <= high && high <= max { - return - } - fmt.fprintf(os.stderr, "%(%:%) Invalid slice indices: [%:%:%]\n", - file, line, column, low, high, max) - __debug_trap() -} -__substring_expr_error :: proc(file: string, line, column: int, - low, high: int) { - if 0 <= low && low <= high { - return - } - fmt.fprintf(os.stderr, "%(%:%) Invalid substring indices: [%:%:%]\n", - file, line, column, low, high) - __debug_trap() -} - - - @@ -357,6 +270,103 @@ __default_allocator :: proc() -> Allocator { } + + + + + + + + + + + +__string_eq :: proc(a, b: string) -> bool { + if a.count != b.count { + return false + } + if ^a[0] == ^b[0] { + return true + } + return memory_compare(^a[0], ^b[0], a.count) == 0 +} + +__string_cmp :: proc(a, b : string) -> int { + // Translation of http://mgronhol.github.io/fast-strcmp/ + n := min(a.count, b.count) + + fast := n/size_of(int) + 1 + offset := (fast-1)*size_of(int) + curr_block := 0 + if n <= size_of(int) { + fast = 0 + } + + la := slice_ptr(^a[0] as ^int, fast) + lb := slice_ptr(^b[0] as ^int, fast) + + for ; curr_block < fast; curr_block++ { + if (la[curr_block] ~ lb[curr_block]) != 0 { + for pos := curr_block*size_of(int); pos < n; pos++ { + if (a[pos] ~ b[pos]) != 0 { + return a[pos] as int - b[pos] as int + } + } + } + + } + + for ; offset < n; offset++ { + if (a[offset] ~ b[offset]) != 0 { + return a[offset] as int - b[offset] as int + } + } + + return 0 +} + +__string_ne :: proc(a, b : string) -> bool #inline { return !__string_eq(a, b) } +__string_lt :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) < 0 } +__string_gt :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) > 0 } +__string_le :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) <= 0 } +__string_ge :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) >= 0 } + + +__assert :: proc(file: string, line, column: int, msg: string) #inline { + fmt.fprintf(os.stderr, "%(%:%) Runtime assertion: %\n", + file, line, column, msg) + __debug_trap() +} + +__bounds_check_error :: proc(file: string, line, column: int, + index, count: int) { + if 0 <= index && index < count { + return + } + fmt.fprintf(os.stderr, "%(%:%) Index % is out of bounds range [0, %)\n", + file, line, column, index, count) + __debug_trap() +} + +__slice_expr_error :: proc(file: string, line, column: int, + low, high, max: int) { + if 0 <= low && low <= high && high <= max { + return + } + fmt.fprintf(os.stderr, "%(%:%) Invalid slice indices: [%:%:%]\n", + file, line, column, low, high, max) + __debug_trap() +} +__substring_expr_error :: proc(file: string, line, column: int, + low, high: int) { + if 0 <= low && low <= high { + return + } + fmt.fprintf(os.stderr, "%(%:%) Invalid substring indices: [%:%:%]\n", + file, line, column, low, high) + __debug_trap() +} + __enum_to_string :: proc(info: ^Type_Info, value: i64) -> string { info = type_info_base(info) diff --git a/src/checker/checker.cpp b/src/checker/checker.cpp index abb911709..6a209cf08 100644 --- a/src/checker/checker.cpp +++ b/src/checker/checker.cpp @@ -145,6 +145,7 @@ enum BuiltinProcId { BuiltinProc_compile_assert, BuiltinProc_assert, + BuiltinProc_panic, BuiltinProc_copy, BuiltinProc_append, @@ -188,6 +189,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = { {STR_LIT("compile_assert"), 1, false, Expr_Stmt}, {STR_LIT("assert"), 1, false, Expr_Stmt}, + {STR_LIT("panic"), 1, false, Expr_Stmt}, {STR_LIT("copy"), 2, false, Expr_Expr}, {STR_LIT("append"), 2, false, Expr_Expr}, @@ -416,12 +418,15 @@ Entity *scope_insert_entity(Scope *s, Entity *entity) { void check_scope_usage(Checker *c, Scope *scope) { // TODO(bill): Use this? -#if 0 +#if 1 gb_for_array(i, scope->elements.entries) { auto *entry = scope->elements.entries + i; Entity *e = entry->value; - if (e->kind == Entity_Variable && !e->Variable.used) { - warning(e->token, "Unused variable: %.*s", LIT(e->token.string)); + if (e->kind == Entity_Variable) { + auto *v = &e->Variable; + if (!v->is_field && !v->used) { + warning(e->token, "Unused variable: %.*s", LIT(e->token.string)); + } } } @@ -1040,7 +1045,7 @@ void check_parsed_files(Checker *c) { warning(id->token, "Multiple #import of the same file within this scope"); } - if (id->import_name.string == make_string("_")) { + if (id->import_name.string == make_string(".")) { // 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; @@ -1057,11 +1062,50 @@ void check_parsed_files(Checker *c) { } } } else { - GB_ASSERT(id->import_name.string.len > 0); - Entity *e = make_entity_import_name(c->allocator, file_scope, id->import_name, t_invalid, - id->fullpath, id->import_name.string, - scope); - add_entity(c, file_scope, NULL, e); + String import_name = id->import_name.string; + if (import_name.len == 0) { + // NOTE(bill): use file name (without extension) as the identifier + // If it is a valid identifier + String filename = id->fullpath; + isize slash = 0; + isize dot = 0; + for (isize i = filename.len-1; i >= 0; i--) { + u8 c = filename.text[i]; + if (c == '/' || c == '\\') { + break; + } + slash = i; + } + + filename.text += slash; + filename.len -= slash; + + dot = filename.len; + while (dot --> 0) { + u8 c = filename.text[dot]; + if (c == '.') { + break; + } + } + + filename.len = dot; + + if (is_string_an_identifier(filename)) { + import_name = filename; + } else { + error(ast_node_token(decl), + "File name, %.*s, cannot be as an import name as it is not a valid identifier", + LIT(filename)); + } + } + + if (import_name.len > 0) { + id->import_name.string = import_name; + Entity *e = make_entity_import_name(c->allocator, file_scope, id->import_name, t_invalid, + id->fullpath, id->import_name.string, + scope); + add_entity(c, file_scope, NULL, e); + } } } } diff --git a/src/checker/expr.cpp b/src/checker/expr.cpp index 75c0f71f4..9dd761ec6 100644 --- a/src/checker/expr.cpp +++ b/src/checker/expr.cpp @@ -1534,7 +1534,9 @@ b32 check_is_castable_to(Checker *c, Operand *operand, Type *y) { return true; } if (is_type_string(xb) && is_type_u8_slice(yb)) { - return true; + if (is_type_typed(xb)) { + return true; + } } // proc <-> proc @@ -2422,6 +2424,20 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) } break; + case BuiltinProc_panic: + // panic :: proc(msg: string) + + if (!is_type_string(operand->type)) { + gbString str = expr_to_string(ce->args[0]); + defer (gb_string_free(str)); + error(ast_node_token(call), + "`%s` is not a string", str); + return false; + } + + operand->mode = Addressing_NoValue; + break; + case BuiltinProc_copy: { // copy :: proc(x, y: []Type) -> int Type *dest_type = NULL, *src_type = NULL; diff --git a/src/codegen/ssa.cpp b/src/codegen/ssa.cpp index 9342e5931..b45bbe5d5 100644 --- a/src/codegen/ssa.cpp +++ b/src/codegen/ssa.cpp @@ -2230,30 +2230,22 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue ssa_emit_if(proc, cond, err, done); proc->curr_block = err; + // TODO(bill): Cleanup allocations here Token token = ast_node_token(ce->args[0]); TokenPos pos = token.pos; gbString expr = expr_to_string(ce->args[0]); defer (gb_string_free(expr)); + isize expr_len = gb_string_length(expr); + String expr_str = {}; + expr_str.text = cast(u8 *)gb_alloc_copy_align(proc->module->allocator, expr, expr_len, 1); + expr_str.len = expr_len; - isize err_len = pos.file.len + 1 + 10 + 1 + 10 + 1; - err_len += 20; - err_len += gb_string_length(expr); - err_len += 2; - - u8 *err_str = gb_alloc_array(proc->module->allocator, u8, err_len); - err_len = gb_snprintf(cast(char *)err_str, err_len, - "%.*s(%td:%td) Runtime assertion: %s\n", - LIT(pos.file), pos.line, pos.column, expr); - err_len--; - - ssaValue *array = ssa_add_global_string_array(proc->module, make_string(err_str, err_len)); - ssaValue *elem = ssa_array_elem(proc, array); - ssaValue *len = ssa_make_const_int(proc->module->allocator, err_len); - ssaValue *string = ssa_emit_string(proc, elem, len); - - ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 1); - args[0] = string; - ssa_emit_global_call(proc, "__assert", args, 1); + ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 4); + args[0] = ssa_emit_global_string(proc, pos.file); + args[1] = ssa_make_const_int(proc->module->allocator, pos.line); + args[2] = ssa_make_const_int(proc->module->allocator, pos.column); + args[3] = ssa_emit_global_string(proc, expr_str); + ssa_emit_global_call(proc, "__assert", args, 4); ssa_emit_jump(proc, done); gb_array_append(proc->blocks, done); @@ -2262,6 +2254,25 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue return NULL; } break; + case BuiltinProc_panic: { + ssa_emit_comment(proc, make_string("panic")); + ssaValue *msg = ssa_build_expr(proc, ce->args[0]); + GB_ASSERT(is_type_string(ssa_type(msg))); + + Token token = ast_node_token(ce->args[0]); + TokenPos pos = token.pos; + + ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 4); + args[0] = ssa_emit_global_string(proc, pos.file); + args[1] = ssa_make_const_int(proc->module->allocator, pos.line); + args[2] = ssa_make_const_int(proc->module->allocator, pos.column); + args[3] = msg; + ssa_emit_global_call(proc, "__assert", args, 4); + + return NULL; + } break; + + case BuiltinProc_copy: { ssa_emit_comment(proc, make_string("copy")); // copy :: proc(dst, src: []Type) -> int diff --git a/src/main.cpp b/src/main.cpp index 765c80b50..0ae2f684d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -126,33 +126,46 @@ int main(int argc, char **argv) { isize base_name_len = gb_path_extension(output_name)-1 - output_name; - i32 exit_code = 0; - // For more passes arguments: http://llvm.org/docs/Passes.html - exit_code = win32_exec_command_line_app( - // "../misc/llvm-bin/opt %s -o %.*s.bc " - "opt %s -o %.*s.bc " - "-memcpyopt " - "-mem2reg " - "-die -dse " - "-dce " - // "-S " - // "-debug-pass=Arguments " - "", - output_name, cast(int)base_name_len, output_name); - if (exit_code != 0) - return exit_code; + { + char buf[300] = {}; + u32 buf_len = GetModuleFileNameA(GetModuleHandleA(NULL), buf, gb_size_of(buf)); + for (isize i = buf_len-1; i >= 0; i--) { + if (buf[i] == '\\' || + buf[i] == '/') { + break; + } + buf_len--; + } - PRINT_TIMER("llvm-opt"); + // For more passes arguments: http://llvm.org/docs/Passes.html + exit_code = win32_exec_command_line_app( + // "../misc/llvm-bin/opt %s -o %.*s.bc " + "\"%.*sbin\\opt.exe\" %s -o %.*s.bc " + "-memcpyopt " + "-mem2reg " + "-die -dse " + "-dce " + // "-S " + // "-debug-pass=Arguments " + "", + buf_len, buf, + output_name, + cast(int)base_name_len, output_name); + if (exit_code != 0) + return exit_code; + + PRINT_TIMER("llvm-opt"); + } #if 1 - gbString lib_str = gb_string_make(gb_heap_allocator(), "-lKernel32.lib"); + gbString lib_str = gb_string_make(gb_heap_allocator(), "-lKernel32"); // defer (gb_string_free(lib_str)); char lib_str_buf[1024] = {}; gb_for_array(i, parser.system_libraries) { String lib = parser.system_libraries[i]; isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf), - " -l%.*s.lib", LIT(lib)); + " -l%.*s", LIT(lib)); lib_str = gb_string_appendc(lib_str, lib_str_buf); } diff --git a/src/parser.cpp b/src/parser.cpp index e34756daa..2ae1dd2aa 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2587,10 +2587,18 @@ AstNode *parse_stmt(AstFile *f) { return make_bad_decl(f, token, f->cursor[0]); } else if (tag == make_string("import")) { // TODO(bill): better error messages - Token import_name; + Token import_name = {}; Token file_path = expect_token(f, Token_String); - expect_token(f, Token_as); - import_name = expect_token(f, Token_Identifier); + if (allow_token(f, Token_as)) { + // NOTE(bill): Custom import name + if (f->cursor[0].kind == Token_Period) { + import_name = f->cursor[0]; + import_name.kind = Token_Identifier; + next_token(f); + } else { + import_name = expect_token(f, Token_Identifier); + } + } if (f->curr_proc == NULL) { return make_import_decl(f, s->TagStmt.token, file_path, import_name, false); @@ -2601,7 +2609,7 @@ AstNode *parse_stmt(AstFile *f) { // TODO(bill): better error messages Token file_path = expect_token(f, Token_String); Token import_name = file_path; - import_name.string = make_string("_"); + import_name.string = make_string("."); if (f->curr_proc == NULL) { return make_import_decl(f, s->TagStmt.token, file_path, import_name, true); @@ -2683,7 +2691,7 @@ AstNodeArray parse_stmt_list(AstFile *f) { ParseFileError init_ast_file(AstFile *f, String fullpath) { if (!string_has_extension(fullpath, make_string("odin"))) { - gb_printf_err("Only `.odin` files are allowed\n"); + // gb_printf_err("Only `.odin` files are allowed\n"); return ParseFile_WrongExtension; } TokenizerInitError err = init_tokenizer(&f->tokenizer, fullpath); @@ -2775,11 +2783,13 @@ b32 try_add_import_path(Parser *p, String path, String rel_path, TokenPos pos) { String get_fullpath_relative(gbAllocator a, String base_dir, String path) { isize str_len = base_dir.len+path.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, path.text, path.len); + isize i = 0; + gb_memcopy(str+i, base_dir.text, base_dir.len); i += base_dir.len; + gb_memcopy(str+i, path.text, path.len); str[str_len] = '\0'; char *path_str = gb_path_get_full_name(a, cast(char *)str); return make_string(path_str); @@ -2861,6 +2871,26 @@ b32 is_import_path_valid(String path) { return false; } +String get_filepath_extension(String path) { + isize dot = 0; + b32 seen_slash = false; + for (isize i = path.len-1; i >= 0; i--) { + u8 c = path.text[i]; + if (c == '/' || c == '\\') { + seen_slash = true; + } + + if (c == '.') { + if (seen_slash) { + return make_string(""); + } + + dot = i; + break; + } + } + return make_string(path.text, dot); +} void parse_file(Parser *p, AstFile *f) { String filepath = f->tokenizer.fullpath; @@ -2900,9 +2930,8 @@ void parse_file(Parser *p, AstFile *f) { continue; } - String import_file = {}; String rel_path = get_fullpath_relative(allocator, base_dir, file_str); - import_file = rel_path; + String import_file = rel_path; if (!gb_file_exists(cast(char *)rel_path.text)) { // NOTE(bill): This should be null terminated String abs_path = get_fullpath_core(allocator, file_str); if (gb_file_exists(cast(char *)abs_path.text)) { @@ -2956,25 +2985,26 @@ ParseFileError parse_files(Parser *p, char *init_filename) { if (pos.line != 0) { gb_printf_err("%.*s(%td:%td) ", LIT(pos.file), pos.line, pos.column); } - gb_printf_err("Failed to parse file: %.*s\n", LIT(import_rel_path)); + gb_printf_err("Failed to parse file: %.*s\n\t", LIT(import_rel_path)); + defer (gb_printf_err("\n")); switch (err) { case ParseFile_WrongExtension: - gb_printf_err("\tInvalid file extension\n"); + gb_printf_err("Invalid file extension: File must have the extension `.odin`"); break; case ParseFile_InvalidFile: - gb_printf_err("\tInvalid file\n"); + gb_printf_err("Invalid file"); break; case ParseFile_EmptyFile: - gb_printf_err("\tFile is empty\n"); + gb_printf_err("File is empty"); break; case ParseFile_Permission: - gb_printf_err("\tFile permissions problem\n"); + gb_printf_err("File permissions problem"); break; case ParseFile_NotFound: - gb_printf_err("\tFile cannot be found\n"); + gb_printf_err("File cannot be found"); break; case ParseFile_InvalidToken: - gb_printf_err("\tInvalid token found in file\n"); + gb_printf_err("Invalid token found in file"); break; } return err; diff --git a/src/unicode.cpp b/src/unicode.cpp index cf415d86b..d8a26924c 100644 --- a/src/unicode.cpp +++ b/src/unicode.cpp @@ -39,3 +39,28 @@ b32 rune_is_whitespace(Rune r) { } return false; } + + +b32 is_string_an_identifier(String s) { + if (s.len < 1) { + return false; + } + isize offset = 0; + while (offset < s.len) { + b32 ok = false; + Rune r = -1; + isize size = gb_utf8_decode(s.text+offset, s.len-offset, &r); + if (offset == 0) { + ok = rune_is_letter(r); + } else { + ok = rune_is_letter(r) || rune_is_digit(r); + } + + if (!ok) { + return false; + } + offset += size; + } + + return offset == s.len; +}