const io = @import("./io.zig"); const math = @import("./math.zig"); pub const DecimalFormat = struct { delimiter: []const io.Byte, positive_prefix: enum {none, plus, space}, pub fn parse(self: DecimalFormat, utf8: []const io.Byte, comptime Decimal: type) ?Decimal { if (utf8.len == 0) { return null; } switch (@typeInfo(Decimal)) { .Int => |int| { var has_sign = switch (utf8[0]) { '-', '+', ' ' => true, else => false, }; var result = @as(Decimal, 0); for (@intFromBool(has_sign) .. utf8.len) |index| { const radix = 10; const code = utf8[index]; switch (code) { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' => { result = try math.checked_add( try math.checked_mul(result, radix), try math.checked_sub(code, '0')); }, else => { if (self.delimiter.len == 0 or !io.equals(self.delimiter, utf8[index ..])) { return null; } }, } } switch (int.signedness) { .signed => { return result * @as(Decimal, if (has_sign and utf8[0] == '-') -1 else 1); }, .unsigned => { if (has_sign and utf8[0] == '-') { return null; } return result; }, } }, .Float => { var has_sign = switch (utf8[0]) { '-', '+', ' ' => true, else => false, }; // "-" if (has_sign and utf8.len == 1) { return null; } const sign_offset = @intFromBool(has_sign); var has_decimal = utf8[sign_offset] == '.'; // "-." if (has_decimal and (utf8.len == 2)) { return null; } var result = @as(Decimal, 0); var factor = @as(Decimal, if (has_sign and utf8[0] == '-') -1 else 1); for (utf8[sign_offset + @intFromBool(has_decimal) .. utf8.len]) |code| { switch (code) { '.' => { if (has_decimal) { return null; } has_decimal = true; }, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' => { if (has_decimal) { factor /= 10.0; } result = ((result * 10.0) + @as(Decimal, @floatFromInt(code - '0'))); }, else => return null, } } return result * factor; }, else => @compileError("`" ++ @typeName(Decimal) ++ "` cannot be formatted as a decimal string"), } } };