mirror of
https://github.com/odin-lang/Odin.git
synced 2025-12-30 09:54:45 +00:00
Add -debug command (still in development)
This commit is contained in:
@@ -22,6 +22,7 @@ struct BuildContext {
|
||||
i32 optimization_level;
|
||||
bool show_timings;
|
||||
bool keep_temp_files;
|
||||
bool debug;
|
||||
|
||||
gbAffinity affinity;
|
||||
isize thread_count;
|
||||
|
||||
@@ -112,6 +112,8 @@ u128 fnv128a(void const *data, isize len) {
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#include "map.cpp"
|
||||
#include "ptr_set.cpp"
|
||||
#include "string_set.cpp"
|
||||
|
||||
24
src/gb/gb.h
24
src/gb/gb.h
@@ -1515,6 +1515,7 @@ typedef struct gbStringHeader {
|
||||
|
||||
#define GB_STRING_HEADER(str) (cast(gbStringHeader *)(str) - 1)
|
||||
|
||||
GB_DEF gbString gb_string_make_reserve (gbAllocator a, isize capacity);
|
||||
GB_DEF gbString gb_string_make (gbAllocator a, char const *str);
|
||||
GB_DEF gbString gb_string_make_length (gbAllocator a, void const *str, isize num_bytes);
|
||||
GB_DEF void gb_string_free (gbString str);
|
||||
@@ -6504,6 +6505,27 @@ gb_inline void gb__set_string_length (gbString str, isize len) { GB_STRING_HEAD
|
||||
gb_inline void gb__set_string_capacity(gbString str, isize cap) { GB_STRING_HEADER(str)->capacity = cap; }
|
||||
|
||||
|
||||
gbString gb_string_make_reserve(gbAllocator a, isize capacity) {
|
||||
isize header_size = gb_size_of(gbStringHeader);
|
||||
void *ptr = gb_alloc(a, header_size + capacity + 1);
|
||||
|
||||
gbString str;
|
||||
gbStringHeader *header;
|
||||
|
||||
if (ptr == NULL) return NULL;
|
||||
gb_zero_size(ptr, header_size + capacity + 1);
|
||||
|
||||
str = cast(char *)ptr + header_size;
|
||||
header = GB_STRING_HEADER(str);
|
||||
header->allocator = a;
|
||||
header->length = 0;
|
||||
header->capacity = capacity;
|
||||
str[capacity] = '\0';
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
gb_inline gbString gb_string_make(gbAllocator a, char const *str) {
|
||||
isize len = str ? gb_strlen(str) : 0;
|
||||
return gb_string_make_length(a, str, len);
|
||||
@@ -6516,8 +6538,8 @@ gbString gb_string_make_length(gbAllocator a, void const *init_str, isize num_by
|
||||
gbString str;
|
||||
gbStringHeader *header;
|
||||
|
||||
if (!init_str) gb_zero_size(ptr, header_size + num_bytes + 1);
|
||||
if (ptr == NULL) return NULL;
|
||||
if (!init_str) gb_zero_size(ptr, header_size + num_bytes + 1);
|
||||
|
||||
str = cast(char *)ptr + header_size;
|
||||
header = GB_STRING_HEADER(str);
|
||||
|
||||
57
src/ir.cpp
57
src/ir.cpp
@@ -109,6 +109,16 @@ struct irBranchBlocks {
|
||||
};
|
||||
|
||||
|
||||
struct irDebugLocation {
|
||||
TokenPos pos;
|
||||
irDebugInfo *debug_scope;
|
||||
};
|
||||
|
||||
struct irDebugScope {
|
||||
irDebugScope * parent;
|
||||
irDebugLocation loc;
|
||||
};
|
||||
|
||||
struct irProcedure {
|
||||
irProcedure * parent;
|
||||
Array<irProcedure *> children;
|
||||
@@ -125,6 +135,8 @@ struct irProcedure {
|
||||
bool is_export;
|
||||
bool is_entry_point;
|
||||
|
||||
irDebugInfo * debug_scope;
|
||||
|
||||
irValue * return_ptr;
|
||||
Array<irValue *> params;
|
||||
Array<irDefer> defer_stmts;
|
||||
@@ -146,6 +158,9 @@ struct irProcedure {
|
||||
i32 block_count;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#define IR_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime"
|
||||
#define IR_TYPE_INFO_DATA_NAME "__$type_info_data"
|
||||
#define IR_TYPE_INFO_TYPES_NAME "__$type_info_types_data"
|
||||
@@ -231,11 +246,10 @@ struct irProcedure {
|
||||
irValue **args; \
|
||||
isize arg_count; \
|
||||
irValue * context_ptr; \
|
||||
irDebugInfo *debug_location; \
|
||||
}) \
|
||||
IR_INSTR_KIND(StartupRuntime, i32) \
|
||||
IR_INSTR_KIND(DebugDeclare, struct { \
|
||||
irDebugInfo *debug_info; \
|
||||
irDebugInfo *scope; \
|
||||
AstNode * expr; \
|
||||
Entity * entity; \
|
||||
bool is_addr; \
|
||||
@@ -388,10 +402,12 @@ struct irValueParam {
|
||||
Array<irValue *> referrers;
|
||||
};
|
||||
|
||||
|
||||
struct irValue {
|
||||
irValueKind kind;
|
||||
i32 index;
|
||||
bool index_set;
|
||||
irValueKind kind;
|
||||
i32 index;
|
||||
bool index_set;
|
||||
irDebugLocation loc;
|
||||
union {
|
||||
irValueConstant Constant;
|
||||
irValueConstantSlice ConstantSlice;
|
||||
@@ -490,8 +506,6 @@ enum irDebugInfoKind {
|
||||
irDebugInfo_Proc,
|
||||
irDebugInfo_AllProcs,
|
||||
|
||||
irDebugInfo_Location,
|
||||
|
||||
irDebugInfo_BasicType, // basic types
|
||||
irDebugInfo_ProcType,
|
||||
irDebugInfo_DerivedType, // pointer, typedef
|
||||
@@ -534,11 +548,6 @@ struct irDebugInfo {
|
||||
struct {
|
||||
Array<irDebugInfo *> procs;
|
||||
} AllProcs;
|
||||
struct {
|
||||
irDebugInfo *scope;
|
||||
TokenPos pos;
|
||||
} Location;
|
||||
|
||||
|
||||
struct {
|
||||
String name;
|
||||
@@ -1070,18 +1079,6 @@ irValue *ir_instr_call(irProcedure *p, irValue *value, irValue *return_ptr, irVa
|
||||
v->Instr.Call.arg_count = arg_count;
|
||||
v->Instr.Call.type = result_type;
|
||||
v->Instr.Call.context_ptr = context_ptr;
|
||||
|
||||
irDebugInfo **pp = map_get(&p->module->debug_info, hash_entity(p->entity));
|
||||
if (pp != nullptr) {
|
||||
GB_ASSERT_MSG(pp != nullptr, "%.*s %p", LIT(p->name), p->entity);
|
||||
irDebugInfo *dl = ir_alloc_debug_info(p->module->allocator, irDebugInfo_Location);
|
||||
dl->Location.scope = *pp;
|
||||
dl->Location.pos = p->entity->token.pos;
|
||||
map_set(&p->module->debug_info, hash_pointer(v), dl);
|
||||
|
||||
v->Instr.Call.debug_location = dl;
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@@ -1100,9 +1097,9 @@ irValue *ir_instr_comment(irProcedure *p, String text) {
|
||||
return v;
|
||||
}
|
||||
|
||||
irValue *ir_instr_debug_declare(irProcedure *p, irDebugInfo *debug_info, AstNode *expr, Entity *entity, bool is_addr, irValue *value) {
|
||||
irValue *ir_instr_debug_declare(irProcedure *p, irDebugInfo *scope, AstNode *expr, Entity *entity, bool is_addr, irValue *value) {
|
||||
irValue *v = ir_alloc_instr(p, irInstr_DebugDeclare);
|
||||
v->Instr.DebugDeclare.debug_info = debug_info;
|
||||
v->Instr.DebugDeclare.scope = scope;
|
||||
v->Instr.DebugDeclare.expr = expr;
|
||||
v->Instr.DebugDeclare.entity = entity;
|
||||
v->Instr.DebugDeclare.is_addr = is_addr;
|
||||
@@ -1130,7 +1127,6 @@ irValue *ir_value_constant_slice(gbAllocator a, Type *type, irValue *backing_arr
|
||||
}
|
||||
|
||||
|
||||
|
||||
irValue *ir_emit(irProcedure *proc, irValue *instr) {
|
||||
GB_ASSERT(instr->kind == irValue_Instr);
|
||||
irBlock *b = proc->curr_block;
|
||||
@@ -1519,6 +1515,8 @@ irDebugInfo *ir_add_debug_info_proc(irProcedure *proc, Entity *entity, String na
|
||||
di->Proc.file = file;
|
||||
di->Proc.pos = entity->token.pos;
|
||||
|
||||
proc->debug_scope = di;
|
||||
|
||||
map_set(&proc->module->debug_info, hash_entity(entity), di);
|
||||
return di;
|
||||
}
|
||||
@@ -7665,7 +7663,10 @@ void ir_init_module(irModule *m, Checker *c) {
|
||||
m->tmp_allocator = gb_arena_allocator(&m->tmp_arena);
|
||||
m->info = &c->info;
|
||||
|
||||
m->generate_debug_info = build_context.ODIN_OS == "windows" && build_context.word_size == 8;
|
||||
m->generate_debug_info = false;
|
||||
if (build_context.debug) {
|
||||
m->generate_debug_info = build_context.ODIN_OS == "windows" && build_context.word_size == 8;
|
||||
}
|
||||
|
||||
map_init(&m->values, heap_allocator());
|
||||
map_init(&m->members, heap_allocator());
|
||||
|
||||
@@ -1465,8 +1465,19 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
|
||||
}
|
||||
ir_write_string(f, ")");
|
||||
|
||||
if (m->generate_debug_info && call->debug_location) {
|
||||
ir_fprintf(f, ", !dbg !%d", call->debug_location->id);
|
||||
if (m->generate_debug_info) {
|
||||
TokenPos pos = value->loc.pos;
|
||||
irDebugInfo *scope = value->loc.debug_scope;
|
||||
i32 id = 0;
|
||||
irProcedure *proc = instr->parent->parent;
|
||||
if (scope != nullptr) {
|
||||
id = scope->id;
|
||||
} else if (proc->debug_scope != nullptr) {
|
||||
id = proc->debug_scope->id;
|
||||
}
|
||||
if (id > 0) {
|
||||
ir_fprintf(f, ", !dbg !DILocation(line: %td, column: %td, scope: !%d)", pos.line, pos.column, id);
|
||||
}
|
||||
}
|
||||
|
||||
ir_write_string(f, "\n");
|
||||
@@ -1632,17 +1643,17 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
|
||||
#endif
|
||||
|
||||
case irInstr_DebugDeclare: {
|
||||
if (!m->generate_debug_info) {
|
||||
break;
|
||||
}
|
||||
|
||||
irInstrDebugDeclare *dd = &instr->DebugDeclare;
|
||||
Type *vt = ir_type(dd->value);
|
||||
irDebugInfo *di = dd->debug_info;
|
||||
irDebugInfo *di = dd->scope;
|
||||
Entity *e = dd->entity;
|
||||
String name = e->token.string;
|
||||
TokenPos pos = e->token.pos;
|
||||
|
||||
|
||||
if (!m->generate_debug_info) {
|
||||
ir_write_string(f, "; ");
|
||||
}
|
||||
ir_write_string(f, "call void @llvm.dbg.declare(");
|
||||
ir_write_string(f, "metadata ");
|
||||
ir_print_type(f, m, vt);
|
||||
@@ -1762,14 +1773,12 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (proc->entity != nullptr) {
|
||||
if (proc->body != nullptr) {
|
||||
irDebugInfo **di_ = map_get(&proc->module->debug_info, hash_pointer(proc->entity));
|
||||
if (di_ != nullptr) {
|
||||
irDebugInfo *di = *di_;
|
||||
GB_ASSERT(di->kind == irDebugInfo_Proc);
|
||||
ir_fprintf(f, "!dbg !%d ", di->id);
|
||||
}
|
||||
if (m->generate_debug_info && proc->entity != nullptr && proc->body != nullptr) {
|
||||
irDebugInfo **di_ = map_get(&proc->module->debug_info, hash_pointer(proc->entity));
|
||||
if (di_ != nullptr) {
|
||||
irDebugInfo *di = *di_;
|
||||
GB_ASSERT(di->kind == irDebugInfo_Proc);
|
||||
ir_fprintf(f, "!dbg !%d ", di->id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1846,14 +1855,15 @@ void print_llvm_ir(irGen *ir) {
|
||||
irModule *m = &ir->module;
|
||||
irFileBuffer buf = {}, *f = &buf;
|
||||
ir_file_buffer_init(f, &ir->output_file);
|
||||
defer (ir_file_buffer_destroy(f));
|
||||
|
||||
if (m->generate_debug_info) {
|
||||
ir_write_string(f, "target datalayout = \"e-m:w-i64:64-f80:128-n8:16:32:64-S128\"\n");
|
||||
ir_write_string(f, "target triple = \"x86_64-pc-windows-msvc19.11.25508\"\n\n");
|
||||
i32 word_bits = cast(i32)(8*build_context.word_size);
|
||||
ir_fprintf(f, "target datalayout = \"e-m:w-i%d:%d-f80:128-n8:16:32:64-S128\"\n", word_bits, word_bits);
|
||||
ir_fprintf(f, "target triple = \"x86%s-pc-windows-msvc17\"\n\n", word_bits == 64 ? "-64" : "");
|
||||
ir_fprintf(f, "\n\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
ir_print_encoded_local(f, str_lit("..opaque"));
|
||||
ir_write_string(f, str_lit(" = type {};\n"));
|
||||
ir_print_encoded_local(f, str_lit("..string"));
|
||||
@@ -1870,7 +1880,6 @@ void print_llvm_ir(irGen *ir) {
|
||||
ir_print_encoded_local(f, str_lit("..complex128"));
|
||||
ir_write_string(f, str_lit(" = type {double, double} ; Basic_complex128\n"));
|
||||
|
||||
|
||||
ir_print_encoded_local(f, str_lit("..any"));
|
||||
ir_write_string(f, str_lit(" = type {"));
|
||||
ir_print_type(f, m, t_rawptr);
|
||||
@@ -2016,16 +2025,17 @@ void print_llvm_ir(irGen *ir) {
|
||||
", runtimeVersion: 0"
|
||||
", isOptimized: false"
|
||||
", emissionKind: FullDebug"
|
||||
", retainedTypes: !0"
|
||||
", enums: !0"
|
||||
", globals: !0"
|
||||
")",
|
||||
file->id, LIT(build_context.ODIN_VERSION));
|
||||
|
||||
break;
|
||||
}
|
||||
case irDebugInfo_File:
|
||||
ir_fprintf(f, "!DIFile(filename: \"");
|
||||
ir_print_escape_path(f, di->File.filename);
|
||||
ir_fprintf(f, "\", directory: \"");
|
||||
ir_print_escape_path(f, di->File.directory);
|
||||
ir_fprintf(f, "!DIFile(filename: \""); ir_print_escape_path(f, di->File.filename);
|
||||
ir_fprintf(f, "\", directory: \""); ir_print_escape_path(f, di->File.directory);
|
||||
ir_fprintf(f, "\"");
|
||||
ir_fprintf(f, ")");
|
||||
break;
|
||||
@@ -2057,12 +2067,6 @@ void print_llvm_ir(irGen *ir) {
|
||||
ir_write_byte(f, '}');
|
||||
break;
|
||||
|
||||
case irDebugInfo_Location:
|
||||
GB_ASSERT(di->Location.scope != nullptr);
|
||||
ir_fprintf(f, "!DILocation(line: %td, column: %td, scope: !%d)",
|
||||
di->Location.pos.line, di->Location.pos.column, di->Location.scope->id);
|
||||
break;
|
||||
|
||||
default:
|
||||
GB_PANIC("Unhandled irDebugInfo kind %d", di->kind);
|
||||
break;
|
||||
@@ -2077,6 +2081,4 @@ void print_llvm_ir(irGen *ir) {
|
||||
ir_fprintf(f, "!%d = !{i32 2, !\"CodeView\", i32 1}\n", di_code_view);
|
||||
ir_fprintf(f, "!%d = !{i32 1, !\"wchar_size\", i32 2}\n", di_wchar_size);
|
||||
}
|
||||
|
||||
ir_file_buffer_destroy(f);
|
||||
}
|
||||
|
||||
40
src/main.cpp
40
src/main.cpp
@@ -209,6 +209,7 @@ enum BuildFlagKind {
|
||||
BuildFlag_KeepTempFiles,
|
||||
BuildFlag_Collection,
|
||||
BuildFlag_BuildMode,
|
||||
BuildFlag_Debug,
|
||||
|
||||
BuildFlag_COUNT,
|
||||
};
|
||||
@@ -244,6 +245,7 @@ bool parse_build_flags(Array<String> args) {
|
||||
add_flag(&build_flags, BuildFlag_KeepTempFiles, str_lit("keep-temp-files"), BuildFlagParam_None);
|
||||
add_flag(&build_flags, BuildFlag_Collection, str_lit("collection"), BuildFlagParam_String);
|
||||
add_flag(&build_flags, BuildFlag_BuildMode, str_lit("build-mode"), BuildFlagParam_String);
|
||||
add_flag(&build_flags, BuildFlag_Debug, str_lit("debug"), BuildFlagParam_None);
|
||||
|
||||
|
||||
Array<String> flag_args = args;
|
||||
@@ -483,6 +485,10 @@ bool parse_build_flags(Array<String> args) {
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case BuildFlag_Debug:
|
||||
build_context.debug = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -778,19 +784,24 @@ int main(int arg_count, char **arg_ptr) {
|
||||
}
|
||||
|
||||
char *output_ext = "exe";
|
||||
char *link_settings = "";
|
||||
gbString link_settings = gb_string_make_reserve(heap_allocator(), 256);
|
||||
defer (gb_string_free(link_settings));
|
||||
|
||||
if (build_context.is_dll) {
|
||||
output_ext = "dll";
|
||||
link_settings = "/DLL";
|
||||
link_settings = gb_string_append_fmt(link_settings, "/DLL");
|
||||
} else {
|
||||
link_settings = "/ENTRY:mainCRTStartup";
|
||||
link_settings = gb_string_append_fmt(link_settings, "/ENTRY:mainCRTStartup");
|
||||
}
|
||||
|
||||
if (build_context.debug) {
|
||||
link_settings = gb_string_append_fmt(link_settings, " /DEBUG /SYMBOLS");
|
||||
}
|
||||
|
||||
exit_code = system_exec_command_line_app("msvc-link", true,
|
||||
"link \"%.*s.obj\" -OUT:\"%.*s.%s\" %s "
|
||||
"/defaultlib:libcmt "
|
||||
// "/nodefaultlib "
|
||||
// "/debug "
|
||||
"/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
|
||||
" %.*s "
|
||||
" %s "
|
||||
@@ -840,9 +851,8 @@ int main(int arg_count, char **arg_ptr) {
|
||||
// NOTE(vassvik): needs to add the root to the library search paths, so that the full filenames of the library
|
||||
// files can be passed with -l:
|
||||
gbString lib_str = gb_string_make(heap_allocator(), "-L/");
|
||||
|
||||
defer (gb_string_free(lib_str));
|
||||
char lib_str_buf[1024] = {0};
|
||||
|
||||
for_array(i, ir_gen.module.foreign_library_paths) {
|
||||
String lib = ir_gen.module.foreign_library_paths[i];
|
||||
|
||||
@@ -850,19 +860,18 @@ int main(int arg_count, char **arg_ptr) {
|
||||
// This allows you to specify '-f' in a #foreign_system_library,
|
||||
// without having to implement any new syntax specifically for MacOS.
|
||||
#if defined(GB_SYSTEM_OSX)
|
||||
isize len;
|
||||
if(lib.len > 2 && lib[0] == '-' && lib[1] == 'f') {
|
||||
if (lib.len > 2 && lib[0] == '-' && lib[1] == 'f') {
|
||||
// framework thingie
|
||||
len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf), " -framework %.*s ", (int)(lib.len) - 2, lib.text + 2);
|
||||
lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", (int)(lib.len) - 2, lib.text + 2);
|
||||
} else if (string_has_extension(lib, str_lit("a"))) {
|
||||
// static libs, absolute full path relative to the file in which the lib was imported from
|
||||
len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf), " %.*s ", LIT(lib));
|
||||
lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
|
||||
} else if (string_has_extension(lib, str_lit("dylib"))) {
|
||||
// dynamic lib, relative path to executable
|
||||
len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf), " -l:%s/%.*s ", cwd, LIT(lib));
|
||||
lib_str = gb_string_append_fmt(lib_str, " -l:%s/%.*s ", cwd, LIT(lib));
|
||||
} else {
|
||||
// dynamic or static system lib, just link regularly searching system library paths
|
||||
len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf), " -l%.*s ", LIT(lib));
|
||||
lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
|
||||
}
|
||||
#else
|
||||
// NOTE(vassvik): static libraries (.a files) in linux can be linked to directly using the full path,
|
||||
@@ -872,18 +881,17 @@ int main(int arg_count, char **arg_ptr) {
|
||||
// the system library paths for the library file).
|
||||
if (string_has_extension(lib, str_lit("a"))) {
|
||||
// static libs, absolute full path relative to the file in which the lib was imported from
|
||||
isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf), " -l:%.*s ", LIT(lib));
|
||||
lib_str = gb_string_append_fmt(lib_str, " -l:%.*s ", LIT(lib));
|
||||
} else if (string_has_extension(lib, str_lit("so"))) {
|
||||
// dynamic lib, relative path to executable
|
||||
// NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible
|
||||
// at runtimeto the executable
|
||||
isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf), " -l:%s/%.*s ", cwd, LIT(lib));
|
||||
lib_str = gb_string_append_fmt(lib_str, " -l:%s/%.*s ", cwd, LIT(lib));
|
||||
} else {
|
||||
// dynamic or static system lib, just link regularly searching system library paths
|
||||
isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf), " -l%.*s ", LIT(lib));
|
||||
lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
|
||||
}
|
||||
#endif
|
||||
lib_str = gb_string_appendc(lib_str, lib_str_buf);
|
||||
}
|
||||
|
||||
// Unlike the Win32 linker code, the output_ext includes the dot, because
|
||||
|
||||
@@ -4679,7 +4679,7 @@ Array<AstNode *> parse_stmt_list(AstFile *f) {
|
||||
|
||||
ParseFileError init_ast_file(AstFile *f, String fullpath, TokenPos *err_pos) {
|
||||
f->fullpath = string_trim_whitespace(fullpath); // Just in case
|
||||
if (!string_has_extension(f->fullpath, str_lit("odin"))) {
|
||||
if (!string_ends_with(f->fullpath, str_lit(".odin"))) {
|
||||
return ParseFile_WrongExtension;
|
||||
}
|
||||
TokenizerInitError err = init_tokenizer(&f->tokenizer, f->fullpath);
|
||||
|
||||
@@ -180,17 +180,20 @@ template <isize N> bool operator >= (String const &a, char const (&b)[N]) { retu
|
||||
|
||||
|
||||
|
||||
gb_inline bool str_has_prefix(String s, String prefix) {
|
||||
isize i;
|
||||
if (prefix.len < s.len) {
|
||||
gb_inline bool string_starts_with(String s, String prefix) {
|
||||
if (prefix.len > s.len) {
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < prefix.len; i++) {
|
||||
if (s[i] != prefix[i]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return substring(s, 0, prefix.len) == prefix;
|
||||
}
|
||||
|
||||
gb_inline bool string_ends_with(String s, String suffix) {
|
||||
if (suffix.len > s.len) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
return substring(s, s.len-suffix.len, s.len) == suffix;
|
||||
}
|
||||
|
||||
gb_inline isize string_extension_position(String str) {
|
||||
@@ -225,26 +228,6 @@ String string_trim_whitespace(String str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
gb_inline bool string_has_extension(String str, String ext) {
|
||||
str = string_trim_whitespace(str);
|
||||
if (str.len <= ext.len+1) {
|
||||
return false;
|
||||
}
|
||||
isize len = str.len;
|
||||
for (isize i = len-1; i >= 0; i--) {
|
||||
if (str[i] == '.') {
|
||||
break;
|
||||
}
|
||||
len--;
|
||||
}
|
||||
if (len == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
u8 *s = str.text + len;
|
||||
return gb_memcompare(s, ext.text, ext.len) == 0;
|
||||
}
|
||||
|
||||
bool string_contains_char(String s, u8 c) {
|
||||
isize i;
|
||||
for (i = 0; i < s.len; i++) {
|
||||
|
||||
Reference in New Issue
Block a user