| ... | ... | @@ -996,49 +996,77 @@ pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, option |
| 996 | 996 | return fbs.pos; |
| 997 | 997 | } |
| 998 | 998 | |
| 999 | | pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) !T { |
| 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{ |
| 999 | pub const ParseIntError = error{ |
| 1022 | 1000 | /// The result cannot fit in the type specified |
| 1023 | 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 | 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 | 1054 | var x: T = 0; |
| 1031 | 1055 | |
| 1032 | 1056 | for (buf) |c| { |
| 1033 | 1057 | const digit = try charToDigit(c, radix); |
| 1034 | 1058 | |
| 1035 | 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 | 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 | 1070 | test "parseUnsigned" { |
| 1043 | 1071 | std.testing.expect((try parseUnsigned(u16, "050124", 10)) == 50124); |
| 1044 | 1072 | std.testing.expect((try parseUnsigned(u16, "65535", 10)) == 65535); |
| ... | ... | @@ -1064,6 +1092,13 @@ test "parseUnsigned" { |
| 1064 | 1092 | std.testing.expect((try parseUnsigned(u1, "001", 16)) == 1); |
| 1065 | 1093 | std.testing.expect((try parseUnsigned(u2, "3", 16)) == 3); |
| 1066 | 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 | 1104 | pub const parseFloat = @import("fmt/parse_float.zig").parseFloat; |