From 265e6aa781e487a3880f97bb6e8c12d2abcb8b57 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Tue, 4 Jun 2024 16:59:07 -0400 Subject: [PATCH] Add `parse_complex/quaternion*` to `core:strconv` --- core/strconv/strconv.odin | 269 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 269 insertions(+) diff --git a/core/strconv/strconv.odin b/core/strconv/strconv.odin index 94842617e..2c8365922 100644 --- a/core/strconv/strconv.odin +++ b/core/strconv/strconv.odin @@ -1124,6 +1124,275 @@ parse_f64_prefix :: proc(str: string) -> (value: f64, nr: int, ok: bool) { ok = !overflow return } +/* +Parses a 128-bit complex number from a string + +**Inputs** +- str: The input string containing a 128-bit complex number. +- n: An optional pointer to an int to store the length of the parsed substring (default: nil). + +Example: + + import "core:fmt" + import "core:strconv" + parse_complex128_example :: proc() { + n: int + c, ok := strconv.parse_complex128("3+1i", &n) + fmt.printfln("%v %i %t", c, n, ok) + + c, ok = strconv.parse_complex128("5+7i hellope", &n) + fmt.printfln("%v %i %t", c, n, ok) + } + +Output: + + 3+1i 4 true + 5+7i 4 false + +**Returns** +- value: The parsed 128-bit complex number. +- ok: `false` if a complex number could not be found, or if the input string contained more than just the number. +*/ +parse_complex128 :: proc(str: string, n: ^int = nil) -> (value: complex128, ok: bool) { + real_value, imag_value: f64 + nr_r, nr_i: int + + real_value, nr_r, _ = parse_f64_prefix(str) + imag_value, nr_i, _ = parse_f64_prefix(str[nr_r:]) + + i_parsed := len(str) >= nr_r + nr_i + 1 && str[nr_r + nr_i] == 'i' + if !i_parsed { + // No `i` means we refuse to treat the second float we parsed as an + // imaginary value. + imag_value = 0 + nr_i = 0 + } + + ok = i_parsed && len(str) == nr_r + nr_i + 1 + + if n != nil { + n^ = nr_r + nr_i + (1 if i_parsed else 0) + } + + value = complex(real_value, imag_value) + return +} +/* +Parses a 64-bit complex number from a string + +**Inputs** +- str: The input string containing a 64-bit complex number. +- n: An optional pointer to an int to store the length of the parsed substring (default: nil). + +Example: + + import "core:fmt" + import "core:strconv" + parse_complex64_example :: proc() { + n: int + c, ok := strconv.parse_complex64("3+1i", &n) + fmt.printfln("%v %i %t", c, n, ok) + + c, ok = strconv.parse_complex64("5+7i hellope", &n) + fmt.printfln("%v %i %t", c, n, ok) + } + +Output: + + 3+1i 4 true + 5+7i 4 false + +**Returns** +- value: The parsed 64-bit complex number. +- ok: `false` if a complex number could not be found, or if the input string contained more than just the number. +*/ +parse_complex64 :: proc(str: string, n: ^int = nil) -> (value: complex64, ok: bool) { + v: complex128 = --- + v, ok = parse_complex128(str, n) + return cast(complex64)v, ok +} +/* +Parses a 32-bit complex number from a string + +**Inputs** +- str: The input string containing a 32-bit complex number. +- n: An optional pointer to an int to store the length of the parsed substring (default: nil). + +Example: + + import "core:fmt" + import "core:strconv" + parse_complex32_example :: proc() { + n: int + c, ok := strconv.parse_complex32("3+1i", &n) + fmt.printfln("%v %i %t", c, n, ok) + + c, ok = strconv.parse_complex32("5+7i hellope", &n) + fmt.printfln("%v %i %t", c, n, ok) + } + +Output: + + 3+1i 4 true + 5+7i 4 false + +**Returns** +- value: The parsed 32-bit complex number. +- ok: `false` if a complex number could not be found, or if the input string contained more than just the number. +*/ +parse_complex32 :: proc(str: string, n: ^int = nil) -> (value: complex32, ok: bool) { + v: complex128 = --- + v, ok = parse_complex128(str, n) + return cast(complex32)v, ok +} +/* +Parses a 256-bit quaternion from a string + +**Inputs** +- str: The input string containing a 256-bit quaternion. +- n: An optional pointer to an int to store the length of the parsed substring (default: nil). + +Example: + + import "core:fmt" + import "core:strconv" + parse_quaternion256_example :: proc() { + n: int + q, ok := strconv.parse_quaternion256("1+2i+3j+4k", &n) + fmt.printfln("%v %i %t", q, n, ok) + + q, ok = strconv.parse_quaternion256("1+2i+3j+4k hellope", &n) + fmt.printfln("%v %i %t", q, n, ok) + } + +Output: + + 1+2i+3j+4k 10 true + 1+2i+3j+4k 10 false + +**Returns** +- value: The parsed 256-bit quaternion. +- ok: `false` if a quaternion could not be found, or if the input string contained more than just the quaternion. +*/ +parse_quaternion256 :: proc(str: string, n: ^int = nil) -> (value: quaternion256, ok: bool) { + iterate_and_assign :: proc (iter: ^string, terminator: byte, nr_total: ^int, state: bool) -> (value: f64, ok: bool) { + if !state { + return + } + + nr: int + value, nr, _ = parse_f64_prefix(iter^) + iter^ = iter[nr:] + + if len(iter) > 0 && iter[0] == terminator { + iter^ = iter[1:] + nr_total^ += nr + 1 + ok = true + } else { + value = 0 + } + + return + } + + real_value, imag_value, jmag_value, kmag_value: f64 + nr: int + + real_value, nr, _ = parse_f64_prefix(str) + iter := str[nr:] + + // Need to have parsed at least something in order to get started. + ok = nr > 0 + + // Quaternion parsing is done this way to honour the rest of the API with + // regards to partial parsing. Otherwise, we could error out early. + imag_value, ok = iterate_and_assign(&iter, 'i', &nr, ok) + jmag_value, ok = iterate_and_assign(&iter, 'j', &nr, ok) + kmag_value, ok = iterate_and_assign(&iter, 'k', &nr, ok) + + if len(iter) != 0 { + ok = false + } + + if n != nil { + n^ = nr + } + + value = quaternion( + real = real_value, + imag = imag_value, + jmag = jmag_value, + kmag = kmag_value) + return +} +/* +Parses a 128-bit quaternion from a string + +**Inputs** +- str: The input string containing a 128-bit quaternion. +- n: An optional pointer to an int to store the length of the parsed substring (default: nil). + +Example: + + import "core:fmt" + import "core:strconv" + parse_quaternion128_example :: proc() { + n: int + q, ok := strconv.parse_quaternion128("1+2i+3j+4k", &n) + fmt.printfln("%v %i %t", q, n, ok) + + q, ok = strconv.parse_quaternion128("1+2i+3j+4k hellope", &n) + fmt.printfln("%v %i %t", q, n, ok) + } + +Output: + + 1+2i+3j+4k 10 true + 1+2i+3j+4k 10 false + +**Returns** +- value: The parsed 128-bit quaternion. +- ok: `false` if a quaternion could not be found, or if the input string contained more than just the quaternion. +*/ +parse_quaternion128 :: proc(str: string, n: ^int = nil) -> (value: quaternion128, ok: bool) { + v: quaternion256 = --- + v, ok = parse_quaternion256(str, n) + return cast(quaternion128)v, ok +} +/* +Parses a 64-bit quaternion from a string + +**Inputs** +- str: The input string containing a 64-bit quaternion. +- n: An optional pointer to an int to store the length of the parsed substring (default: nil). + +Example: + + import "core:fmt" + import "core:strconv" + parse_quaternion64_example :: proc() { + n: int + q, ok := strconv.parse_quaternion64("1+2i+3j+4k", &n) + fmt.printfln("%v %i %t", q, n, ok) + + q, ok = strconv.parse_quaternion64("1+2i+3j+4k hellope", &n) + fmt.printfln("%v %i %t", q, n, ok) + } + +Output: + + 1+2i+3j+4k 10 true + 1+2i+3j+4k 10 false + +**Returns** +- value: The parsed 64-bit quaternion. +- ok: `false` if a quaternion could not be found, or if the input string contained more than just the quaternion. +*/ +parse_quaternion64 :: proc(str: string, n: ^int = nil) -> (value: quaternion64, ok: bool) { + v: quaternion256 = --- + v, ok = parse_quaternion256(str, n) + return cast(quaternion64)v, ok +} /* Appends a boolean value as a string to the given buffer