mirror of
https://github.com/odin-lang/Odin.git
synced 2026-07-17 21:21:04 +00:00
Merge branch 'master' into windows-llvm-11.1.0
This commit is contained in:
@@ -3,9 +3,7 @@ package strings
|
||||
|
||||
import "core:io"
|
||||
import "core:mem"
|
||||
import "core:slice"
|
||||
import "core:unicode"
|
||||
import "core:runtime"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
// returns a clone of the string `s` allocated using the `allocator`
|
||||
@@ -996,6 +994,36 @@ last_index_any :: proc(s, chars: string) -> int {
|
||||
return -1
|
||||
}
|
||||
|
||||
index_multi :: proc(s: string, substrs: []string) -> (idx: int, width: int) {
|
||||
idx = -1
|
||||
if s == "" || len(substrs) <= 0 {
|
||||
return
|
||||
}
|
||||
// disallow "" substr
|
||||
for substr in substrs {
|
||||
if len(substr) == 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
lowest_index := len(s)
|
||||
found := false
|
||||
for substr in substrs {
|
||||
if i := index(s, substr); i >= 0 {
|
||||
if i < lowest_index {
|
||||
lowest_index = i
|
||||
width = len(substr)
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
idx = lowest_index
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
returns the count of the string `substr` found in the string `s`
|
||||
returns the rune_count + 1 of the string `s` on empty `substr`
|
||||
@@ -1412,8 +1440,58 @@ trim_suffix :: proc(s, suffix: string) -> string {
|
||||
res := strings.split_multi("testing,this.out_nice---done~~~last", splits[:])
|
||||
fmt.eprintln(res) // -> [testing, this, out, nice, done, last]
|
||||
*/
|
||||
split_multi :: proc(s: string, substrs: []string, allocator := context.allocator) -> (buf: []string) #no_bounds_check {
|
||||
split_multi :: proc(s: string, substrs: []string, allocator := context.allocator) -> []string #no_bounds_check {
|
||||
if s == "" || len(substrs) <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// disallow "" substr
|
||||
for substr in substrs {
|
||||
if len(substr) == 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// calculate the needed len of `results`
|
||||
n := 1
|
||||
for it := s; len(it) > 0; {
|
||||
i, w := index_multi(it, substrs)
|
||||
if i < 0 {
|
||||
break
|
||||
}
|
||||
n += 1
|
||||
it = it[i+w:]
|
||||
}
|
||||
|
||||
results := make([dynamic]string, 0, n, allocator)
|
||||
{
|
||||
it := s
|
||||
for len(it) > 0 {
|
||||
i, w := index_multi(it, substrs)
|
||||
if i < 0 {
|
||||
break
|
||||
}
|
||||
part := it[:i]
|
||||
append(&results, part)
|
||||
it = it[i+w:]
|
||||
}
|
||||
append(&results, it)
|
||||
}
|
||||
assert(len(results) == n)
|
||||
return results[:]
|
||||
}
|
||||
|
||||
/*
|
||||
splits the input string `s` by all possible `substrs` []string in an iterator fashion
|
||||
returns the split string every iteration, the full string on no match
|
||||
splits := [?]string { "---", "~~~", ".", "_", "," }
|
||||
it := "testing,this.out_nice---done~~~last"
|
||||
for str in strings.split_multi_iterate(&it, splits[:]) {
|
||||
fmt.eprintln(str) // every iteration -> [testing, this, out, nice, done, last]
|
||||
}
|
||||
*/
|
||||
split_multi_iterate :: proc(it: ^string, substrs: []string) -> (res: string, ok: bool) #no_bounds_check {
|
||||
if it == nil || len(it) == 0 || len(substrs) <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1424,130 +1502,17 @@ split_multi :: proc(s: string, substrs: []string, allocator := context.allocator
|
||||
}
|
||||
}
|
||||
|
||||
// TODO maybe remove duplicate substrs
|
||||
// sort substrings by string size, largest to smallest
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
temp_substrs := slice.clone(substrs, context.temp_allocator)
|
||||
|
||||
slice.sort_by(temp_substrs, proc(a, b: string) -> bool {
|
||||
return len(a) > len(b)
|
||||
})
|
||||
|
||||
substrings_found: int
|
||||
temp := s
|
||||
|
||||
// count substr results found in string
|
||||
first_pass: for len(temp) > 0 {
|
||||
for substr in temp_substrs {
|
||||
size := len(substr)
|
||||
|
||||
// check range and compare string to substr
|
||||
if size <= len(temp) && temp[:size] == substr {
|
||||
substrings_found += 1
|
||||
temp = temp[size:]
|
||||
continue first_pass
|
||||
}
|
||||
}
|
||||
|
||||
// step through string
|
||||
_, skip := utf8.decode_rune_in_string(temp[:])
|
||||
temp = temp[skip:]
|
||||
// calculate the needed len of `results`
|
||||
i, w := index_multi(it^, substrs)
|
||||
if i >= 0 {
|
||||
res = it[:i]
|
||||
it^ = it[i+w:]
|
||||
} else {
|
||||
// last value
|
||||
res = it^
|
||||
it^ = it[len(it):]
|
||||
}
|
||||
|
||||
// skip when no results
|
||||
if substrings_found < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
buf = make([]string, substrings_found + 1, allocator)
|
||||
buf_index: int
|
||||
temp = s
|
||||
temp_old := temp
|
||||
|
||||
// gather results in the same fashion
|
||||
second_pass: for len(temp) > 0 {
|
||||
for substr in temp_substrs {
|
||||
size := len(substr)
|
||||
|
||||
// check range and compare string to substr
|
||||
if size <= len(temp) && temp[:size] == substr {
|
||||
buf[buf_index] = temp_old[:len(temp_old) - len(temp)]
|
||||
buf_index += 1
|
||||
temp = temp[size:]
|
||||
temp_old = temp
|
||||
continue second_pass
|
||||
}
|
||||
}
|
||||
|
||||
// step through string
|
||||
_, skip := utf8.decode_rune_in_string(temp[:])
|
||||
temp = temp[skip:]
|
||||
}
|
||||
|
||||
buf[buf_index] = temp_old[:]
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
// state for the split multi iterator
|
||||
Split_Multi :: struct {
|
||||
temp: string,
|
||||
temp_old: string,
|
||||
substrs: []string,
|
||||
}
|
||||
|
||||
// returns split multi state with sorted `substrs`
|
||||
split_multi_init :: proc(s: string, substrs: []string) -> Split_Multi {
|
||||
// sort substrings, largest to smallest
|
||||
temp_substrs := slice.clone(substrs, context.temp_allocator)
|
||||
slice.sort_by(temp_substrs, proc(a, b: string) -> bool {
|
||||
return len(a) > len(b)
|
||||
})
|
||||
|
||||
return {
|
||||
temp = s,
|
||||
temp_old = s,
|
||||
substrs = temp_substrs,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
splits the input string `s` by all possible `substrs` []string in an iterator fashion
|
||||
returns the split string every iteration, the full string on no match
|
||||
|
||||
splits := [?]string { "---", "~~~", ".", "_", "," }
|
||||
state := strings.split_multi_init("testing,this.out_nice---done~~~last", splits[:])
|
||||
for str in strings.split_multi_iterate(&state) {
|
||||
fmt.eprintln(str) // every iteration -> [testing, this, out, nice, done, last]
|
||||
}
|
||||
*/
|
||||
split_multi_iterate :: proc(using sm: ^Split_Multi) -> (res: string, ok: bool) #no_bounds_check {
|
||||
pass: for len(temp) > 0 {
|
||||
for substr in substrs {
|
||||
size := len(substr)
|
||||
|
||||
// check range and compare string to substr
|
||||
if size <= len(temp) && temp[:size] == substr {
|
||||
res = temp_old[:len(temp_old) - len(temp)]
|
||||
temp = temp[size:]
|
||||
temp_old = temp
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// step through string
|
||||
_, skip := utf8.decode_rune_in_string(temp[:])
|
||||
temp = temp[skip:]
|
||||
}
|
||||
|
||||
// allow last iteration
|
||||
if temp_old != "" {
|
||||
res = temp_old[:]
|
||||
ok = true
|
||||
temp_old = ""
|
||||
}
|
||||
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -337,8 +337,6 @@ struct lbProcedure {
|
||||
|
||||
LLVMMetadataRef debug_info;
|
||||
|
||||
lbAddr current_elision_hint;
|
||||
|
||||
PtrMap<Ast *, lbValue> selector_values;
|
||||
PtrMap<Ast *, lbAddr> selector_addr;
|
||||
PtrMap<LLVMValueRef, lbTupleFix> tuple_fix_map;
|
||||
|
||||
@@ -485,13 +485,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
LLVMValueRef ptr = LLVMBuildInBoundsGEP2(p->builder, llvm_type, array_data, indices, 2, "");
|
||||
LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), count, true);
|
||||
|
||||
lbAddr slice = {};
|
||||
if (p->current_elision_hint.addr.value && are_types_identical(lb_addr_type(p->current_elision_hint), type)) {
|
||||
slice = p->current_elision_hint;
|
||||
p->current_elision_hint = {};
|
||||
} else {
|
||||
slice = lb_add_local_generated(p, type, false);
|
||||
}
|
||||
lbAddr slice = lb_add_local_generated(p, type, false);
|
||||
map_set(&m->exact_value_compound_literal_addr_map, value.value_compound, slice);
|
||||
|
||||
lb_fill_slice(p, slice, {ptr, alloc_type_pointer(elem)}, {len, t_int});
|
||||
|
||||
@@ -2269,54 +2269,56 @@ gb_internal void lb_build_stmt(lbProcedure *p, Ast *node) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lvals = array_make<lbAddr>(permanent_allocator(), 0, vd->names.count);
|
||||
|
||||
for (Ast *name : vd->names) {
|
||||
lbAddr lval = {};
|
||||
if (!is_blank_ident(name)) {
|
||||
Entity *e = entity_of_node(name);
|
||||
// bool zero_init = true; // Always do it
|
||||
bool zero_init = vd->values.count == 0;
|
||||
lval = lb_add_local(p, e->type, e, zero_init);
|
||||
}
|
||||
array_add(&lvals, lval);
|
||||
}
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
|
||||
auto const &values = vd->values;
|
||||
if (values.count > 0) {
|
||||
auto inits = array_make<lbValue>(permanent_allocator(), 0, lvals.count);
|
||||
if (values.count == 0) {
|
||||
auto lvals = slice_make<lbAddr>(temporary_allocator(), vd->names.count);
|
||||
for_array(i, vd->names) {
|
||||
Ast *name = vd->names[i];
|
||||
if (!is_blank_ident(name)) {
|
||||
Entity *e = entity_of_node(name);
|
||||
// bool zero_init = true; // Always do it
|
||||
bool zero_init = values.count == 0;
|
||||
lvals[i] = lb_add_local(p, e->type, e, zero_init);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
auto lvals_preused = slice_make<bool>(temporary_allocator(), vd->names.count);
|
||||
auto lvals = slice_make<lbAddr>(temporary_allocator(), vd->names.count);
|
||||
auto inits = array_make<lbValue>(temporary_allocator(), 0, lvals.count);
|
||||
|
||||
isize lval_index = 0;
|
||||
for (Ast *rhs : values) {
|
||||
p->current_elision_hint = lvals[lval_index];
|
||||
|
||||
rhs = unparen_expr(rhs);
|
||||
lbValue init = lb_build_expr(p, rhs);
|
||||
#if 1
|
||||
if (p->current_elision_hint.addr.value != lvals[lval_index].addr.value) {
|
||||
lvals[lval_index] = {}; // do nothing so that nothing will assign to it
|
||||
} else {
|
||||
|
||||
if (rhs->kind == Ast_CompoundLit) {
|
||||
// NOTE(bill, 2023-02-17): lb_const_value might produce a stack local variable for the
|
||||
// compound literal, so reusing that variable should minimize the stack wastage
|
||||
if (rhs->kind == Ast_CompoundLit) {
|
||||
lbAddr *comp_lit_addr = map_get(&p->module->exact_value_compound_literal_addr_map, rhs);
|
||||
if (comp_lit_addr) {
|
||||
Entity *e = entity_of_node(vd->names[lval_index]);
|
||||
if (e) {
|
||||
GB_ASSERT(p->current_elision_hint.addr.value == nullptr);
|
||||
GB_ASSERT(p->current_elision_hint.addr.value != lvals[lval_index].addr.value);
|
||||
lvals[lval_index] = {}; // do nothing so that nothing will assign to it
|
||||
}
|
||||
lbAddr *comp_lit_addr = map_get(&p->module->exact_value_compound_literal_addr_map, rhs);
|
||||
if (comp_lit_addr) {
|
||||
if (Entity *e = entity_of_node(vd->names[lval_index])) {
|
||||
lbValue val = comp_lit_addr->addr;
|
||||
lb_add_entity(p->module, e, val);
|
||||
lb_add_debug_local_variable(p, val.value, e->type, e->token);
|
||||
lvals_preused[lval_index] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
lval_index += lb_append_tuple_values(p, &inits, init);
|
||||
}
|
||||
GB_ASSERT(lval_index == lvals.count);
|
||||
|
||||
p->current_elision_hint = {};
|
||||
for_array(i, vd->names) {
|
||||
Ast *name = vd->names[i];
|
||||
if (!is_blank_ident(name) && !lvals_preused[i]) {
|
||||
Entity *e = entity_of_node(name);
|
||||
bool zero_init = values.count == 0;
|
||||
lvals[i] = lb_add_local(p, e->type, e, zero_init);
|
||||
}
|
||||
}
|
||||
|
||||
GB_ASSERT(lvals.count == inits.count);
|
||||
for_array(i, inits) {
|
||||
|
||||
Reference in New Issue
Block a user