| ... | @@ -996,49 +996,77 @@ pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, option | ... | @@ -996,49 +996,77 @@ pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, option |
| 996 | return fbs.pos; | 996 | return fbs.pos; |
| 997 | } | 997 | } |
| 998 | | 998 | |
| 999 | pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) !T { | 999 | pub const ParseIntError = error{ |
| 1000 | if (!T.is_signed) return parseUnsigned(T, buf, radix); | | |
| 1001 | if (buf.len == 0) return @as(T, 0); | | |
| 1002 | if (buf[0] == '-') { | | |
| 1003 | return math.negate(try parseUnsigned(T, buf[1..], radix)); | | |
| 1004 | } else if (buf[0] == '+') { | | |
| 1005 | return parseUnsigned(T, buf[1..], radix); | | |
| 1006 | } else { | | |
| 1007 | return parseUnsigned(T, buf, radix); | | |
| 1008 | } | | |
| 1009 | } | | |
| 1010 | | | |
| 1011 | test "parseInt" { | | |
| 1012 | std.testing.expect((parseInt(i32, "-10", 10) catch unreachable) == -10); | | |
| 1013 | std.testing.expect((parseInt(i32, "+10", 10) catch unreachable) == 10); | | |
| 1014 | std.testing.expect(if (parseInt(i32, " 10", 10)) |_| false else |err| err == error.InvalidCharacter); | | |
| 1015 | std.testing.expect(if (parseInt(i32, "10 ", 10)) |_| false else |err| err == error.InvalidCharacter); | | |
| 1016 | std.testing.expect(if (parseInt(u32, "-10", 10)) |_| false else |err| err == error.InvalidCharacter); | | |
| 1017 | std.testing.expect((parseInt(u8, "255", 10) catch unreachable) == 255); | | |
| 1018 | std.testing.expect(if (parseInt(u8, "256", 10)) |_| false else |err| err == error.Overflow); | | |
| 1019 | } | | |
| 1020 | | | |
| 1021 | pub const ParseUnsignedError = error{ | | |
| 1022 | /// The result cannot fit in the type specified | 1000 | /// The result cannot fit in the type specified |
| 1023 | Overflow, | 1001 | Overflow, |
| 1024 | | 1002 | |
| 1025 | /// The input had a byte that was not a digit | 1003 | /// The input was empty or had a byte that was not a digit |
| 1026 | InvalidCharacter, | 1004 | InvalidCharacter, |
| 1027 | }; | 1005 | }; |
| 1028 | | 1006 | |
| 1029 | pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsignedError!T { | 1007 | pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) ParseIntError!T { |
| | 1008 | if (buf.len == 0) return error.InvalidCharacter; |
| | 1009 | if (buf[0] == '+') return parseWithSign(T, buf[1..], radix, .Pos); |
| | 1010 | if (buf[0] == '-') return parseWithSign(T, buf[1..], radix, .Neg); |
| | 1011 | return parseWithSign(T, buf, radix, .Pos); |
| | 1012 | } |
| | 1013 | |
| | 1014 | test "parseInt" { |
| | 1015 | std.testing.expect((try parseInt(i32, "-10", 10)) == -10); |
| | 1016 | std.testing.expect((try parseInt(i32, "+10", 10)) == 10); |
| | 1017 | std.testing.expect((try parseInt(u32, "+10", 10)) == 10); |
| | 1018 | std.testing.expectError(error.Overflow, parseInt(u32, "-10", 10)); |
| | 1019 | std.testing.expectError(error.InvalidCharacter, parseInt(u32, " 10", 10)); |
| | 1020 | std.testing.expectError(error.InvalidCharacter, parseInt(u32, "10 ", 10)); |
| | 1021 | std.testing.expect((try parseInt(u8, "255", 10)) == 255); |
| | 1022 | std.testing.expectError(error.Overflow, parseInt(u8, "256", 10)); |
| | 1023 | |
| | 1024 | // +0 and -0 should work for unsigned |
| | 1025 | std.testing.expect((try parseInt(u8, "-0", 10)) == 0); |
| | 1026 | std.testing.expect((try parseInt(u8, "+0", 10)) == 0); |
| | 1027 | |
| | 1028 | // ensure minInt is parsed correctly |
| | 1029 | std.testing.expect((try parseInt(i8, "-128", 10)) == math.minInt(i8)); |
| | 1030 | std.testing.expect((try parseInt(i43, "-4398046511104", 10)) == math.minInt(i43)); |
| | 1031 | |
| | 1032 | // empty string or bare +- is invalid |
| | 1033 | std.testing.expectError(error.InvalidCharacter, parseInt(u32, "", 10)); |
| | 1034 | std.testing.expectError(error.InvalidCharacter, parseInt(i32, "", 10)); |
| | 1035 | std.testing.expectError(error.InvalidCharacter, parseInt(u32, "+", 10)); |
| | 1036 | std.testing.expectError(error.InvalidCharacter, parseInt(i32, "+", 10)); |
| | 1037 | std.testing.expectError(error.InvalidCharacter, parseInt(u32, "-", 10)); |
| | 1038 | std.testing.expectError(error.InvalidCharacter, parseInt(i32, "-", 10)); |
| | 1039 | } |
| | 1040 | |
| | 1041 | fn parseWithSign( |
| | 1042 | comptime T: type, |
| | 1043 | buf: []const u8, |
| | 1044 | radix: u8, |
| | 1045 | comptime sign: enum { Pos, Neg }, |
| | 1046 | ) ParseIntError!T { |
| | 1047 | if (buf.len == 0) return error.InvalidCharacter; |
| | 1048 | |
| | 1049 | const add = switch (sign) { |
| | 1050 | .Pos => math.add, |
| | 1051 | .Neg => math.sub, |
| | 1052 | }; |
| | 1053 | |
| 1030 | var x: T = 0; | 1054 | var x: T = 0; |
| 1031 | | 1055 | |
| 1032 | for (buf) |c| { | 1056 | for (buf) |c| { |
| 1033 | const digit = try charToDigit(c, radix); | 1057 | const digit = try charToDigit(c, radix); |
| 1034 | | 1058 | |
| 1035 | if (x != 0) x = try math.mul(T, x, try math.cast(T, radix)); | 1059 | if (x != 0) x = try math.mul(T, x, try math.cast(T, radix)); |
| 1036 | x = try math.add(T, x, try math.cast(T, digit)); | 1060 | x = try add(T, x, try math.cast(T, digit)); |
| 1037 | } | 1061 | } |
| 1038 | | 1062 | |
| 1039 | return x; | 1063 | return x; |
| 1040 | } | 1064 | } |
| 1041 | | 1065 | |
| | 1066 | pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseIntError!T { |
| | 1067 | return parseWithSign(T, buf, radix, .Pos); |
| | 1068 | } |
| | 1069 | |
| 1042 | test "parseUnsigned" { | 1070 | test "parseUnsigned" { |
| 1043 | std.testing.expect((try parseUnsigned(u16, "050124", 10)) == 50124); | 1071 | std.testing.expect((try parseUnsigned(u16, "050124", 10)) == 50124); |
| 1044 | std.testing.expect((try parseUnsigned(u16, "65535", 10)) == 65535); | 1072 | std.testing.expect((try parseUnsigned(u16, "65535", 10)) == 65535); |
| ... | @@ -1064,6 +1092,13 @@ test "parseUnsigned" { | ... | @@ -1064,6 +1092,13 @@ test "parseUnsigned" { |
| 1064 | std.testing.expect((try parseUnsigned(u1, "001", 16)) == 1); | 1092 | std.testing.expect((try parseUnsigned(u1, "001", 16)) == 1); |
| 1065 | std.testing.expect((try parseUnsigned(u2, "3", 16)) == 3); | 1093 | std.testing.expect((try parseUnsigned(u2, "3", 16)) == 3); |
| 1066 | std.testing.expectError(error.Overflow, parseUnsigned(u2, "4", 16)); | 1094 | std.testing.expectError(error.Overflow, parseUnsigned(u2, "4", 16)); |
| | 1095 | |
| | 1096 | // parseUnsigned does not expect a sign |
| | 1097 | std.testing.expectError(error.InvalidCharacter, parseUnsigned(u8, "+0", 10)); |
| | 1098 | std.testing.expectError(error.InvalidCharacter, parseUnsigned(u8, "-0", 10)); |
| | 1099 | |
| | 1100 | // test empty string error |
| | 1101 | std.testing.expectError(error.InvalidCharacter, parseUnsigned(u8, "", 10)); |
| 1067 | } | 1102 | } |
| 1068 | | 1103 | |
| 1069 | pub const parseFloat = @import("fmt/parse_float.zig").parseFloat; | 1104 | pub const parseFloat = @import("fmt/parse_float.zig").parseFloat; |