diff --git a/lib/std/core/str.c3 b/lib/std/core/str.c3 index 8bddc8b5f..4df5b2e87 100644 --- a/lib/std/core/str.c3 +++ b/lib/std/core/str.c3 @@ -11,6 +11,13 @@ private const uint SURROGATE_BITS = 10; private const uint SURROGATE_LOW_VALUE = 0xDC00; private const uint SURROGATE_HIGH_VALUE = 0xD800; +fault NumberConversion +{ + EMPTY_STRING, + NEGATIVE_VALUE, + MALFORMED_INTEGER, + INTEGER_OVERFLOW, +} fn String join(char[][] s, char[] joiner) { if (!s.len) return (String)null; @@ -37,6 +44,89 @@ macro bool char_in_set(char c, char[] set) } return false; } + +private macro char_is_space_tab(char c) +{ + return c == ' ' || c == '\t'; +} + +private macro to_signed_integer($Type, char[] string) +{ + usz len = string.len; + usz index = 0; + char* ptr = string.ptr; + while (index < len && char_is_space_tab(ptr[index])) index++; + if (len == index) return NumberConversion.EMPTY_STRING!; + bool is_negative; + switch (string[index]) + { + case '-': + if ($Type.min == 0) return NumberConversion.NEGATIVE_VALUE!; + is_negative = true; + index++; + case '+': + index++; + default: + break; + } + if (len == index) return NumberConversion.MALFORMED_INTEGER!; + $Type base = 10; + if (string[index] == '0') + { + index++; + if (index == len) return ($Type)0; + switch (string[index]) + { + case 'x': + case 'X': + base = 16; + index++; + case 'b': + case 'B': + base = 2; + index++; + case 'o': + case 'O': + base = 8; + index++; + default: + break; + } + if (len == index) return NumberConversion.MALFORMED_INTEGER!; + } + $Type value = 0; + while (index != len) + { + char c = {| + char ch = string[index++]; + if (base != 16 || ch < 'A') return (char)(ch - '0'); + if (ch <= 'F') return (char)(ch - 'A'); + if (ch < 'a') return NumberConversion.MALFORMED_INTEGER!; + if (ch > 'f') return NumberConversion.MALFORMED_INTEGER!; + return (char)(ch - 'a'); + |}?; + if (c >= base) return NumberConversion.MALFORMED_INTEGER!; + value = {| + if (is_negative) + { + $Type new_value = value * base - c; + if (new_value > value) return NumberConversion.INTEGER_OVERFLOW!; + return new_value; + } + $Type new_value = value * base + c; + if (new_value < value) return NumberConversion.INTEGER_OVERFLOW!; + return new_value; + |}?; + } + return value; +} + +fn int128! to_int128(char[] string) = to_signed_integer(int128, string); +fn long! to_long(char[] string) = to_signed_integer(long, string); +fn int! to_int(char[] string) = to_signed_integer(int, string); +fn short! to_short(char[] string) = to_signed_integer(short, string); +fn ichar! to_ichar(char[] string) = to_signed_integer(ichar, string); + fn char[] trim(char[] string, char[] to_trim = "\t\n\r ") { usz start = 0; diff --git a/lib/std/io/io.c3 b/lib/std/io/io.c3 index db76bb057..4ca624869 100644 --- a/lib/std/io/io.c3 +++ b/lib/std/io/io.c3 @@ -179,12 +179,26 @@ fn String File.getline(File* file, Allocator* allocator = mem::current_allocator while (!file.eof()) { int c = libc::fgetc(file.file); + if (c == -1) break; if (c == '\n') break; s.append_char((char)c); } return s; } +/** + * @param [&in] file + * @require file.file `File must be initialized` + * @return "a zero terminated char[] (the pointer may be safely cast into a ZString)" + */ +fn char[] File.tgetline(File* file) +{ + + String s = file.getline(mem::temp_allocator()); + ZString z = s.zstr(); + return z[:s.len()]; +} + fn char! File.getc(File* file) { int c = libc::fgetc(file.file); diff --git a/src/version.h b/src/version.h index fee63c160..fc8f3cbdf 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.3.102" \ No newline at end of file +#define COMPILER_VERSION "0.3.103" \ No newline at end of file