diff --git a/std/fmt.zig b/std/fmt.zig index 8c4995cb9..984d46272 100644 --- a/std/fmt.zig +++ b/std/fmt.zig @@ -69,7 +69,6 @@ pub fn format( FormatFillAndAlign, FormatWidth, FormatPrecision, - Pointer, }; comptime var start_index = 0; @@ -109,9 +108,6 @@ pub fn format( state = .Start; start_index = i; }, - '*' => { - state = .Pointer; - }, ':' => { state = if (comptime peekIsAlign(fmt[i..])) State.FormatFillAndAlign else State.FormatWidth; specifier_end = i; @@ -256,19 +252,6 @@ pub fn format( @compileError("Unexpected character in precision value: " ++ [_]u8{c}); }, }, - .Pointer => switch (c) { - '}' => { - const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg); - - try output(context, @typeName(@typeOf(args[arg_to_print]).Child)); - try output(context, "@"); - try formatInt(@ptrToInt(args[arg_to_print]), 16, false, 0, context, Errors, output); - - state = .Start; - start_index = i + 1; - }, - else => @compileError("Unexpected format character after '*'"), - }, } } comptime { @@ -293,12 +276,19 @@ pub fn format( pub fn formatType( value: var, comptime fmt: []const u8, - comptime options: FormatOptions, + options: FormatOptions, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void, max_depth: usize, ) Errors!void { + if (comptime std.mem.eql(u8, fmt, "*")) { + try output(context, @typeName(@typeOf(value).Child)); + try output(context, "@"); + try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, context, Errors, output); + return; + } + const T = @typeOf(value); switch (@typeInfo(T)) { .ComptimeInt, .Int, .Float => { @@ -438,15 +428,15 @@ pub fn formatType( fn formatValue( value: var, comptime fmt: []const u8, - comptime options: FormatOptions, + options: FormatOptions, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void, ) Errors!void { if (comptime std.mem.eql(u8, fmt, "B")) { - return formatBytes(value, options.width, 1000, context, Errors, output); + return formatBytes(value, options, 1000, context, Errors, output); } else if (comptime std.mem.eql(u8, fmt, "Bi")) { - return formatBytes(value, options.width, 1024, context, Errors, output); + return formatBytes(value, options, 1024, context, Errors, output); } const T = @typeOf(value); @@ -460,7 +450,7 @@ fn formatValue( pub fn formatIntValue( value: var, comptime fmt: []const u8, - comptime options: FormatOptions, + options: FormatOptions, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void, @@ -479,7 +469,7 @@ pub fn formatIntValue( uppercase = false; } else if (comptime std.mem.eql(u8, fmt, "c")) { if (@typeOf(int_value).bit_count <= 8) { - return formatAsciiChar(u8(int_value), context, Errors, output); + return formatAsciiChar(u8(int_value), options, context, Errors, output); } else { @compileError("Cannot print integer that is larger than 8 bits as a ascii"); } @@ -496,21 +486,21 @@ pub fn formatIntValue( @compileError("Unknown format string: '" ++ fmt ++ "'"); } - return formatInt(int_value, radix, uppercase, options.width orelse 0, context, Errors, output); + return formatInt(int_value, radix, uppercase, options, context, Errors, output); } fn formatFloatValue( value: var, comptime fmt: []const u8, - comptime options: FormatOptions, + options: FormatOptions, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void, ) Errors!void { if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "e")) { - return formatFloatScientific(value, options.precision, context, Errors, output); + return formatFloatScientific(value, options, context, Errors, output); } else if (comptime std.mem.eql(u8, fmt, "d")) { - return formatFloatDecimal(value, options.precision, context, Errors, output); + return formatFloatDecimal(value, options, context, Errors, output); } else { @compileError("Unknown format string: '" ++ fmt ++ "'"); } @@ -519,7 +509,7 @@ fn formatFloatValue( pub fn formatText( bytes: []const u8, comptime fmt: []const u8, - comptime options: FormatOptions, + options: FormatOptions, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void, @@ -527,11 +517,10 @@ pub fn formatText( if (fmt.len == 0) { return output(context, bytes); } else if (comptime std.mem.eql(u8, fmt, "s")) { - if (options.width) |w| return formatBuf(bytes, w, context, Errors, output); - return formatBuf(bytes, 0, context, Errors, output); + return formatBuf(bytes, options, context, Errors, output); } else if (comptime (std.mem.eql(u8, fmt, "x") or std.mem.eql(u8, fmt, "X"))) { for (bytes) |c| { - try formatInt(c, 16, fmt[0] == 'X', 2, context, Errors, output); + try formatInt(c, 16, fmt[0] == 'X', FormatOptions{ .width = 2, .fill = '0' }, context, Errors, output); } return; } else { @@ -541,6 +530,7 @@ pub fn formatText( pub fn formatAsciiChar( c: u8, + options: FormatOptions, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void, @@ -550,15 +540,16 @@ pub fn formatAsciiChar( pub fn formatBuf( buf: []const u8, - width: usize, + options: FormatOptions, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void, ) Errors!void { try output(context, buf); + const width = options.width orelse 0; var leftover_padding = if (width > buf.len) (width - buf.len) else return; - const pad_byte: u8 = ' '; + const pad_byte: u8 = options.fill; while (leftover_padding > 0) : (leftover_padding -= 1) { try output(context, (*const [1]u8)(&pad_byte)[0..1]); } @@ -569,7 +560,7 @@ pub fn formatBuf( // same type unambiguously. pub fn formatFloatScientific( value: var, - maybe_precision: ?usize, + options: FormatOptions, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void, @@ -591,7 +582,7 @@ pub fn formatFloatScientific( if (x == 0.0) { try output(context, "0"); - if (maybe_precision) |precision| { + if (options.precision) |precision| { if (precision != 0) { try output(context, "."); var i: usize = 0; @@ -610,7 +601,7 @@ pub fn formatFloatScientific( var buffer: [32]u8 = undefined; var float_decimal = errol.errol3(x, buffer[0..]); - if (maybe_precision) |precision| { + if (options.precision) |precision| { errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific); try output(context, float_decimal.digits[0..1]); @@ -650,13 +641,13 @@ pub fn formatFloatScientific( if (exp > -10 and exp < 10) { try output(context, "0"); } - try formatInt(exp, 10, false, 0, context, Errors, output); + try formatInt(exp, 10, false, FormatOptions{ .width = 0 }, context, Errors, output); } else { try output(context, "-"); if (exp > -10 and exp < 10) { try output(context, "0"); } - try formatInt(-exp, 10, false, 0, context, Errors, output); + try formatInt(-exp, 10, false, FormatOptions{ .width = 0 }, context, Errors, output); } } @@ -664,7 +655,7 @@ pub fn formatFloatScientific( // By default floats are printed at full precision (no rounding). pub fn formatFloatDecimal( value: var, - maybe_precision: ?usize, + options: FormatOptions, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void, @@ -686,7 +677,7 @@ pub fn formatFloatDecimal( if (x == 0.0) { try output(context, "0"); - if (maybe_precision) |precision| { + if (options.precision) |precision| { if (precision != 0) { try output(context, "."); var i: usize = 0; @@ -707,7 +698,7 @@ pub fn formatFloatDecimal( var buffer: [32]u8 = undefined; var float_decimal = errol.errol3(x, buffer[0..]); - if (maybe_precision) |precision| { + if (options.precision) |precision| { errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Decimal); // exp < 0 means the leading is always 0 as errol result is normalized. @@ -809,7 +800,7 @@ pub fn formatFloatDecimal( pub fn formatBytes( value: var, - width: ?usize, + options: FormatOptions, comptime radix: usize, context: var, comptime Errors: type, @@ -833,7 +824,7 @@ pub fn formatBytes( else => unreachable, }; - try formatFloatDecimal(new_value, width, context, Errors, output); + try formatFloatDecimal(new_value, options, context, Errors, output); if (suffix == ' ') { return output(context, "B"); @@ -851,7 +842,7 @@ pub fn formatInt( value: var, base: u8, uppercase: bool, - width: usize, + options: FormatOptions, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void, @@ -863,9 +854,9 @@ pub fn formatInt( value; if (@typeOf(int_value).is_signed) { - return formatIntSigned(int_value, base, uppercase, width, context, Errors, output); + return formatIntSigned(int_value, base, uppercase, options, context, Errors, output); } else { - return formatIntUnsigned(int_value, base, uppercase, width, context, Errors, output); + return formatIntUnsigned(int_value, base, uppercase, options, context, Errors, output); } } @@ -873,26 +864,30 @@ fn formatIntSigned( value: var, base: u8, uppercase: bool, - width: usize, + options: FormatOptions, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void, ) Errors!void { + const new_options = FormatOptions{ + .width = if (options.width) |w| (if (w == 0) 0 else w - 1) else null, + .precision = options.precision, + .fill = options.fill, + }; + const uint = @IntType(false, @typeOf(value).bit_count); if (value < 0) { const minus_sign: u8 = '-'; try output(context, (*const [1]u8)(&minus_sign)[0..]); const new_value = @intCast(uint, -(value + 1)) + 1; - const new_width = if (width == 0) 0 else (width - 1); - return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output); - } else if (width == 0) { - return formatIntUnsigned(@intCast(uint, value), base, uppercase, width, context, Errors, output); + return formatIntUnsigned(new_value, base, uppercase, new_options, context, Errors, output); + } else if (options.width == null or options.width.? == 0) { + return formatIntUnsigned(@intCast(uint, value), base, uppercase, options, context, Errors, output); } else { const plus_sign: u8 = '+'; try output(context, (*const [1]u8)(&plus_sign)[0..]); const new_value = @intCast(uint, value); - const new_width = if (width == 0) 0 else (width - 1); - return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output); + return formatIntUnsigned(new_value, base, uppercase, new_options, context, Errors, output); } } @@ -900,7 +895,7 @@ fn formatIntUnsigned( value: var, base: u8, uppercase: bool, - width: usize, + options: FormatOptions, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void, @@ -921,31 +916,32 @@ fn formatIntUnsigned( } const digits_buf = buf[index..]; + const width = options.width orelse 0; const padding = if (width > digits_buf.len) (width - digits_buf.len) else 0; if (padding > index) { - const zero_byte: u8 = '0'; + const zero_byte: u8 = options.fill; var leftover_padding = padding - index; while (true) { try output(context, (*const [1]u8)(&zero_byte)[0..]); leftover_padding -= 1; if (leftover_padding == 0) break; } - mem.set(u8, buf[0..index], '0'); + mem.set(u8, buf[0..index], options.fill); return output(context, buf); } else { const padded_buf = buf[index - padding ..]; - mem.set(u8, padded_buf[0..padding], '0'); + mem.set(u8, padded_buf[0..padding], options.fill); return output(context, padded_buf); } } -pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, width: usize) usize { +pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, options: FormatOptions) usize { var context = FormatIntBuf{ .out_buf = out_buf, .index = 0, }; - formatInt(value, base, uppercase, width, &context, error{}, formatIntCallback) catch unreachable; + formatInt(value, base, uppercase, options, &context, error{}, formatIntCallback) catch unreachable; return context.index; } const FormatIntBuf = struct { @@ -1088,23 +1084,23 @@ fn countSize(size: *usize, bytes: []const u8) (error{}!void) { test "bufPrintInt" { var buffer: [100]u8 = undefined; const buf = buffer[0..]; - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, 0), "-101111000110000101001110")); - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 10, false, 0), "-12345678")); - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, false, 0), "-bc614e")); - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, true, 0), "-BC614E")); + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, FormatOptions{}), "-101111000110000101001110")); + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 10, false, FormatOptions{}), "-12345678")); + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, false, FormatOptions{}), "-bc614e")); + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, true, FormatOptions{}), "-BC614E")); - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(12345678), 10, true, 0), "12345678")); + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(12345678), 10, true, FormatOptions{}), "12345678")); - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(666), 10, false, 6), "000666")); - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 6), "001234")); - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 1), "1234")); + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(666), 10, false, FormatOptions{ .width = 6 }), " 666")); + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, FormatOptions{ .width = 6 }), " 1234")); + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, FormatOptions{ .width = 1 }), "1234")); - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(42), 10, false, 3), "+42")); - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-42), 10, false, 3), "-42")); + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(42), 10, false, FormatOptions{ .width = 3 }), "+42")); + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-42), 10, false, FormatOptions{ .width = 3 }), "-42")); } -fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, width: usize) []u8 { - return buf[0..formatIntBuf(buf, value, base, uppercase, width)]; +fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, options: FormatOptions) []u8 { + return buf[0..formatIntBuf(buf, value, base, uppercase, options)]; } test "parse u64 digit too big" { @@ -1162,7 +1158,8 @@ test "int.specifier" { } test "int.padded" { - try testFmt("u8: '0001'", "u8: '{:4}'", u8(1)); + try testFmt("u8: ' 1'", "u8: '{:4}'", u8(1)); + try testFmt("u8: 'xxx1'", "u8: '{:x<4}'", u8(1)); } test "buffer" { @@ -1237,7 +1234,7 @@ test "cstr" { test "filesize" { try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024)); - try testFmt("file size: 66.06MB\n", "file size: {B:2}\n", usize(63 * 1024 * 1024)); + try testFmt("file size: 66.06MB\n", "file size: {B:.2}\n", usize(63 * 1024 * 1024)); } test "struct" { @@ -1342,7 +1339,7 @@ test "custom" { pub fn format( self: SelfType, comptime fmt: []const u8, - comptime options: FormatOptions, + options: FormatOptions, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void, @@ -1548,7 +1545,7 @@ test "formatType max_depth" { pub fn format( self: SelfType, comptime fmt: []const u8, - comptime options: FormatOptions, + options: FormatOptions, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void, diff --git a/std/math/big/int.zig b/std/math/big/int.zig index 4ad5c92b3..8a6f6c1f7 100644 --- a/std/math/big/int.zig +++ b/std/math/big/int.zig @@ -519,7 +519,7 @@ pub const Int = struct { pub fn format( self: Int, comptime fmt: []const u8, - comptime options: std.fmt.FormatOptions, + options: std.fmt.FormatOptions, context: var, comptime FmtError: type, output: fn (@typeOf(context), []const u8) FmtError!void, diff --git a/test/compare_output.zig b/test/compare_output.zig index 79057f3c5..cbc74c8be 100644 --- a/test/compare_output.zig +++ b/test/compare_output.zig @@ -124,7 +124,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ const stdout = &(io.getStdOut() catch unreachable).outStream().stream; \\ stdout.print("Hello, world!\n{d:4} {x:3} {c}\n", u32(12), u16(0x12), u8('a')) catch unreachable; \\} - , "Hello, world!\n0012 012 a\n"); + , "Hello, world!\n 12 12 a\n"); cases.addC("number literals", \\const builtin = @import("builtin"); diff --git a/test/stage1/behavior/enum_with_members.zig b/test/stage1/behavior/enum_with_members.zig index 2e022a342..08b195494 100644 --- a/test/stage1/behavior/enum_with_members.zig +++ b/test/stage1/behavior/enum_with_members.zig @@ -8,8 +8,8 @@ const ET = union(enum) { pub fn print(a: *const ET, buf: []u8) anyerror!usize { return switch (a.*) { - ET.SINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0), - ET.UINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0), + ET.SINT => |x| fmt.formatIntBuf(buf, x, 10, false, fmt.FormatOptions{}), + ET.UINT => |x| fmt.formatIntBuf(buf, x, 10, false, fmt.FormatOptions{}), }; } };