From 190932935c3f6e4398934ec766214b61adc1bbca Mon Sep 17 00:00:00 2001 From: Tetralux Date: Thu, 20 Feb 2020 02:42:27 +0000 Subject: [PATCH 1/3] Refactor `parse_*` procedures: arbitrary bases, and return (value, rest_of_string) --- core/strconv/strconv.odin | 212 +++++++++++++++++++++++++++++++------- 1 file changed, 177 insertions(+), 35 deletions(-) diff --git a/core/strconv/strconv.odin b/core/strconv/strconv.odin index d4bf5464c..caadd2911 100644 --- a/core/strconv/strconv.odin +++ b/core/strconv/strconv.odin @@ -23,7 +23,53 @@ _digit_value :: proc(r: rune) -> int { return v; } -parse_i64 :: proc(str: string) -> i64 { +// Parses an integer value from a string, in the given base, without a prefix. +// ``` +// n, rest := strconv.parse_i64_of_base("-1234eeee", 10); +// assert(n == -1234 && rest == "eeee"); +// ``` +parse_i64_of_base :: proc(str: string, base: int) -> (value: i64, rest: string) { + assert(base <= 16, "base must be 1-16"); + s := str; + neg := false; + if len(s) > 1 { + switch s[0] { + case '-': + neg = true; + s = s[1:]; + case '+': + s = s[1:]; + } + } + + i := 0; + reached_end := true; + for r in s { + i += 1; + if r == '_' do continue; + v := i64(_digit_value(r)); + if v >= i64(base) { + reached_end = false; + break; + } + value *= i64(base); + value += v; + } + + if neg do value = -value; + rest = reached_end ? "" : s[i-1:]; + return; +} + +// Parses a integer value from a string, in base 10, unless there's a prefix. +// ``` +// n, rest := strconv.parse_i64_maybe_prefixed("1234"); +// assert(n == 1234 && rest == ""); +// +// n, rest = strconv.parse_i64_maybe_prefixed("0xeeee"); +// assert(n == 0xeeee && rest == ""); +// ``` +parse_i64_maybe_prefixed :: proc(str: string) -> (value: i64, rest: string) { s := str; neg := false; if len(s) > 1 { @@ -48,28 +94,71 @@ parse_i64 :: proc(str: string) -> i64 { } } - - value: i64; + i := 0; + reached_end := true; for r in s { - if r == '_' { - continue; - } - + i += 1; + if r == '_' do continue; v := i64(_digit_value(r)); if v >= base { + reached_end = false; break; } value *= base; value += v; } - if neg do return -value; - return value; + if neg do value = -value; + rest = reached_end ? "" : s[i-1:]; + return; } -parse_u64 :: proc(str: string) -> u64 { +parse_i64 :: proc{parse_i64_maybe_prefixed, parse_i64_of_base}; + +// Parses an unsigned integer value from a string, in the given base, and +// without a prefix. +// ``` +// n, rest := strconv.parse_u64_of_base("1234eeee", 10); +// assert(n == 1234 && rest == "eeee"); +// +// n, rest = strconv.parse_u64_of_base("1234eeee", 16); +// assert(n == 0x1234eeee && rest == ""); +// ``` +parse_u64_of_base :: proc(str: string, base: int) -> (value: u64, rest: string) { + assert(base <= 16, "base must be 1-16"); + s := str; + if len(s) > 1 && s[0] == '+' { + s = s[1:]; + } + + i := 0; + reached_end := true; + for r in s { + i += 1; + if r == '_' do continue; + v := u64(_digit_value(r)); + if v >= u64(base) { + reached_end = false; + break; + } + value *= u64(base); + value += v; + } + + rest = reached_end ? "" : s[i-1:]; + return; +} + +// Parses an unsigned integer value from a string in base 10, unless there's a prefix. +// ``` +// n, rest := strconv.parse_u64_maybe_prefixed("1234"); +// assert(n == 1234 && rest == ""); +// +// n, rest = strconv.parse_u64_maybe_prefixed("0xeeee"); +// assert(n == 0xeeee && rest == ""); +// ``` +parse_u64_maybe_prefixed :: proc(str: string) -> (value: u64, rest: string) { s := str; - neg := false; if len(s) > 1 && s[0] == '+' { s = s[1:]; } @@ -86,36 +175,75 @@ parse_u64 :: proc(str: string) -> u64 { } } - - value: u64; + i := 0; + reached_end := true; for r in s { + i += 1; if r == '_' do continue; v := u64(_digit_value(r)); - if v >= base do break; + if v >= base { + reached_end = false; + break; + } value *= base; value += u64(v); } - if neg do return -value; - return value; + rest = reached_end ? "" : s[i-1:]; + return; +} + +parse_u64 :: proc{parse_u64_maybe_prefixed, parse_u64_of_base}; + +// Parses an integer value of the given base, or the base implied by a prefix. +// If base is zero, assumes base 10, unless there's a prefix. e.g. '0x' for hexadecimal. +// +// ``` +// n, rest := strconv.parse_int("1234"); // without prefix, inferred base 10 +// assert(n == 1234 && rest == ""); +// +// n, rest = strconv.parse_int("ffff", 16); // without prefix, explicit base +// assert(n == 0xffff && rest == ""); +// +// n, rest = strconv.parse_int("0xffff"); // with prefix and inferred base +// assert(n == 0xffff && rest == ""); +// ``` +parse_int :: proc(s: string, base := 0) -> (value: int, rest: string) { + v: i64 = ---; + switch base { + case 0: v, rest = parse_i64_maybe_prefixed(s); + case: v, rest = parse_i64_of_base(s, base); + } + value = int(v); + return; +} +parse_uint :: proc(s: string, base := 0) -> (value: uint, rest: string) { + v: u64 = ---; + switch base { + case 0: v, rest = parse_u64_maybe_prefixed(s); + case: v, rest = parse_u64_of_base(s, base); + } + value = uint(v); + return; } -parse_int :: proc(s: string) -> int { - return int(parse_i64(s)); -} -parse_uint :: proc(s: string) -> uint { - return uint(parse_u64(s)); +parse_f32 :: proc(s: string) -> (value: f32, rest: string) { + v, r := parse_f64(s); + return f32(v), r; } -parse_f32 :: proc(s: string) -> f32 { - return f32(parse_f64(s)); -} - - -parse_f64 :: proc(s: string) -> f64 { +// Parses a floating point number from a string. +// ``` +// n, rest := strconv.parse_f64("12.34eee"); +// assert(n == 12.34 && rest == "eee"); +// +// n, rest = strconv.parse_f64("12.34"); +// assert(n == 12.34 && rest == ""); +// ``` +parse_f64 :: proc(s: string) -> (value: f64, rest: string) { if s == "" { - return 0; + return; } i := 0; @@ -125,7 +253,6 @@ parse_f64 :: proc(s: string) -> f64 { case '+': i += 1; } - value: f64 = 0; for ; i < len(s); i += 1 { r := rune(s[i]); if r == '_' do continue; @@ -136,6 +263,7 @@ parse_f64 :: proc(s: string) -> f64 { value += f64(v); } + reached_end := true; if i < len(s) && s[i] == '.' { pow10: f64 = 10; i += 1; @@ -145,7 +273,10 @@ parse_f64 :: proc(s: string) -> f64 { if r == '_' do continue; v := _digit_value(r); - if v >= 10 do break; + if v >= 10 { + reached_end = false; + break; + } value += f64(v)/pow10; pow10 *= 10; } @@ -169,7 +300,10 @@ parse_f64 :: proc(s: string) -> f64 { if r == '_' do continue; d := u32(_digit_value(r)); - if d >= 10 do break; + if d >= 10 { + reached_end = false; + break; + } exp = exp * 10 + d; } if exp > 308 { exp = 308; } @@ -180,8 +314,14 @@ parse_f64 :: proc(s: string) -> f64 { } } - if frac do return sign * (value/scale); - return sign * (value*scale); + if frac { + value = sign * (value/scale); + } else { + value = sign * (value*scale); + } + + rest = reached_end ? "" : s[i-1:]; + return; } @@ -203,10 +343,12 @@ itoa :: proc(buf: []byte, i: int) -> string { return append_int(buf, i64(i), 10); } atoi :: proc(s: string) -> int { - return parse_int(s); + v, _ := parse_int(s); + return v; } atof :: proc(s: string) -> f64 { - return parse_f64(s); + v, _ := parse_f64(s); + return v; } ftoa :: append_float; From 704ee9f8510cdaa75fe207316957efb70e8269de Mon Sep 17 00:00:00 2001 From: Tetralux Date: Wed, 29 Apr 2020 22:08:48 +0100 Subject: [PATCH 2/3] Return (value: T, ok: bool) instead --- core/strconv/strconv.odin | 50 ++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/core/strconv/strconv.odin b/core/strconv/strconv.odin index caadd2911..7e2728eb6 100644 --- a/core/strconv/strconv.odin +++ b/core/strconv/strconv.odin @@ -28,7 +28,7 @@ _digit_value :: proc(r: rune) -> int { // n, rest := strconv.parse_i64_of_base("-1234eeee", 10); // assert(n == -1234 && rest == "eeee"); // ``` -parse_i64_of_base :: proc(str: string, base: int) -> (value: i64, rest: string) { +parse_i64_of_base :: proc(str: string, base: int) -> (value: i64, ok: bool) { assert(base <= 16, "base must be 1-16"); s := str; neg := false; @@ -43,13 +43,11 @@ parse_i64_of_base :: proc(str: string, base: int) -> (value: i64, rest: string) } i := 0; - reached_end := true; for r in s { i += 1; if r == '_' do continue; v := i64(_digit_value(r)); if v >= i64(base) { - reached_end = false; break; } value *= i64(base); @@ -57,7 +55,7 @@ parse_i64_of_base :: proc(str: string, base: int) -> (value: i64, rest: string) } if neg do value = -value; - rest = reached_end ? "" : s[i-1:]; + ok = i > 1; return; } @@ -69,7 +67,7 @@ parse_i64_of_base :: proc(str: string, base: int) -> (value: i64, rest: string) // n, rest = strconv.parse_i64_maybe_prefixed("0xeeee"); // assert(n == 0xeeee && rest == ""); // ``` -parse_i64_maybe_prefixed :: proc(str: string) -> (value: i64, rest: string) { +parse_i64_maybe_prefixed :: proc(str: string) -> (value: i64, ok: bool) { s := str; neg := false; if len(s) > 1 { @@ -95,13 +93,11 @@ parse_i64_maybe_prefixed :: proc(str: string) -> (value: i64, rest: string) { } i := 0; - reached_end := true; for r in s { i += 1; if r == '_' do continue; v := i64(_digit_value(r)); if v >= base { - reached_end = false; break; } value *= base; @@ -109,7 +105,7 @@ parse_i64_maybe_prefixed :: proc(str: string) -> (value: i64, rest: string) { } if neg do value = -value; - rest = reached_end ? "" : s[i-1:]; + ok = i > 1; return; } @@ -124,7 +120,7 @@ parse_i64 :: proc{parse_i64_maybe_prefixed, parse_i64_of_base}; // n, rest = strconv.parse_u64_of_base("1234eeee", 16); // assert(n == 0x1234eeee && rest == ""); // ``` -parse_u64_of_base :: proc(str: string, base: int) -> (value: u64, rest: string) { +parse_u64_of_base :: proc(str: string, base: int) -> (value: u64, ok: bool) { assert(base <= 16, "base must be 1-16"); s := str; if len(s) > 1 && s[0] == '+' { @@ -132,20 +128,18 @@ parse_u64_of_base :: proc(str: string, base: int) -> (value: u64, rest: string) } i := 0; - reached_end := true; for r in s { i += 1; if r == '_' do continue; v := u64(_digit_value(r)); if v >= u64(base) { - reached_end = false; break; } value *= u64(base); value += v; } - rest = reached_end ? "" : s[i-1:]; + ok = i > 1; return; } @@ -157,7 +151,7 @@ parse_u64_of_base :: proc(str: string, base: int) -> (value: u64, rest: string) // n, rest = strconv.parse_u64_maybe_prefixed("0xeeee"); // assert(n == 0xeeee && rest == ""); // ``` -parse_u64_maybe_prefixed :: proc(str: string) -> (value: u64, rest: string) { +parse_u64_maybe_prefixed :: proc(str: string) -> (value: u64, ok: bool) { s := str; if len(s) > 1 && s[0] == '+' { s = s[1:]; @@ -176,20 +170,18 @@ parse_u64_maybe_prefixed :: proc(str: string) -> (value: u64, rest: string) { } i := 0; - reached_end := true; for r in s { i += 1; if r == '_' do continue; v := u64(_digit_value(r)); if v >= base { - reached_end = false; break; } value *= base; value += u64(v); } - rest = reached_end ? "" : s[i-1:]; + ok = i > 1; return; } @@ -208,29 +200,30 @@ parse_u64 :: proc{parse_u64_maybe_prefixed, parse_u64_of_base}; // n, rest = strconv.parse_int("0xffff"); // with prefix and inferred base // assert(n == 0xffff && rest == ""); // ``` -parse_int :: proc(s: string, base := 0) -> (value: int, rest: string) { +parse_int :: proc(s: string, base := 0) -> (value: int, ok: bool) { v: i64 = ---; switch base { - case 0: v, rest = parse_i64_maybe_prefixed(s); - case: v, rest = parse_i64_of_base(s, base); + case 0: v, ok = parse_i64_maybe_prefixed(s); + case: v, ok = parse_i64_of_base(s, base); } value = int(v); return; } -parse_uint :: proc(s: string, base := 0) -> (value: uint, rest: string) { +parse_uint :: proc(s: string, base := 0) -> (value: uint, ok: bool) { v: u64 = ---; switch base { - case 0: v, rest = parse_u64_maybe_prefixed(s); - case: v, rest = parse_u64_of_base(s, base); + case 0: v, ok = parse_u64_maybe_prefixed(s); + case: v, ok = parse_u64_of_base(s, base); } value = uint(v); return; } -parse_f32 :: proc(s: string) -> (value: f32, rest: string) { - v, r := parse_f64(s); - return f32(v), r; +parse_f32 :: proc(s: string) -> (value: f32, ok: bool) { + v: f64 = ---; + v, ok = parse_f64(s); + return f32(v), ok; } // Parses a floating point number from a string. @@ -241,7 +234,7 @@ parse_f32 :: proc(s: string) -> (value: f32, rest: string) { // n, rest = strconv.parse_f64("12.34"); // assert(n == 12.34 && rest == ""); // ``` -parse_f64 :: proc(s: string) -> (value: f64, rest: string) { +parse_f64 :: proc(s: string) -> (value: f64, ok: bool) { if s == "" { return; } @@ -263,7 +256,6 @@ parse_f64 :: proc(s: string) -> (value: f64, rest: string) { value += f64(v); } - reached_end := true; if i < len(s) && s[i] == '.' { pow10: f64 = 10; i += 1; @@ -274,7 +266,6 @@ parse_f64 :: proc(s: string) -> (value: f64, rest: string) { v := _digit_value(r); if v >= 10 { - reached_end = false; break; } value += f64(v)/pow10; @@ -301,7 +292,6 @@ parse_f64 :: proc(s: string) -> (value: f64, rest: string) { d := u32(_digit_value(r)); if d >= 10 { - reached_end = false; break; } exp = exp * 10 + d; @@ -320,7 +310,7 @@ parse_f64 :: proc(s: string) -> (value: f64, rest: string) { value = sign * (value*scale); } - rest = reached_end ? "" : s[i-1:]; + ok = i > 0; return; } From 25f77e32ee475bdb5e509fc5ccf6130590e72fad Mon Sep 17 00:00:00 2001 From: Tetralux Date: Wed, 29 Apr 2020 22:19:48 +0100 Subject: [PATCH 3/3] Fix doc comments --- core/strconv/strconv.odin | 124 ++++++++++++++++++++++++++------------ 1 file changed, 84 insertions(+), 40 deletions(-) diff --git a/core/strconv/strconv.odin b/core/strconv/strconv.odin index 7e2728eb6..a3c4f7302 100644 --- a/core/strconv/strconv.odin +++ b/core/strconv/strconv.odin @@ -24,9 +24,12 @@ _digit_value :: proc(r: rune) -> int { } // Parses an integer value from a string, in the given base, without a prefix. +// +// Returns ok=false if no numeric value of the appropriate base could be found. +// // ``` -// n, rest := strconv.parse_i64_of_base("-1234eeee", 10); -// assert(n == -1234 && rest == "eeee"); +// n, ok := strconv.parse_i64_of_base("-1234eeee", 10); +// assert(n == -1234 && ok); // ``` parse_i64_of_base :: proc(str: string, base: int) -> (value: i64, ok: bool) { assert(base <= 16, "base must be 1-16"); @@ -60,12 +63,15 @@ parse_i64_of_base :: proc(str: string, base: int) -> (value: i64, ok: bool) { } // Parses a integer value from a string, in base 10, unless there's a prefix. -// ``` -// n, rest := strconv.parse_i64_maybe_prefixed("1234"); -// assert(n == 1234 && rest == ""); // -// n, rest = strconv.parse_i64_maybe_prefixed("0xeeee"); -// assert(n == 0xeeee && rest == ""); +// Returns ok=false if a base 10 integer could not be found. +// +// ``` +// n, ok := strconv.parse_i64_maybe_prefixed("1234"); +// assert(n == 1234 && ok); +// +// n, ok = strconv.parse_i64_maybe_prefixed("0xeeee"); +// assert(n == 0xeeee && ok); // ``` parse_i64_maybe_prefixed :: proc(str: string) -> (value: i64, ok: bool) { s := str; @@ -113,12 +119,15 @@ parse_i64 :: proc{parse_i64_maybe_prefixed, parse_i64_of_base}; // Parses an unsigned integer value from a string, in the given base, and // without a prefix. -// ``` -// n, rest := strconv.parse_u64_of_base("1234eeee", 10); -// assert(n == 1234 && rest == "eeee"); // -// n, rest = strconv.parse_u64_of_base("1234eeee", 16); -// assert(n == 0x1234eeee && rest == ""); +// Returns ok=false if no numeric value of the appropriate base could be found. +// +// ``` +// n, ok := strconv.parse_u64_of_base("1234eeee", 10); +// assert(n == 1234 && ok); +// +// n, ok = strconv.parse_u64_of_base("5678eeee", 16); +// assert(n == 0x5678eeee && ok); // ``` parse_u64_of_base :: proc(str: string, base: int) -> (value: u64, ok: bool) { assert(base <= 16, "base must be 1-16"); @@ -144,12 +153,16 @@ parse_u64_of_base :: proc(str: string, base: int) -> (value: u64, ok: bool) { } // Parses an unsigned integer value from a string in base 10, unless there's a prefix. -// ``` -// n, rest := strconv.parse_u64_maybe_prefixed("1234"); -// assert(n == 1234 && rest == ""); // -// n, rest = strconv.parse_u64_maybe_prefixed("0xeeee"); -// assert(n == 0xeeee && rest == ""); +// Returns ok=false if a base 10 integer could not be found, or +// if the value was negative. +// +// ``` +// n, ok := strconv.parse_u64_maybe_prefixed("1234"); +// assert(n == 1234 && ok); +// +// n, ok = strconv.parse_u64_maybe_prefixed("0xeeee"); +// assert(n == 0xeeee && ok); // ``` parse_u64_maybe_prefixed :: proc(str: string) -> (value: u64, ok: bool) { s := str; @@ -174,9 +187,7 @@ parse_u64_maybe_prefixed :: proc(str: string) -> (value: u64, ok: bool) { i += 1; if r == '_' do continue; v := u64(_digit_value(r)); - if v >= base { - break; - } + if v >= base do break; value *= base; value += u64(v); } @@ -187,18 +198,21 @@ parse_u64_maybe_prefixed :: proc(str: string) -> (value: u64, ok: bool) { parse_u64 :: proc{parse_u64_maybe_prefixed, parse_u64_of_base}; -// Parses an integer value of the given base, or the base implied by a prefix. -// If base is zero, assumes base 10, unless there's a prefix. e.g. '0x' for hexadecimal. +// Parses an integer value from a string in the given base, or +// - if the string has a prefix (e.g: '0x') then that will determine the base; +// - otherwise, assumes base 10. +// +// Returns ok=false if no appropriate value could be found. // // ``` -// n, rest := strconv.parse_int("1234"); // without prefix, inferred base 10 -// assert(n == 1234 && rest == ""); +// n, ok := strconv.parse_int("1234"); // without prefix, inferred base 10 +// assert(n == 1234 && ok); // -// n, rest = strconv.parse_int("ffff", 16); // without prefix, explicit base -// assert(n == 0xffff && rest == ""); +// n, ok = strconv.parse_int("ffff", 16); // without prefix, explicit base +// assert(n == 0xffff && ok); // -// n, rest = strconv.parse_int("0xffff"); // with prefix and inferred base -// assert(n == 0xffff && rest == ""); +// n, ok = strconv.parse_int("0xffff"); // with prefix and inferred base +// assert(n == 0xffff && ok); // ``` parse_int :: proc(s: string, base := 0) -> (value: int, ok: bool) { v: i64 = ---; @@ -209,6 +223,26 @@ parse_int :: proc(s: string, base := 0) -> (value: int, ok: bool) { value = int(v); return; } + + +// Parses an unsigned integer value from a string in the given base, or +// - if the string has a prefix (e.g: '0x') then that will determine the base; +// - otherwise, assumes base 10. +// +// Returns ok=false if: +// - no appropriate value could be found; or +// - the value was negative. +// +// ``` +// n, ok := strconv.parse_uint("1234"); // without prefix, inferred base 10 +// assert(n == 1234 && ok); +// +// n, ok = strconv.parse_uint("ffff", 16); // without prefix, explicit base +// assert(n == 0xffff && ok); +// +// n, ok = strconv.parse_uint("0xffff"); // with prefix and inferred base +// assert(n == 0xffff && ok); +// ``` parse_uint :: proc(s: string, base := 0) -> (value: uint, ok: bool) { v: u64 = ---; switch base { @@ -220,19 +254,33 @@ parse_uint :: proc(s: string, base := 0) -> (value: uint, ok: bool) { } +// Parses a 32-bit floating point number from a string. +// +// Returns ok=false if a base 10 float could not be found. +// +// ``` +// n, ok := strconv.parse_f32("12.34eee"); +// assert(n == 12.34 && ok); +// +// n, ok = strconv.parse_f32("12.34"); +// assert(n == 12.34 && ok); +// ``` parse_f32 :: proc(s: string) -> (value: f32, ok: bool) { v: f64 = ---; v, ok = parse_f64(s); return f32(v), ok; } -// Parses a floating point number from a string. -// ``` -// n, rest := strconv.parse_f64("12.34eee"); -// assert(n == 12.34 && rest == "eee"); +// Parses a 64-bit floating point number from a string. // -// n, rest = strconv.parse_f64("12.34"); -// assert(n == 12.34 && rest == ""); +// Returns ok=false if a base 10 float could not be found. +// +// ``` +// n, ok := strconv.parse_f32("12.34eee"); +// assert(n == 12.34 && ok); +// +// n, ok = strconv.parse_f32("12.34"); +// assert(n == 12.34 && ok); // ``` parse_f64 :: proc(s: string) -> (value: f64, ok: bool) { if s == "" { @@ -265,9 +313,7 @@ parse_f64 :: proc(s: string) -> (value: f64, ok: bool) { if r == '_' do continue; v := _digit_value(r); - if v >= 10 { - break; - } + if v >= 10 do break; value += f64(v)/pow10; pow10 *= 10; } @@ -291,9 +337,7 @@ parse_f64 :: proc(s: string) -> (value: f64, ok: bool) { if r == '_' do continue; d := u32(_digit_value(r)); - if d >= 10 { - break; - } + if d >= 10 do break; exp = exp * 10 + d; } if exp > 308 { exp = 308; }