authorgravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2019-06-20 20:07:43+12:00
committergravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2019-06-21 20:11:15+12:00
log11526b6e9da75ac682e59fbc2a37a738b8a23d6f
tree569edb8e4fc752b6486d049d36a6062854813343
parent381c6a38b145665a22440f7aa816f0ddd9b70ee5

breaking: Add positional, precision and width support to std.fmt

This removes the odd width and precision specifiers found and replacing them with the more consistent api described in #1358. Take the following example: {1:5.9} This refers to the first argument (0-indexed) in the argument list. It will be printed with a minimum width of 5 and will have a precision of 9 (if applicable). Not all types correctly use these parameters just yet. There are still some missing gaps to fill in. Fill characters and alignment have yet to be implemented.

5 files changed, 354 insertions(+), 367 deletions(-)

src-self-hosted/dep_tokenizer.zig+1-1
......@@ -999,7 +999,7 @@ fn printCharValues(out: var, bytes: []const u8) !void {
999999
10001000fn printUnderstandableChar(out: var, char: u8) !void {
10011001 if (!std.ascii.isPrint(char) or char == ' ') {
1002 std.fmt.format(out.context, anyerror, out.output, "\\x{X2}", char) catch {};
1002 std.fmt.format(out.context, anyerror, out.output, "\\x{X:2}", char) catch {};
10031003 } else {
10041004 try out.write("'");
10051005 try out.write([_]u8{printable_char_tab[char]});
std/fmt.zig+349-363
......@@ -10,6 +10,22 @@ const lossyCast = std.math.lossyCast;
1010
1111pub const default_max_depth = 3;
1212
13pub const FormatOptions = struct {
14 precision: ?usize = null,
15 width: ?usize = null,
16};
17
18fn nextArg(comptime used_pos_args: *u32, comptime maybe_pos_arg: ?comptime_int, comptime next_arg: *comptime_int) comptime_int {
19 if (maybe_pos_arg) |pos_arg| {
20 used_pos_args.* |= 1 << pos_arg;
21 return pos_arg;
22 } else {
23 const arg = next_arg.*;
24 next_arg.* += 1;
25 return arg;
26 }
27}
28
1329/// Renders fmt string with args, calling output with slices of bytes.
1430/// If `output` returns an error, the error is returned from `format` and
1531/// `output` is not called again.
......@@ -20,17 +36,29 @@ pub fn format(
2036 comptime fmt: []const u8,
2137 args: ...,
2238) Errors!void {
39 const ArgSetType = @IntType(false, 32);
40 if (args.len > ArgSetType.bit_count) {
41 @compileError("32 arguments max are supported per format call");
42 }
43
2344 const State = enum {
2445 Start,
25 OpenBrace,
46 Positional,
2647 CloseBrace,
27 FormatString,
48 Specifier,
49 FormatWidth,
50 FormatPrecision,
2851 Pointer,
2952 };
3053
3154 comptime var start_index = 0;
3255 comptime var state = State.Start;
3356 comptime var next_arg = 0;
57 comptime var maybe_pos_arg: ?comptime_int = null;
58 comptime var used_pos_args: ArgSetType = 0;
59 comptime var specifier_start = 0;
60 comptime var specifier_end = 0;
61 comptime var options = FormatOptions{};
3462
3563 inline for (fmt) |c, i| {
3664 switch (state) {
......@@ -39,58 +67,165 @@ pub fn format(
3967 if (start_index < i) {
4068 try output(context, fmt[start_index..i]);
4169 }
70
4271 start_index = i;
43 state = State.OpenBrace;
72 specifier_start = i + 1;
73 specifier_end = i + 1;
74 maybe_pos_arg = null;
75 state = .Positional;
76 options = FormatOptions{};
4477 },
45
4678 '}' => {
4779 if (start_index < i) {
4880 try output(context, fmt[start_index..i]);
4981 }
50 state = State.CloseBrace;
82 state = .CloseBrace;
5183 },
5284 else => {},
5385 },
54 .OpenBrace => switch (c) {
86 .Positional => switch (c) {
5587 '{' => {
56 state = State.Start;
88 state = .Start;
5789 start_index = i;
5890 },
91 '*' => {
92 state = .Pointer;
93 },
94 ':' => {
95 state = .FormatWidth;
96 specifier_end = i;
97 },
98 '0'...'9' => {
99 if (maybe_pos_arg == null) {
100 maybe_pos_arg = 0;
101 }
102
103 maybe_pos_arg.? *= 10;
104 maybe_pos_arg.? += c - '0';
105 specifier_start = i + 1;
106
107 if (maybe_pos_arg.? >= args.len) {
108 @compileError("Positional value refers to non-existent argument");
109 }
110 },
59111 '}' => {
60 try formatType(args[next_arg], fmt[0..0], context, Errors, output, default_max_depth);
61 next_arg += 1;
62 state = State.Start;
112 const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg);
113
114 try formatType(
115 args[arg_to_print],
116 fmt[0..0],
117 options,
118 context,
119 Errors,
120 output,
121 default_max_depth,
122 );
123
124 state = .Start;
63125 start_index = i + 1;
64126 },
65 '*' => state = State.Pointer,
66127 else => {
67 state = State.FormatString;
128 state = .Specifier;
129 specifier_start = i;
68130 },
69131 },
70132 .CloseBrace => switch (c) {
71133 '}' => {
72 state = State.Start;
134 state = .Start;
73135 start_index = i;
74136 },
75137 else => @compileError("Single '}' encountered in format string"),
76138 },
77 .FormatString => switch (c) {
139 .Specifier => switch (c) {
140 ':' => {
141 specifier_end = i;
142 state = .FormatWidth;
143 },
78144 '}' => {
79 const s = start_index + 1;
80 try formatType(args[next_arg], fmt[s..i], context, Errors, output, default_max_depth);
81 next_arg += 1;
82 state = State.Start;
145 const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg);
146
147 try formatType(
148 args[arg_to_print],
149 fmt[specifier_start..i],
150 options,
151 context,
152 Errors,
153 output,
154 default_max_depth,
155 );
156 state = .Start;
83157 start_index = i + 1;
84158 },
85159 else => {},
86160 },
161 .FormatWidth => switch (c) {
162 '0'...'9' => {
163 if (options.width == null) {
164 options.width = 0;
165 }
166
167 options.width.? *= 10;
168 options.width.? += c - '0';
169 },
170 '.' => {
171 state = .FormatPrecision;
172 },
173 '}' => {
174 const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg);
175
176 try formatType(
177 args[arg_to_print],
178 fmt[specifier_start..specifier_end],
179 options,
180 context,
181 Errors,
182 output,
183 default_max_depth,
184 );
185 state = .Start;
186 start_index = i + 1;
187 },
188 else => {
189 @compileError("Unexpected character in width value: " ++ [_]u8{c});
190 },
191 },
192 .FormatPrecision => switch (c) {
193 '0'...'9' => {
194 if (options.precision == null) {
195 options.precision = 0;
196 }
197
198 options.precision.? *= 10;
199 options.precision.? += c - '0';
200 },
201 '}' => {
202 const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg);
203
204 try formatType(
205 args[arg_to_print],
206 fmt[specifier_start..specifier_end],
207 options,
208 context,
209 Errors,
210 output,
211 default_max_depth,
212 );
213 state = .Start;
214 start_index = i + 1;
215 },
216 else => {
217 @compileError("Unexpected character in precision value: " ++ [_]u8{c});
218 },
219 },
87220 .Pointer => switch (c) {
88221 '}' => {
89 try output(context, @typeName(@typeOf(args[next_arg]).Child));
222 const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg);
223
224 try output(context, @typeName(@typeOf(args[arg_to_print]).Child));
90225 try output(context, "@");
91 try formatInt(@ptrToInt(args[next_arg]), 16, false, 0, context, Errors, output);
92 next_arg += 1;
93 state = State.Start;
226 try formatInt(@ptrToInt(args[arg_to_print]), 16, false, 0, context, Errors, output);
227
228 state = .Start;
94229 start_index = i + 1;
95230 },
96231 else => @compileError("Unexpected format character after '*'"),
......@@ -98,7 +233,13 @@ pub fn format(
98233 }
99234 }
100235 comptime {
101 if (args.len != next_arg) {
236 // All arguments must have been printed but we allow mixing positional and fixed to achieve this.
237 var i: usize = 0;
238 inline while (i < next_arg) : (i += 1) {
239 used_pos_args |= 1 << i;
240 }
241
242 if (@popCount(ArgSetType, used_pos_args) != args.len) {
102243 @compileError("Unused arguments");
103244 }
104245 if (state != State.Start) {
......@@ -113,6 +254,7 @@ pub fn format(
113254pub fn formatType(
114255 value: var,
115256 comptime fmt: []const u8,
257 comptime options: FormatOptions,
116258 context: var,
117259 comptime Errors: type,
118260 output: fn (@typeOf(context), []const u8) Errors!void,
......@@ -121,7 +263,7 @@ pub fn formatType(
121263 const T = @typeOf(value);
122264 switch (@typeInfo(T)) {
123265 .ComptimeInt, .Int, .Float => {
124 return formatValue(value, fmt, context, Errors, output);
266 return formatValue(value, fmt, options, context, Errors, output);
125267 },
126268 .Void => {
127269 return output(context, "void");
......@@ -131,16 +273,16 @@ pub fn formatType(
131273 },
132274 .Optional => {
133275 if (value) |payload| {
134 return formatType(payload, fmt, context, Errors, output, max_depth);
276 return formatType(payload, fmt, options, context, Errors, output, max_depth);
135277 } else {
136278 return output(context, "null");
137279 }
138280 },
139281 .ErrorUnion => {
140282 if (value) |payload| {
141 return formatType(payload, fmt, context, Errors, output, max_depth);
283 return formatType(payload, fmt, options, context, Errors, output, max_depth);
142284 } else |err| {
143 return formatType(err, fmt, context, Errors, output, max_depth);
285 return formatType(err, fmt, options, context, Errors, output, max_depth);
144286 }
145287 },
146288 .ErrorSet => {
......@@ -152,16 +294,16 @@ pub fn formatType(
152294 },
153295 .Enum => {
154296 if (comptime std.meta.trait.hasFn("format")(T)) {
155 return value.format(fmt, context, Errors, output);
297 return value.format(fmt, options, context, Errors, output);
156298 }
157299
158300 try output(context, @typeName(T));
159301 try output(context, ".");
160 return formatType(@tagName(value), "", context, Errors, output, max_depth);
302 return formatType(@tagName(value), "", options, context, Errors, output, max_depth);
161303 },
162304 .Union => {
163305 if (comptime std.meta.trait.hasFn("format")(T)) {
164 return value.format(fmt, context, Errors, output);
306 return value.format(fmt, options, context, Errors, output);
165307 }
166308
167309 try output(context, @typeName(T));
......@@ -175,7 +317,7 @@ pub fn formatType(
175317 try output(context, " = ");
176318 inline for (info.fields) |u_field| {
177319 if (@enumToInt(UnionTagType(value)) == u_field.enum_field.?.value) {
178 try formatType(@field(value, u_field.name), "", context, Errors, output, max_depth - 1);
320 try formatType(@field(value, u_field.name), "", options, context, Errors, output, max_depth - 1);
179321 }
180322 }
181323 try output(context, " }");
......@@ -185,7 +327,7 @@ pub fn formatType(
185327 },
186328 .Struct => {
187329 if (comptime std.meta.trait.hasFn("format")(T)) {
188 return value.format(fmt, context, Errors, output);
330 return value.format(fmt, options, context, Errors, output);
189331 }
190332
191333 try output(context, @typeName(T));
......@@ -201,7 +343,7 @@ pub fn formatType(
201343 }
202344 try output(context, @memberName(T, field_i));
203345 try output(context, " = ");
204 try formatType(@field(value, @memberName(T, field_i)), "", context, Errors, output, max_depth - 1);
346 try formatType(@field(value, @memberName(T, field_i)), "", options, context, Errors, output, max_depth - 1);
205347 }
206348 try output(context, " }");
207349 },
......@@ -209,12 +351,12 @@ pub fn formatType(
209351 .One => switch (@typeInfo(ptr_info.child)) {
210352 builtin.TypeId.Array => |info| {
211353 if (info.child == u8) {
212 return formatText(value, fmt, context, Errors, output);
354 return formatText(value, fmt, options, context, Errors, output);
213355 }
214356 return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
215357 },
216358 builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => {
217 return formatType(value.*, fmt, context, Errors, output, max_depth);
359 return formatType(value.*, fmt, options, context, Errors, output, max_depth);
218360 },
219361 else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)),
220362 },
......@@ -222,17 +364,17 @@ pub fn formatType(
222364 if (ptr_info.child == u8) {
223365 if (fmt.len > 0 and fmt[0] == 's') {
224366 const len = mem.len(u8, value);
225 return formatText(value[0..len], fmt, context, Errors, output);
367 return formatText(value[0..len], fmt, options, context, Errors, output);
226368 }
227369 }
228370 return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
229371 },
230372 .Slice => {
231373 if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) {
232 return formatText(value, fmt, context, Errors, output);
374 return formatText(value, fmt, options, context, Errors, output);
233375 }
234376 if (ptr_info.child == u8) {
235 return formatText(value, fmt, context, Errors, output);
377 return formatText(value, fmt, options, context, Errors, output);
236378 }
237379 return format(context, Errors, output, "{}@{x}", @typeName(ptr_info.child), @ptrToInt(value.ptr));
238380 },
......@@ -242,7 +384,7 @@ pub fn formatType(
242384 },
243385 .Array => |info| {
244386 if (info.child == u8) {
245 return formatText(value, fmt, context, Errors, output);
387 return formatText(value, fmt, options, context, Errors, output);
246388 }
247389 return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(&value));
248390 },
......@@ -256,28 +398,23 @@ pub fn formatType(
256398fn formatValue(
257399 value: var,
258400 comptime fmt: []const u8,
401 comptime options: FormatOptions,
259402 context: var,
260403 comptime Errors: type,
261404 output: fn (@typeOf(context), []const u8) Errors!void,
262405) Errors!void {
263 if (fmt.len > 0 and fmt[0] == 'B') {
264 comptime var width: ?usize = null;
265 if (fmt.len > 1) {
266 if (fmt[1] == 'i') {
267 if (fmt.len > 2) {
268 width = comptime (parseUnsigned(usize, fmt[2..], 10) catch unreachable);
269 }
270 return formatBytes(value, width, 1024, context, Errors, output);
271 }
272 width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable);
273 }
274 return formatBytes(value, width, 1000, context, Errors, output);
406 if (comptime std.mem.eql(u8, fmt, "B")) {
407 if (options.width) |w| return formatBytes(value, w, 1000, context, Errors, output);
408 return formatBytes(value, null, 1000, context, Errors, output);
409 } else if (comptime std.mem.eql(u8, fmt, "Bi")) {
410 if (options.width) |w| return formatBytes(value, w, 1024, context, Errors, output);
411 return formatBytes(value, null, 1024, context, Errors, output);
275412 }
276413
277414 const T = @typeOf(value);
278415 switch (@typeId(T)) {
279 .Float => return formatFloatValue(value, fmt, context, Errors, output),
280 .Int, .ComptimeInt => return formatIntValue(value, fmt, context, Errors, output),
416 .Float => return formatFloatValue(value, fmt, options, context, Errors, output),
417 .Int, .ComptimeInt => return formatIntValue(value, fmt, options, context, Errors, output),
281418 else => comptime unreachable,
282419 }
283420}
......@@ -285,13 +422,13 @@ fn formatValue(
285422pub fn formatIntValue(
286423 value: var,
287424 comptime fmt: []const u8,
425 comptime options: FormatOptions,
288426 context: var,
289427 comptime Errors: type,
290428 output: fn (@typeOf(context), []const u8) Errors!void,
291429) Errors!void {
292430 comptime var radix = 10;
293431 comptime var uppercase = false;
294 comptime var width = 0;
295432
296433 const int_value = if (@typeOf(value) == comptime_int) blk: {
297434 const Int = math.IntFittingRange(value, value);
......@@ -299,83 +436,72 @@ pub fn formatIntValue(
299436 } else
300437 value;
301438
302 if (fmt.len > 0) {
303 switch (fmt[0]) {
304 'c' => {
305 if (@typeOf(int_value).bit_count <= 8) {
306 if (fmt.len > 1)
307 @compileError("Unknown format character: " ++ [_]u8{fmt[1]});
308 return formatAsciiChar(u8(int_value), context, Errors, output);
309 }
310 },
311 'b' => {
312 radix = 2;
313 uppercase = false;
314 width = 0;
315 },
316 'd' => {
317 radix = 10;
318 uppercase = false;
319 width = 0;
320 },
321 'x' => {
322 radix = 16;
323 uppercase = false;
324 width = 0;
325 },
326 'X' => {
327 radix = 16;
328 uppercase = true;
329 width = 0;
330 },
331 else => @compileError("Unknown format character: " ++ [_]u8{fmt[0]}),
439 if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "d")) {
440 radix = 10;
441 uppercase = false;
442 } else if (comptime std.mem.eql(u8, fmt, "c")) {
443 if (@typeOf(int_value).bit_count <= 8) {
444 return formatAsciiChar(u8(int_value), context, Errors, output);
445 } else {
446 @compileError("Cannot print integer that is larger than 8 bits as a ascii");
332447 }
333 if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable);
448 } else if (comptime std.mem.eql(u8, fmt, "b")) {
449 radix = 2;
450 uppercase = false;
451 } else if (comptime std.mem.eql(u8, fmt, "x")) {
452 radix = 16;
453 uppercase = false;
454 } else if (comptime std.mem.eql(u8, fmt, "X")) {
455 radix = 16;
456 uppercase = true;
457 } else {
458 @compileError("Unknown format string: '" ++ fmt ++ "'");
334459 }
335 return formatInt(int_value, radix, uppercase, width, context, Errors, output);
460
461 if (options.width) |w| return formatInt(int_value, radix, uppercase, w, context, Errors, output);
462 return formatInt(int_value, radix, uppercase, 0, context, Errors, output);
336463}
337464
338465fn formatFloatValue(
339466 value: var,
340467 comptime fmt: []const u8,
468 comptime options: FormatOptions,
341469 context: var,
342470 comptime Errors: type,
343471 output: fn (@typeOf(context), []const u8) Errors!void,
344472) Errors!void {
345 comptime var width: ?usize = null;
346 comptime var float_fmt = 'e';
347 if (fmt.len > 0) {
348 float_fmt = fmt[0];
349 if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable);
350 }
351
352 switch (float_fmt) {
353 'e' => try formatFloatScientific(value, width, context, Errors, output),
354 '.' => try formatFloatDecimal(value, width, context, Errors, output),
355 else => @compileError("Unknown format character: " ++ [_]u8{float_fmt}),
473 if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "e")) {
474 if (options.precision) |p| return formatFloatScientific(value, p, context, Errors, output);
475 return formatFloatScientific(value, null, context, Errors, output);
476 } else if (comptime std.mem.eql(u8, fmt, "d")) {
477 if (options.precision) |p| return formatFloatDecimal(value, p, context, Errors, output);
478 return formatFloatDecimal(value, options.precision, context, Errors, output);
479 } else {
480 @compileError("Unknown format string: '" ++ fmt ++ "'");
356481 }
357482}
358483
359484pub fn formatText(
360485 bytes: []const u8,
361486 comptime fmt: []const u8,
487 comptime options: FormatOptions,
362488 context: var,
363489 comptime Errors: type,
364490 output: fn (@typeOf(context), []const u8) Errors!void,
365491) Errors!void {
366 if (fmt.len > 0) {
367 if (fmt[0] == 's') {
368 comptime var width = 0;
369 if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable);
370 return formatBuf(bytes, width, context, Errors, output);
371 } else if ((fmt[0] == 'x') or (fmt[0] == 'X')) {
372 for (bytes) |c| {
373 try formatInt(c, 16, fmt[0] == 'X', 2, context, Errors, output);
374 }
375 return;
376 } else @compileError("Unknown format character: " ++ [_]u8{fmt[0]});
492 if (fmt.len == 0) {
493 return output(context, bytes);
494 } else if (comptime std.mem.eql(u8, fmt, "s")) {
495 if (options.width) |w| return formatBuf(bytes, w, context, Errors, output);
496 return formatBuf(bytes, 0, context, Errors, output);
497 } else if (comptime (std.mem.eql(u8, fmt, "x") or std.mem.eql(u8, fmt, "X"))) {
498 for (bytes) |c| {
499 try formatInt(c, 16, fmt[0] == 'X', 2, context, Errors, output);
500 }
501 return;
502 } else {
503 @compileError("Unknown format string: '" ++ fmt ++ "'");
377504 }
378 return output(context, bytes);
379505}
380506
381507pub fn formatAsciiChar(
......@@ -868,7 +994,7 @@ test "parseUnsigned" {
868994
869995pub const parseFloat = @import("fmt/parse_float.zig").parseFloat;
870996
871test "fmt.parseFloat" {
997test "parseFloat" {
872998 _ = @import("fmt/parse_float.zig");
873999}
8741000
......@@ -960,7 +1086,7 @@ test "parse unsigned comptime" {
9601086 }
9611087}
9621088
963test "fmt.optional" {
1089test "optional" {
9641090 {
9651091 const value: ?i32 = 1234;
9661092 try testFmt("optional: 1234\n", "optional: {}\n", value);
......@@ -971,7 +1097,7 @@ test "fmt.optional" {
9711097 }
9721098}
9731099
974test "fmt.error" {
1100test "error" {
9751101 {
9761102 const value: anyerror!i32 = 1234;
9771103 try testFmt("error union: 1234\n", "error union: {}\n", value);
......@@ -982,14 +1108,14 @@ test "fmt.error" {
9821108 }
9831109}
9841110
985test "fmt.int.small" {
1111test "int.small" {
9861112 {
9871113 const value: u3 = 0b101;
9881114 try testFmt("u3: 5\n", "u3: {}\n", value);
9891115 }
9901116}
9911117
992test "fmt.int.specifier" {
1118test "int.specifier" {
9931119 {
9941120 const value: u8 = 'a';
9951121 try testFmt("u8: a\n", "u8: {c}\n", value);
......@@ -1000,27 +1126,31 @@ test "fmt.int.specifier" {
10001126 }
10011127}
10021128
1003test "fmt.buffer" {
1129test "int.padded" {
1130 try testFmt("u8: '0001'", "u8: '{:4}'", u8(1));
1131}
1132
1133test "buffer" {
10041134 {
10051135 var buf1: [32]u8 = undefined;
10061136 var context = BufPrintContext{ .remaining = buf1[0..] };
1007 try formatType(1234, "", &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth);
1137 try formatType(1234, "", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth);
10081138 var res = buf1[0 .. buf1.len - context.remaining.len];
10091139 testing.expect(mem.eql(u8, res, "1234"));
10101140
10111141 context = BufPrintContext{ .remaining = buf1[0..] };
1012 try formatType('a', "c", &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth);
1142 try formatType('a', "c", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth);
10131143 res = buf1[0 .. buf1.len - context.remaining.len];
10141144 testing.expect(mem.eql(u8, res, "a"));
10151145
10161146 context = BufPrintContext{ .remaining = buf1[0..] };
1017 try formatType(0b1100, "b", &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth);
1147 try formatType(0b1100, "b", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth);
10181148 res = buf1[0 .. buf1.len - context.remaining.len];
10191149 testing.expect(mem.eql(u8, res, "1100"));
10201150 }
10211151}
10221152
1023test "fmt.array" {
1153test "array" {
10241154 {
10251155 const value: [3]u8 = "abc";
10261156 try testFmt("array: abc\n", "array: {}\n", value);
......@@ -1035,7 +1165,7 @@ test "fmt.array" {
10351165 }
10361166}
10371167
1038test "fmt.slice" {
1168test "slice" {
10391169 {
10401170 const value: []const u8 = "abc";
10411171 try testFmt("slice: abc\n", "slice: {}\n", value);
......@@ -1045,11 +1175,11 @@ test "fmt.slice" {
10451175 try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", value);
10461176 }
10471177
1048 try testFmt("buf: Test \n", "buf: {s5}\n", "Test");
1178 try testFmt("buf: Test \n", "buf: {s:5}\n", "Test");
10491179 try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test");
10501180}
10511181
1052test "fmt.pointer" {
1182test "pointer" {
10531183 {
10541184 const value = @intToPtr(*i32, 0xdeadbeef);
10551185 try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", value);
......@@ -1065,17 +1195,17 @@ test "fmt.pointer" {
10651195 }
10661196}
10671197
1068test "fmt.cstr" {
1198test "cstr" {
10691199 try testFmt("cstr: Test C\n", "cstr: {s}\n", c"Test C");
1070 try testFmt("cstr: Test C \n", "cstr: {s10}\n", c"Test C");
1200 try testFmt("cstr: Test C \n", "cstr: {s:10}\n", c"Test C");
10711201}
10721202
1073test "fmt.filesize" {
1203test "filesize" {
10741204 try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024));
1075 try testFmt("file size: 66.06MB\n", "file size: {B2}\n", usize(63 * 1024 * 1024));
1205 try testFmt("file size: 66.06MB\n", "file size: {B:2}\n", usize(63 * 1024 * 1024));
10761206}
10771207
1078test "fmt.struct" {
1208test "struct" {
10791209 {
10801210 const Struct = struct {
10811211 field: u8,
......@@ -1094,7 +1224,7 @@ test "fmt.struct" {
10941224 }
10951225}
10961226
1097test "fmt.enum" {
1227test "enum" {
10981228 const Enum = enum {
10991229 One,
11001230 Two,
......@@ -1104,229 +1234,71 @@ test "fmt.enum" {
11041234 try testFmt("enum: Enum.Two\n", "enum: {}\n", &value);
11051235}
11061236
1107test "fmt.float.scientific" {
1108 {
1109 var buf1: [32]u8 = undefined;
1110 const value: f32 = 1.34;
1111 const result = try bufPrint(buf1[0..], "f32: {e}\n", value);
1112 testing.expect(mem.eql(u8, result, "f32: 1.34000003e+00\n"));
1113 }
1114 {
1115 var buf1: [32]u8 = undefined;
1116 const value: f32 = 12.34;
1117 const result = try bufPrint(buf1[0..], "f32: {e}\n", value);
1118 testing.expect(mem.eql(u8, result, "f32: 1.23400001e+01\n"));
1119 }
1120 {
1121 var buf1: [32]u8 = undefined;
1122 const value: f64 = -12.34e10;
1123 const result = try bufPrint(buf1[0..], "f64: {e}\n", value);
1124 testing.expect(mem.eql(u8, result, "f64: -1.234e+11\n"));
1125 }
1126 {
1127 // This fails on release due to a minor rounding difference.
1128 // --release-fast outputs 9.999960000000001e-40 vs. the expected.
1129 // TODO fix this, it should be the same in Debug and ReleaseFast
1130 if (builtin.mode == builtin.Mode.Debug) {
1131 var buf1: [32]u8 = undefined;
1132 const value: f64 = 9.999960e-40;
1133 const result = try bufPrint(buf1[0..], "f64: {e}\n", value);
1134 testing.expect(mem.eql(u8, result, "f64: 9.99996e-40\n"));
1135 }
1136 }
1237test "float.scientific" {
1238 try testFmt("f32: 1.34000003e+00", "f32: {e}", f32(1.34));
1239 try testFmt("f32: 1.23400001e+01", "f32: {e}", f32(12.34));
1240 try testFmt("f64: -1.234e+11", "f64: {e}", f64(-12.34e10));
1241 try testFmt("f64: 9.99996e-40", "f64: {e}", f64(9.999960e-40));
11371242}
11381243
1139test "fmt.float.scientific.precision" {
1140 {
1141 var buf1: [32]u8 = undefined;
1142 const value: f64 = 1.409706e-42;
1143 const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
1144 testing.expect(mem.eql(u8, result, "f64: 1.40971e-42\n"));
1145 }
1146 {
1147 var buf1: [32]u8 = undefined;
1148 const value: f64 = @bitCast(f32, u32(814313563));
1149 const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
1150 testing.expect(mem.eql(u8, result, "f64: 1.00000e-09\n"));
1151 }
1152 {
1153 var buf1: [32]u8 = undefined;
1154 const value: f64 = @bitCast(f32, u32(1006632960));
1155 const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
1156 testing.expect(mem.eql(u8, result, "f64: 7.81250e-03\n"));
1157 }
1158 {
1159 // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05.
1160 // In fact, libc doesn't round a lot of 5 cases up when one past the precision point.
1161 var buf1: [32]u8 = undefined;
1162 const value: f64 = @bitCast(f32, u32(1203982400));
1163 const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
1164 testing.expect(mem.eql(u8, result, "f64: 1.00001e+05\n"));
1165 }
1244test "float.scientific.precision" {
1245 try testFmt("f64: 1.40971e-42", "f64: {e:.5}", f64(1.409706e-42));
1246 try testFmt("f64: 1.00000e-09", "f64: {e:.5}", f64(@bitCast(f32, u32(814313563))));
1247 try testFmt("f64: 7.81250e-03", "f64: {e:.5}", f64(@bitCast(f32, u32(1006632960))));
1248 // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05.
1249 // In fact, libc doesn't round a lot of 5 cases up when one past the precision point.
1250 try testFmt("f64: 1.00001e+05", "f64: {e:.5}", f64(@bitCast(f32, u32(1203982400))));
11661251}
11671252
1168test "fmt.float.special" {
1169 {
1170 var buf1: [32]u8 = undefined;
1171 const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64);
1172 testing.expect(mem.eql(u8, result, "f64: nan\n"));
1173 }
1253test "float.special" {
1254 try testFmt("f64: nan", "f64: {}", math.nan_f64);
1255 // negative nan is not defined by IEE 754,
1256 // and ARM thus normalizes it to positive nan
11741257 if (builtin.arch != builtin.Arch.arm) {
1175 // negative nan is not defined by IEE 754,
1176 // and ARM thus normalizes it to positive nan
1177 var buf1: [32]u8 = undefined;
1178 const result = try bufPrint(buf1[0..], "f64: {}\n", -math.nan_f64);
1179 testing.expect(mem.eql(u8, result, "f64: -nan\n"));
1180 }
1181 {
1182 var buf1: [32]u8 = undefined;
1183 const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64);
1184 testing.expect(mem.eql(u8, result, "f64: inf\n"));
1185 }
1186 {
1187 var buf1: [32]u8 = undefined;
1188 const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64);
1189 testing.expect(mem.eql(u8, result, "f64: -inf\n"));
1258 try testFmt("f64: -nan", "f64: {}", -math.nan_f64);
11901259 }
1260 try testFmt("f64: inf", "f64: {}", math.inf_f64);
1261 try testFmt("f64: -inf", "f64: {}", -math.inf_f64);
11911262}
11921263
1193test "fmt.float.decimal" {
1194 {
1195 var buf1: [64]u8 = undefined;
1196 const value: f64 = 1.52314e+29;
1197 const result = try bufPrint(buf1[0..], "f64: {.}\n", value);
1198 testing.expect(mem.eql(u8, result, "f64: 152314000000000000000000000000\n"));
1199 }
1200 {
1201 var buf1: [32]u8 = undefined;
1202 const value: f32 = 1.1234;
1203 const result = try bufPrint(buf1[0..], "f32: {.1}\n", value);
1204 testing.expect(mem.eql(u8, result, "f32: 1.1\n"));
1205 }
1206 {
1207 var buf1: [32]u8 = undefined;
1208 const value: f32 = 1234.567;
1209 const result = try bufPrint(buf1[0..], "f32: {.2}\n", value);
1210 testing.expect(mem.eql(u8, result, "f32: 1234.57\n"));
1211 }
1212 {
1213 var buf1: [32]u8 = undefined;
1214 const value: f32 = -11.1234;
1215 const result = try bufPrint(buf1[0..], "f32: {.4}\n", value);
1216 // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64).
1217 // -11.12339... is rounded back up to -11.1234
1218 testing.expect(mem.eql(u8, result, "f32: -11.1234\n"));
1219 }
1220 {
1221 var buf1: [32]u8 = undefined;
1222 const value: f32 = 91.12345;
1223 const result = try bufPrint(buf1[0..], "f32: {.5}\n", value);
1224 testing.expect(mem.eql(u8, result, "f32: 91.12345\n"));
1225 }
1226 {
1227 var buf1: [32]u8 = undefined;
1228 const value: f64 = 91.12345678901235;
1229 const result = try bufPrint(buf1[0..], "f64: {.10}\n", value);
1230 testing.expect(mem.eql(u8, result, "f64: 91.1234567890\n"));
1231 }
1232 {
1233 var buf1: [32]u8 = undefined;
1234 const value: f64 = 0.0;
1235 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
1236 testing.expect(mem.eql(u8, result, "f64: 0.00000\n"));
1237 }
1238 {
1239 var buf1: [32]u8 = undefined;
1240 const value: f64 = 5.700;
1241 const result = try bufPrint(buf1[0..], "f64: {.0}\n", value);
1242 testing.expect(mem.eql(u8, result, "f64: 6\n"));
1243 }
1244 {
1245 var buf1: [32]u8 = undefined;
1246 const value: f64 = 9.999;
1247 const result = try bufPrint(buf1[0..], "f64: {.1}\n", value);
1248 testing.expect(mem.eql(u8, result, "f64: 10.0\n"));
1249 }
1250 {
1251 var buf1: [32]u8 = undefined;
1252 const value: f64 = 1.0;
1253 const result = try bufPrint(buf1[0..], "f64: {.3}\n", value);
1254 testing.expect(mem.eql(u8, result, "f64: 1.000\n"));
1255 }
1256 {
1257 var buf1: [32]u8 = undefined;
1258 const value: f64 = 0.0003;
1259 const result = try bufPrint(buf1[0..], "f64: {.8}\n", value);
1260 testing.expect(mem.eql(u8, result, "f64: 0.00030000\n"));
1261 }
1262 {
1263 var buf1: [32]u8 = undefined;
1264 const value: f64 = 1.40130e-45;
1265 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
1266 testing.expect(mem.eql(u8, result, "f64: 0.00000\n"));
1267 }
1268 {
1269 var buf1: [32]u8 = undefined;
1270 const value: f64 = 9.999960e-40;
1271 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
1272 testing.expect(mem.eql(u8, result, "f64: 0.00000\n"));
1273 }
1264test "float.decimal" {
1265 try testFmt("f64: 152314000000000000000000000000", "f64: {d}", f64(1.52314e+29));
1266 try testFmt("f32: 1.1", "f32: {d:.1}", f32(1.1234));
1267 try testFmt("f32: 1234.57", "f32: {d:.2}", f32(1234.567));
1268 // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64).
1269 // -11.12339... is rounded back up to -11.1234
1270 try testFmt("f32: -11.1234", "f32: {d:.4}", f32(-11.1234));
1271 try testFmt("f32: 91.12345", "f32: {d:.5}", f32(91.12345));
1272 try testFmt("f64: 91.1234567890", "f64: {d:.10}", f64(91.12345678901235));
1273 try testFmt("f64: 0.00000", "f64: {d:.5}", f64(0.0));
1274 try testFmt("f64: 6", "f64: {d:.0}", f64(5.700));
1275 try testFmt("f64: 10.0", "f64: {d:.1}", f64(9.999));
1276 try testFmt("f64: 1.000", "f64: {d:.3}", f64(1.0));
1277 try testFmt("f64: 0.00030000", "f64: {d:.8}", f64(0.0003));
1278 try testFmt("f64: 0.00000", "f64: {d:.5}", f64(1.40130e-45));
1279 try testFmt("f64: 0.00000", "f64: {d:.5}", f64(9.999960e-40));
12741280}
12751281
1276test "fmt.float.libc.sanity" {
1277 {
1278 var buf1: [32]u8 = undefined;
1279 const value: f64 = f64(@bitCast(f32, u32(916964781)));
1280 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
1281 testing.expect(mem.eql(u8, result, "f64: 0.00001\n"));
1282 }
1283 {
1284 var buf1: [32]u8 = undefined;
1285 const value: f64 = f64(@bitCast(f32, u32(925353389)));
1286 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
1287 testing.expect(mem.eql(u8, result, "f64: 0.00001\n"));
1288 }
1289 {
1290 var buf1: [32]u8 = undefined;
1291 const value: f64 = f64(@bitCast(f32, u32(1036831278)));
1292 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
1293 testing.expect(mem.eql(u8, result, "f64: 0.10000\n"));
1294 }
1295 {
1296 var buf1: [32]u8 = undefined;
1297 const value: f64 = f64(@bitCast(f32, u32(1065353133)));
1298 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
1299 testing.expect(mem.eql(u8, result, "f64: 1.00000\n"));
1300 }
1301 {
1302 var buf1: [32]u8 = undefined;
1303 const value: f64 = f64(@bitCast(f32, u32(1092616192)));
1304 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
1305 testing.expect(mem.eql(u8, result, "f64: 10.00000\n"));
1306 }
1282test "float.libc.sanity" {
1283 try testFmt("f64: 0.00001", "f64: {d:.5}", f64(@bitCast(f32, u32(916964781))));
1284 try testFmt("f64: 0.00001", "f64: {d:.5}", f64(@bitCast(f32, u32(925353389))));
1285 try testFmt("f64: 0.10000", "f64: {d:.5}", f64(@bitCast(f32, u32(1036831278))));
1286 try testFmt("f64: 1.00000", "f64: {d:.5}", f64(@bitCast(f32, u32(1065353133))));
1287 try testFmt("f64: 10.00000", "f64: {d:.5}", f64(@bitCast(f32, u32(1092616192))));
1288
13071289 // libc differences
1308 {
1309 var buf1: [32]u8 = undefined;
1310 // This is 0.015625 exactly according to gdb. We thus round down,
1311 // however glibc rounds up for some reason. This occurs for all
1312 // floats of the form x.yyyy25 on a precision point.
1313 const value: f64 = f64(@bitCast(f32, u32(1015021568)));
1314 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
1315 testing.expect(mem.eql(u8, result, "f64: 0.01563\n"));
1316 }
1317 // std-windows-x86_64-Debug-bare test case fails
1318 {
1319 // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3
1320 // also rounds to 630 so I'm inclined to believe libc is not
1321 // optimal here.
1322 var buf1: [32]u8 = undefined;
1323 const value: f64 = f64(@bitCast(f32, u32(1518338049)));
1324 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
1325 testing.expect(mem.eql(u8, result, "f64: 18014400656965630.00000\n"));
1326 }
1290 //
1291 // This is 0.015625 exactly according to gdb. We thus round down,
1292 // however glibc rounds up for some reason. This occurs for all
1293 // floats of the form x.yyyy25 on a precision point.
1294 try testFmt("f64: 0.01563", "f64: {d:.5}", f64(@bitCast(f32, u32(1015021568))));
1295 // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3
1296 // also rounds to 630 so I'm inclined to believe libc is not
1297 // optimal here.
1298 try testFmt("f64: 18014400656965630.00000", "f64: {d:.5}", f64(@bitCast(f32, u32(1518338049))));
13271299}
13281300
1329test "fmt.custom" {
1301test "custom" {
13301302 const Vec2 = struct {
13311303 const SelfType = @This();
13321304 x: f32,
......@@ -1335,20 +1307,17 @@ test "fmt.custom" {
13351307 pub fn format(
13361308 self: SelfType,
13371309 comptime fmt: []const u8,
1310 comptime options: FormatOptions,
13381311 context: var,
13391312 comptime Errors: type,
13401313 output: fn (@typeOf(context), []const u8) Errors!void,
13411314 ) Errors!void {
1342 switch (fmt.len) {
1343 0 => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y),
1344 1 => switch (fmt[0]) {
1345 //point format
1346 'p' => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y),
1347 //dimension format
1348 'd' => return std.fmt.format(context, Errors, output, "{.3}x{.3}", self.x, self.y),
1349 else => unreachable,
1350 },
1351 else => unreachable,
1315 if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "p")) {
1316 return std.fmt.format(context, Errors, output, "({d:.3},{d:.3})", self.x, self.y);
1317 } else if (comptime std.mem.eql(u8, fmt, "d")) {
1318 return std.fmt.format(context, Errors, output, "{d:.3}x{d:.3}", self.x, self.y);
1319 } else {
1320 @compileError("Unknown format character: '" ++ fmt ++ "'");
13521321 }
13531322 }
13541323 };
......@@ -1366,7 +1335,7 @@ test "fmt.custom" {
13661335 try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", value);
13671336}
13681337
1369test "fmt.struct" {
1338test "struct" {
13701339 const S = struct {
13711340 a: u32,
13721341 b: anyerror,
......@@ -1380,7 +1349,7 @@ test "fmt.struct" {
13801349 try testFmt("S{ .a = 456, .b = error.Unused }", "{}", inst);
13811350}
13821351
1383test "fmt.union" {
1352test "union" {
13841353 const TU = union(enum) {
13851354 float: f32,
13861355 int: u32,
......@@ -1410,7 +1379,7 @@ test "fmt.union" {
14101379 testing.expect(mem.eql(u8, uu_result[0..3], "EU@"));
14111380}
14121381
1413test "fmt.enum" {
1382test "enum" {
14141383 const E = enum {
14151384 One,
14161385 Two,
......@@ -1422,7 +1391,7 @@ test "fmt.enum" {
14221391 try testFmt("E.Two", "{}", inst);
14231392}
14241393
1425test "fmt.struct.self-referential" {
1394test "struct.self-referential" {
14261395 const S = struct {
14271396 const SelfType = @This();
14281397 a: ?*SelfType,
......@@ -1436,7 +1405,7 @@ test "fmt.struct.self-referential" {
14361405 try testFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", inst);
14371406}
14381407
1439test "fmt.bytes.hex" {
1408test "bytes.hex" {
14401409 const some_bytes = "\xCA\xFE\xBA\xBE";
14411410 try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", some_bytes);
14421411 try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", some_bytes);
......@@ -1478,7 +1447,7 @@ pub fn trim(buf: []const u8) []const u8 {
14781447 return buf[start..end];
14791448}
14801449
1481test "fmt.trim" {
1450test "trim" {
14821451 testing.expect(mem.eql(u8, "abc", trim("\n abc \t")));
14831452 testing.expect(mem.eql(u8, "", trim(" ")));
14841453 testing.expect(mem.eql(u8, "", trim("")));
......@@ -1505,22 +1474,22 @@ pub fn hexToBytes(out: []u8, input: []const u8) !void {
15051474 }
15061475}
15071476
1508test "fmt.hexToBytes" {
1477test "hexToBytes" {
15091478 const test_hex_str = "909A312BB12ED1F819B3521AC4C1E896F2160507FFC1C8381E3B07BB16BD1706";
15101479 var pb: [32]u8 = undefined;
15111480 try hexToBytes(pb[0..], test_hex_str);
15121481 try testFmt(test_hex_str, "{X}", pb);
15131482}
15141483
1515test "fmt.formatIntValue with comptime_int" {
1484test "formatIntValue with comptime_int" {
15161485 const value: comptime_int = 123456789123456789;
15171486
15181487 var buf = try std.Buffer.init(std.debug.global_allocator, "");
1519 try formatIntValue(value, "", &buf, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append);
1488 try formatIntValue(value, "", FormatOptions{}, &buf, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append);
15201489 assert(mem.eql(u8, buf.toSlice(), "123456789123456789"));
15211490}
15221491
1523test "fmt.formatType max_depth" {
1492test "formatType max_depth" {
15241493 const Vec2 = struct {
15251494 const SelfType = @This();
15261495 x: f32,
......@@ -1529,11 +1498,16 @@ test "fmt.formatType max_depth" {
15291498 pub fn format(
15301499 self: SelfType,
15311500 comptime fmt: []const u8,
1501 comptime options: FormatOptions,
15321502 context: var,
15331503 comptime Errors: type,
15341504 output: fn (@typeOf(context), []const u8) Errors!void,
15351505 ) Errors!void {
1536 return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y);
1506 if (fmt.len == 0) {
1507 return std.fmt.format(context, Errors, output, "({d:.3},{d:.3})", self.x, self.y);
1508 } else {
1509 @compileError("Unknown format string: '" ++ fmt ++ "'");
1510 }
15371511 }
15381512 };
15391513 const E = enum {
......@@ -1565,18 +1539,30 @@ test "fmt.formatType max_depth" {
15651539 inst.tu.ptr = &inst.tu;
15661540
15671541 var buf0 = try std.Buffer.init(std.debug.global_allocator, "");
1568 try formatType(inst, "", &buf0, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 0);
1542 try formatType(inst, "", FormatOptions{}, &buf0, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 0);
15691543 assert(mem.eql(u8, buf0.toSlice(), "S{ ... }"));
15701544
15711545 var buf1 = try std.Buffer.init(std.debug.global_allocator, "");
1572 try formatType(inst, "", &buf1, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 1);
1546 try formatType(inst, "", FormatOptions{}, &buf1, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 1);
15731547 assert(mem.eql(u8, buf1.toSlice(), "S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }"));
15741548
15751549 var buf2 = try std.Buffer.init(std.debug.global_allocator, "");
1576 try formatType(inst, "", &buf2, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 2);
1550 try formatType(inst, "", FormatOptions{}, &buf2, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 2);
15771551 assert(mem.eql(u8, buf2.toSlice(), "S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }"));
15781552
15791553 var buf3 = try std.Buffer.init(std.debug.global_allocator, "");
1580 try formatType(inst, "", &buf3, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 3);
1554 try formatType(inst, "", FormatOptions{}, &buf3, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 3);
15811555 assert(mem.eql(u8, buf3.toSlice(), "S{ .a = S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ .ptr = TU{ ... } } }, .e = E.Two, .vec = (10.200,2.220) }"));
15821556}
1557
1558test "positional" {
1559 try testFmt("2 1 0", "{2} {1} {0}", usize(0), usize(1), usize(2));
1560 try testFmt("2 1 0", "{2} {1} {}", usize(0), usize(1), usize(2));
1561 try testFmt("0 0", "{0} {0}", usize(0));
1562 try testFmt("0 1", "{} {1}", usize(0), usize(1));
1563 try testFmt("1 0 0 1", "{1} {} {0} {}", usize(0), usize(1));
1564}
1565
1566test "positional with specifier" {
1567 try testFmt("10.0", "{0d:.1}", f64(9.999));
1568}
std/math/big/int.zig+1
......@@ -519,6 +519,7 @@ pub const Int = struct {
519519 pub fn format(
520520 self: Int,
521521 comptime fmt: []const u8,
522 comptime options: std.fmt.FormatOptions,
522523 context: var,
523524 comptime FmtError: type,
524525 output: fn (@typeOf(context), []const u8) FmtError!void,
std/special/build_runner.zig+2-2
......@@ -170,7 +170,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void {
170170
171171 const allocator = builder.allocator;
172172 for (builder.top_level_steps.toSliceConst()) |top_level_step| {
173 try out_stream.print(" {s22} {}\n", top_level_step.step.name, top_level_step.description);
173 try out_stream.print(" {s:22} {}\n", top_level_step.step.name, top_level_step.description);
174174 }
175175
176176 try out_stream.write(
......@@ -191,7 +191,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void {
191191 for (builder.available_options_list.toSliceConst()) |option| {
192192 const name = try fmt.allocPrint(allocator, " -D{}=[{}]", option.name, Builder.typeIdName(option.type_id));
193193 defer allocator.free(name);
194 try out_stream.print("{s24} {}\n", name, option.description);
194 try out_stream.print("{s:24} {}\n", name, option.description);
195195 }
196196 }
197197
test/compare_output.zig+1-1
......@@ -122,7 +122,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
122122 \\
123123 \\pub fn main() void {
124124 \\ const stdout = &(io.getStdOut() catch unreachable).outStream().stream;
125 \\ stdout.print("Hello, world!\n{d4} {x3} {c}\n", u32(12), u16(0x12), u8('a')) catch unreachable;
125 \\ stdout.print("Hello, world!\n{d:4} {x:3} {c}\n", u32(12), u16(0x12), u8('a')) catch unreachable;
126126 \\}
127127 , "Hello, world!\n0012 012 a\n");
128128