mcap/modules/Basic/Simple_String.jai

296 lines
7.9 KiB
Plaintext

string_view :: inline (s: string, start_index: s64, view_count: s64) -> string {
assert(view_count >= 0 && start_index >= 0);
if view_count < 0 || start_index < 0 || start_index >= s.count return "";
new_count := view_count;
if start_index + view_count > s.count {
new_count = s.count - start_index;
}
return { new_count, s.data + start_index };
}
to_string :: inline (s: *u8, length: s64) -> string { // We should probably deprecate this in favor of your using string.{...}?
if !s return "";
result: string = ---;
result.count = length;
result.data = s;
return result;
}
// Intended for C strings in fixed-size arrays that may or may not be zero-terminated.
// It stops at the first zero in c_string.data but does not exceed c_string.count.
to_string :: (c_string: [] u8) -> string {
if c_string.count == 0 return "";
result: string = ---;
result.data = c_string.data;
length := 0;
while c_string[length] && length < c_string.count {
length += 1;
}
result.count = length;
return result;
}
// The following functions:
//
// to_string :: inline (s: *u8) -> string;
// c_style_strlen :: (x: *u8) -> s64;
//
// are in Runtime_Support.jai and can be called from anywhere.
// Allocates memory.
to_c_string :: (s: string) -> *u8 {
result : *u8 = alloc(s.count + 1);
memcpy(result, s.data, s.count);
result[s.count] = 0;
return result;
}
// Note: Right now when code is generated, c + A - a
// doesn't get constexpr evaluation evoked on the A - a
// because it parses into the AST as (c + A) - a, and we
// do not do tree rearrangement for constexpr evaluation.
// (And I am not sure that we should). -jblow, 25 April 2018.
// Maybe to_upper, to_lower, etc should not even be in Basic... -jblow
// These are locale indepdent, only support english characters, and do not support utf8.
to_upper :: (c: u8) -> u8 {
if c >= #char "a" && c <= #char "z" return c + #char "A" - #char "a";
else return c;
}
to_lower :: (c: u8) -> u8 {
if (c >= #char "A" && c <= #char "Z") return c + #char "a" - #char "A";
else return c;
}
is_alpha :: (c: u8) -> bool {
return (c >= #char "a" && c <= #char "z") || (c >= #char "A" && c <= #char "Z");
}
is_digit :: (c: u8) -> bool {
return c >= #char "0" && c <= #char "9";
}
is_alnum :: (c: u8) -> bool { // This also includes underscore!
return is_alpha(c) || is_digit(c) || c == #char "_";
}
is_space :: (c: u8) -> bool {
return c == #char " " || c == #char "\t" || c == #char "\r" || c == #char "\n";
}
is_print :: (c: u8) -> bool {
// True for any character from space to tilde (DEL not included).
return c >= 0x20 && c < 0x7F;
}
advance :: inline (s: *string, amount: s64 = 1) {
assert(amount >= 0);
assert(s.count >= amount);
s.count -= amount;
s.data += amount;
}
advance :: inline (s: string, amount: s64 = 1) -> string {
assert(amount >= 0);
assert(s.count >= amount);
t: string = ---;
t.count = s.count - amount;
t.data = s.data + amount;
return t;
}
temp_c_string :: (s: string) -> *u8 {
result : *u8 = temporary_alloc(s.count + 1);
memcpy(result, s.data, s.count);
result[s.count] = 0;
return result;
}
copy_temporary_string :: inline (s: string) -> string {
return copy_string(s,, temp);
}
copy_string :: (s: string) -> string {
if !s.count return "";
copy := alloc_string(s.count);
if !copy.data return "";
memcpy(copy.data, s.data, s.count);
return copy;
}
/*
@Bug: We want to have string_to_float be parameterized,
but it doesn't quite work consistently for some reason.
In both LLVM and x64, the procedures appear to return
0 sometimes (but not always, depending on what is going on
in the calling procedure). -jblow, 19 February 2017
Test code:
f1 := string_to_float("42.6");
print("f1 is type %, value %\n", type_of(f1), f1);
f2 := string_to_float64("42.6");
print("f2 is type %, value %\n", type_of(f2), f2);
f3 := string_to_float_old("42.6");
print("f3 is type %, value %\n", type_of(f3), f3);
string_to_float :: #bake_constants string_to_float_type_T (T = float);
string_to_float64 :: #bake_constants string_to_float_type_T (T = float64);
string_to_float_type_T :: (_arg: string) -> result: $T, success: bool, remainder: string {
arg := eat_spaces(_arg);
if !arg {
return 0, false, arg;
}
found_decimal := false;
// @Temporary:
// See how much of the string is a digit, +, -, ., or e.
limit := arg.count;
for 0..arg.count-1 {
c := arg[it];
if (c >= #char "0") && (c <= #char "9") {
// We are good.
} else if c == #char "." {
// We are good.
} else if (c == #char "-") || (c == #char "+") || (c == #char "e") {
// We are good.
} else {
limit = it;
break;
}
}
substring := arg;
substring.count = limit;
remainder := arg;
advance(*remainder, limit);
// @Temporary @Speed @Ugh
cs := temp_c_string(substring);
result := cast(T) atof(cs);
return result, true, remainder;
}
*/
//
// @Cleanup! We would like to just do:
//
// to_integer :: #bake_arguments string_to_int(base=10);
//
// But this causes dead code elimination to think string_to_int is live,
// even if it's not. So it would be good to come up with a way to make
// this work better! In the meantime...
//
to_integer :: (s: string) -> result: int, success: bool, remainder: string {
result, success, remainder := string_to_int(s);
return result, success, remainder;
}
string_to_int :: (t: string, base := 10, $T := int) -> result: T, success: bool, remainder: string {
//
// This string_to_int might be a little slower than max speed because we multiply by 'sign' for
// each digit. Our original approach did not do this, but rather we just accumulated into a
// positive value and then negated at the end if we saw a -. But this does not let us parse the
// maximum negative number without overflowing the positive number, which works, *if* you are
// not doing arithmetic bounds checks. We could have done #no_abc on this procedure, but
// I decided it's worthwhile to just try to preserve the ability to report genuine overflows,
// so I did 'the clean version' here. -jblow, 4 July 2023
//
assert(base == 16 || base <= 10);
s := eat_spaces(t);
if !s return 0, false, "";
sign : T = 1;
#if #run type_info(T).signed {
if s[0] == #char "-" {
sign = -1;
advance(*s, 1);
s = eat_spaces(s);
} else if s[0] == #char "+" {
advance(*s, 1);
s = eat_spaces(s);
}
}
sum: T = 0;
cursor := 0;
// @Incomplete: We should probably check for overflows…
if base == 16 {
while cursor < s.count {
c := s[cursor];
value: u8 = ---;
if is_digit(c) {
value = c - #char "0";
} else if (c >= #char "a") && (c <= #char "f") {
value = c - #char "a" + 10;
} else if (c >= #char "A") && (c <= #char "F") {
value = c - #char "A" + 10;
} else {
break;
}
sum *= cast(T) base;
sum += sign * cast(T)value;
cursor += 1;
}
} else {
while cursor < s.count {
c := s[cursor];
if !is_digit(c) break;
digit := c - #char "0";
if digit >= base break;
sum *= cast(T) base;
sum += sign*cast(T)digit;
cursor += 1;
}
}
success := (cursor != 0);
advance(*s, cursor);
return sum, success, s;
}
eat_spaces :: (_s: string) -> string {
s := _s;
while s {
if (s[0] != #char " ") && (s[0] != 9) break; // 9 is horizontal tab.
advance(*s, 1);
}
return s;
}