Browse Source

Add `i128` and `u128` parsers to `strconv`

gingerBill 3 years ago
parent
commit
ebc09d5e4e
1 changed files with 223 additions and 0 deletions
  1. 223 0
      core/strconv/strconv.odin

+ 223 - 0
core/strconv/strconv.odin

@@ -300,6 +300,229 @@ parse_uint :: proc(s: string, base := 0) -> (value: uint, ok: bool) {
 }
 
 
+// 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,
+// or if the input string contained more than just the number.
+//
+// ```
+// n, ok := strconv.parse_i128_of_base("-1234eeee", 10);
+// assert(n == -1234 && ok);
+// ```
+parse_i128_of_base :: proc(str: string, base: int) -> (value: i128, ok: bool) {
+	assert(base <= 16, "base must be 1-16")
+
+	s := str
+	if s == "" {
+		return
+	}
+
+	neg := false
+	if len(s) > 1 {
+		switch s[0] {
+		case '-':
+			neg = true
+			s = s[1:]
+		case '+':
+			s = s[1:]
+		}
+	}
+
+
+	i := 0
+	for r in s {
+		if r == '_' {
+			i += 1
+			continue
+		}
+		v := i128(_digit_value(r))
+		if v >= i128(base) {
+			break
+		}
+		value *= i128(base)
+		value += v
+		i += 1
+	}
+	s = s[i:]
+
+	if neg {
+		value = -value
+	}
+	ok = len(s) == 0
+	return
+}
+
+// Parses a integer value from a string, in base 10, unless there's a prefix.
+//
+// Returns ok=false if a valid integer could not be found,
+// or if the input string contained more than just the number.
+//
+// ```
+// n, ok := strconv.parse_i128_maybe_prefixed("1234");
+// assert(n == 1234 && ok);
+//
+// n, ok = strconv.parse_i128_maybe_prefixed("0xeeee");
+// assert(n == 0xeeee && ok);
+// ```
+parse_i128_maybe_prefixed :: proc(str: string) -> (value: i128, ok: bool) {
+	s := str
+	if s == "" {
+		return
+	}
+
+	neg := false
+	if len(s) > 1 {
+		switch s[0] {
+		case '-':
+			neg = true
+			s = s[1:]
+		case '+':
+			s = s[1:]
+		}
+	}
+
+
+	base: i128 = 10
+	if len(s) > 2 && s[0] == '0' {
+		switch s[1] {
+		case 'b': base =  2;  s = s[2:]
+		case 'o': base =  8;  s = s[2:]
+		case 'd': base = 10;  s = s[2:]
+		case 'z': base = 12;  s = s[2:]
+		case 'x': base = 16;  s = s[2:]
+		}
+	}
+
+
+	i := 0
+	for r in s {
+		if r == '_' {
+			i += 1
+			continue
+		}
+		v := i128(_digit_value(r))
+		if v >= base {
+			break
+		}
+		value *= base
+		value += v
+		i += 1
+	}
+	s = s[i:]
+
+	if neg {
+		value = -value
+	}
+	ok = len(s) == 0
+	return
+}
+
+parse_i128 :: proc{parse_i128_maybe_prefixed, parse_i128_of_base}
+
+// Parses an unsigned integer value from a string, in the given base, and
+// without a prefix.
+//
+// Returns ok=false if no numeric value of the appropriate base could be found,
+// or if the input string contained more than just the number.
+//
+// ```
+// n, ok := strconv.parse_u128_of_base("1234eeee", 10);
+// assert(n == 1234 && ok);
+//
+// n, ok = strconv.parse_u128_of_base("5678eeee", 16);
+// assert(n == 0x5678eeee && ok);
+// ```
+parse_u128_of_base :: proc(str: string, base: int) -> (value: u128, ok: bool) {
+	assert(base <= 16, "base must be 1-16")
+	s := str
+	if s == "" {
+		return
+	}
+
+	if len(s) > 1 && s[0] == '+' {
+		s = s[1:]
+	}
+
+	i := 0
+	for r in s {
+		if r == '_' {
+			i += 1
+			continue
+		}
+		v := u128(_digit_value(r))
+		if v >= u128(base) {
+			break
+		}
+		value *= u128(base)
+		value += v
+		i += 1
+	}
+	s = s[i:]
+
+	ok = len(s) == 0
+	return
+}
+
+// Parses an unsigned integer value from a string in base 10, unless there's a prefix.
+//
+// Returns ok=false if a valid integer could not be found, if the value was negative,
+// or if the input string contained more than just the number.
+//
+// ```
+// n, ok := strconv.parse_u128_maybe_prefixed("1234");
+// assert(n == 1234 && ok);
+//
+// n, ok = strconv.parse_u128_maybe_prefixed("0xeeee");
+// assert(n == 0xeeee && ok);
+// ```
+parse_u128_maybe_prefixed :: proc(str: string) -> (value: u128, ok: bool) {
+	s := str
+	if s == "" {
+		return
+	}
+
+	if len(s) > 1 && s[0] == '+' {
+		s = s[1:]
+	}
+
+
+	base := u128(10)
+	if len(s) > 2 && s[0] == '0' {
+		switch s[1] {
+		case 'b': base =  2;  s = s[2:]
+		case 'o': base =  8;  s = s[2:]
+		case 'd': base = 10;  s = s[2:]
+		case 'z': base = 12;  s = s[2:]
+		case 'x': base = 16;  s = s[2:]
+		}
+	}
+
+	i := 0
+	for r in s {
+		if r == '_' {
+			i += 1
+			continue
+		}
+		v := u128(_digit_value(r))
+		if v >= base {
+			break
+		}
+		value *= base
+		value += v
+		i += 1
+	}
+	s = s[i:]
+
+	ok = len(s) == 0
+	return
+}
+
+parse_u128 :: proc{parse_u128_maybe_prefixed, parse_u128_of_base}
+
+
+
+
+
 // Parses a 32-bit floating point number from a string.
 //
 // Returns ok=false if a base 10 float could not be found,