authorgravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2019-06-25 20:15:33+12:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2019-06-25 20:15:33+12:00
logf5af349bd6ab644139ecf4bc0b0761a13a5e6458
tree7f6d4efc8806ceee94511776ebf4d63c125c47e7
parentde2b0cd722ca8fe98d16c86825db4cb2a70931c6
parent08e8d30dd642e42d8b8b16f43b487dbf42adb5ba
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #2714 from ziglang/fmt-overhaul

Add positional, precision and width support to std.fmt

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

src-self-hosted/dep_tokenizer.zig+1-1
......@@ -998,7 +998,7 @@ fn printCharValues(out: var, bytes: []const u8) !void {
998998
999999fn printUnderstandableChar(out: var, char: u8) !void {
10001000 if (!std.ascii.isPrint(char) or char == ' ') {
1001 std.fmt.format(out.context, anyerror, out.output, "\\x{X2}", char) catch {};
1001 std.fmt.format(out.context, anyerror, out.output, "\\x{X:2}", char) catch {};
10021002 } else {
10031003 try out.write("'");
10041004 try out.write([_]u8{printable_char_tab[char]});
std/fmt.zig+397-363
......@@ -10,6 +10,42 @@ const lossyCast = std.math.lossyCast;
1010
1111pub const default_max_depth = 3;
1212
13pub const Alignment = enum {
14 Left,
15 Center,
16 Right,
17};
18
19pub const FormatOptions = struct {
20 precision: ?usize = null,
21 width: ?usize = null,
22 alignment: ?Alignment = null,
23 fill: u8 = ' ',
24};
25
26fn nextArg(comptime used_pos_args: *u32, comptime maybe_pos_arg: ?comptime_int, comptime next_arg: *comptime_int) comptime_int {
27 if (maybe_pos_arg) |pos_arg| {
28 used_pos_args.* |= 1 << pos_arg;
29 return pos_arg;
30 } else {
31 const arg = next_arg.*;
32 next_arg.* += 1;
33 return arg;
34 }
35}
36
37fn peekIsAlign(comptime fmt: []const u8) bool {
38 // Should only be called during a state transition to the format segment.
39 std.debug.assert(fmt[0] == ':');
40
41 inline for (([_]u8{ 1, 2 })[0..]) |i| {
42 if (fmt.len > i and (fmt[i] == '<' or fmt[i] == '^' or fmt[i] == '>')) {
43 return true;
44 }
45 }
46 return false;
47}
48
1349/// Renders fmt string with args, calling output with slices of bytes.
1450/// If `output` returns an error, the error is returned from `format` and
1551/// `output` is not called again.
......@@ -20,17 +56,30 @@ pub fn format(
2056 comptime fmt: []const u8,
2157 args: ...,
2258) Errors!void {
59 const ArgSetType = @IntType(false, 32);
60 if (args.len > ArgSetType.bit_count) {
61 @compileError("32 arguments max are supported per format call");
62 }
63
2364 const State = enum {
2465 Start,
25 OpenBrace,
66 Positional,
2667 CloseBrace,
27 FormatString,
68 Specifier,
69 FormatFillAndAlign,
70 FormatWidth,
71 FormatPrecision,
2872 Pointer,
2973 };
3074
3175 comptime var start_index = 0;
3276 comptime var state = State.Start;
3377 comptime var next_arg = 0;
78 comptime var maybe_pos_arg: ?comptime_int = null;
79 comptime var used_pos_args: ArgSetType = 0;
80 comptime var specifier_start = 0;
81 comptime var specifier_end = 0;
82 comptime var options = FormatOptions{};
3483
3584 inline for (fmt) |c, i| {
3685 switch (state) {
......@@ -39,58 +88,183 @@ pub fn format(
3988 if (start_index < i) {
4089 try output(context, fmt[start_index..i]);
4190 }
91
4292 start_index = i;
43 state = State.OpenBrace;
93 specifier_start = i + 1;
94 specifier_end = i + 1;
95 maybe_pos_arg = null;
96 state = .Positional;
97 options = FormatOptions{};
4498 },
45
4699 '}' => {
47100 if (start_index < i) {
48101 try output(context, fmt[start_index..i]);
49102 }
50 state = State.CloseBrace;
103 state = .CloseBrace;
51104 },
52105 else => {},
53106 },
54 .OpenBrace => switch (c) {
107 .Positional => switch (c) {
55108 '{' => {
56 state = State.Start;
109 state = .Start;
57110 start_index = i;
58111 },
112 '*' => {
113 state = .Pointer;
114 },
115 ':' => {
116 state = if (comptime peekIsAlign(fmt[i..])) State.FormatFillAndAlign else State.FormatWidth;
117 specifier_end = i;
118 },
119 '0'...'9' => {
120 if (maybe_pos_arg == null) {
121 maybe_pos_arg = 0;
122 }
123
124 maybe_pos_arg.? *= 10;
125 maybe_pos_arg.? += c - '0';
126 specifier_start = i + 1;
127
128 if (maybe_pos_arg.? >= args.len) {
129 @compileError("Positional value refers to non-existent argument");
130 }
131 },
59132 '}' => {
60 try formatType(args[next_arg], fmt[0..0], context, Errors, output, default_max_depth);
61 next_arg += 1;
62 state = State.Start;
133 const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg);
134
135 try formatType(
136 args[arg_to_print],
137 fmt[0..0],
138 options,
139 context,
140 Errors,
141 output,
142 default_max_depth,
143 );
144
145 state = .Start;
63146 start_index = i + 1;
64147 },
65 '*' => state = State.Pointer,
66148 else => {
67 state = State.FormatString;
149 state = .Specifier;
150 specifier_start = i;
68151 },
69152 },
70153 .CloseBrace => switch (c) {
71154 '}' => {
72 state = State.Start;
155 state = .Start;
73156 start_index = i;
74157 },
75158 else => @compileError("Single '}' encountered in format string"),
76159 },
77 .FormatString => switch (c) {
160 .Specifier => switch (c) {
161 ':' => {
162 specifier_end = i;
163 state = if (comptime peekIsAlign(fmt[i..])) State.FormatFillAndAlign else State.FormatWidth;
164 },
78165 '}' => {
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;
166 const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg);
167
168 try formatType(
169 args[arg_to_print],
170 fmt[specifier_start..i],
171 options,
172 context,
173 Errors,
174 output,
175 default_max_depth,
176 );
177 state = .Start;
83178 start_index = i + 1;
84179 },
85180 else => {},
86181 },
182 // Only entered if the format string contains a fill/align segment.
183 .FormatFillAndAlign => switch (c) {
184 '<' => {
185 options.alignment = Alignment.Left;
186 state = .FormatWidth;
187 },
188 '^' => {
189 options.alignment = Alignment.Center;
190 state = .FormatWidth;
191 },
192 '>' => {
193 options.alignment = Alignment.Right;
194 state = .FormatWidth;
195 },
196 else => {
197 options.fill = c;
198 },
199 },
200 .FormatWidth => switch (c) {
201 '0'...'9' => {
202 if (options.width == null) {
203 options.width = 0;
204 }
205
206 options.width.? *= 10;
207 options.width.? += c - '0';
208 },
209 '.' => {
210 state = .FormatPrecision;
211 },
212 '}' => {
213 const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg);
214
215 try formatType(
216 args[arg_to_print],
217 fmt[specifier_start..specifier_end],
218 options,
219 context,
220 Errors,
221 output,
222 default_max_depth,
223 );
224 state = .Start;
225 start_index = i + 1;
226 },
227 else => {
228 @compileError("Unexpected character in width value: " ++ [_]u8{c});
229 },
230 },
231 .FormatPrecision => switch (c) {
232 '0'...'9' => {
233 if (options.precision == null) {
234 options.precision = 0;
235 }
236
237 options.precision.? *= 10;
238 options.precision.? += c - '0';
239 },
240 '}' => {
241 const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg);
242
243 try formatType(
244 args[arg_to_print],
245 fmt[specifier_start..specifier_end],
246 options,
247 context,
248 Errors,
249 output,
250 default_max_depth,
251 );
252 state = .Start;
253 start_index = i + 1;
254 },
255 else => {
256 @compileError("Unexpected character in precision value: " ++ [_]u8{c});
257 },
258 },
87259 .Pointer => switch (c) {
88260 '}' => {
89 try output(context, @typeName(@typeOf(args[next_arg]).Child));
261 const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg);
262
263 try output(context, @typeName(@typeOf(args[arg_to_print]).Child));
90264 try output(context, "@");
91 try formatInt(@ptrToInt(args[next_arg]), 16, false, 0, context, Errors, output);
92 next_arg += 1;
93 state = State.Start;
265 try formatInt(@ptrToInt(args[arg_to_print]), 16, false, 0, context, Errors, output);
266
267 state = .Start;
94268 start_index = i + 1;
95269 },
96270 else => @compileError("Unexpected format character after '*'"),
......@@ -98,7 +272,13 @@ pub fn format(
98272 }
99273 }
100274 comptime {
101 if (args.len != next_arg) {
275 // All arguments must have been printed but we allow mixing positional and fixed to achieve this.
276 var i: usize = 0;
277 inline while (i < next_arg) : (i += 1) {
278 used_pos_args |= 1 << i;
279 }
280
281 if (@popCount(ArgSetType, used_pos_args) != args.len) {
102282 @compileError("Unused arguments");
103283 }
104284 if (state != State.Start) {
......@@ -113,6 +293,7 @@ pub fn format(
113293pub fn formatType(
114294 value: var,
115295 comptime fmt: []const u8,
296 comptime options: FormatOptions,
116297 context: var,
117298 comptime Errors: type,
118299 output: fn (@typeOf(context), []const u8) Errors!void,
......@@ -121,7 +302,7 @@ pub fn formatType(
121302 const T = @typeOf(value);
122303 switch (@typeInfo(T)) {
123304 .ComptimeInt, .Int, .Float => {
124 return formatValue(value, fmt, context, Errors, output);
305 return formatValue(value, fmt, options, context, Errors, output);
125306 },
126307 .Void => {
127308 return output(context, "void");
......@@ -131,16 +312,16 @@ pub fn formatType(
131312 },
132313 .Optional => {
133314 if (value) |payload| {
134 return formatType(payload, fmt, context, Errors, output, max_depth);
315 return formatType(payload, fmt, options, context, Errors, output, max_depth);
135316 } else {
136317 return output(context, "null");
137318 }
138319 },
139320 .ErrorUnion => {
140321 if (value) |payload| {
141 return formatType(payload, fmt, context, Errors, output, max_depth);
322 return formatType(payload, fmt, options, context, Errors, output, max_depth);
142323 } else |err| {
143 return formatType(err, fmt, context, Errors, output, max_depth);
324 return formatType(err, fmt, options, context, Errors, output, max_depth);
144325 }
145326 },
146327 .ErrorSet => {
......@@ -152,16 +333,16 @@ pub fn formatType(
152333 },
153334 .Enum => {
154335 if (comptime std.meta.trait.hasFn("format")(T)) {
155 return value.format(fmt, context, Errors, output);
336 return value.format(fmt, options, context, Errors, output);
156337 }
157338
158339 try output(context, @typeName(T));
159340 try output(context, ".");
160 return formatType(@tagName(value), "", context, Errors, output, max_depth);
341 return formatType(@tagName(value), "", options, context, Errors, output, max_depth);
161342 },
162343 .Union => {
163344 if (comptime std.meta.trait.hasFn("format")(T)) {
164 return value.format(fmt, context, Errors, output);
345 return value.format(fmt, options, context, Errors, output);
165346 }
166347
167348 try output(context, @typeName(T));
......@@ -175,7 +356,7 @@ pub fn formatType(
175356 try output(context, " = ");
176357 inline for (info.fields) |u_field| {
177358 if (@enumToInt(UnionTagType(value)) == u_field.enum_field.?.value) {
178 try formatType(@field(value, u_field.name), "", context, Errors, output, max_depth - 1);
359 try formatType(@field(value, u_field.name), "", options, context, Errors, output, max_depth - 1);
179360 }
180361 }
181362 try output(context, " }");
......@@ -185,7 +366,7 @@ pub fn formatType(
185366 },
186367 .Struct => {
187368 if (comptime std.meta.trait.hasFn("format")(T)) {
188 return value.format(fmt, context, Errors, output);
369 return value.format(fmt, options, context, Errors, output);
189370 }
190371
191372 try output(context, @typeName(T));
......@@ -201,7 +382,7 @@ pub fn formatType(
201382 }
202383 try output(context, @memberName(T, field_i));
203384 try output(context, " = ");
204 try formatType(@field(value, @memberName(T, field_i)), "", context, Errors, output, max_depth - 1);
385 try formatType(@field(value, @memberName(T, field_i)), "", options, context, Errors, output, max_depth - 1);
205386 }
206387 try output(context, " }");
207388 },
......@@ -209,12 +390,12 @@ pub fn formatType(
209390 .One => switch (@typeInfo(ptr_info.child)) {
210391 builtin.TypeId.Array => |info| {
211392 if (info.child == u8) {
212 return formatText(value, fmt, context, Errors, output);
393 return formatText(value, fmt, options, context, Errors, output);
213394 }
214395 return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
215396 },
216397 builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => {
217 return formatType(value.*, fmt, context, Errors, output, max_depth);
398 return formatType(value.*, fmt, options, context, Errors, output, max_depth);
218399 },
219400 else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)),
220401 },
......@@ -222,17 +403,17 @@ pub fn formatType(
222403 if (ptr_info.child == u8) {
223404 if (fmt.len > 0 and fmt[0] == 's') {
224405 const len = mem.len(u8, value);
225 return formatText(value[0..len], fmt, context, Errors, output);
406 return formatText(value[0..len], fmt, options, context, Errors, output);
226407 }
227408 }
228409 return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
229410 },
230411 .Slice => {
231412 if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) {
232 return formatText(value, fmt, context, Errors, output);
413 return formatText(value, fmt, options, context, Errors, output);
233414 }
234415 if (ptr_info.child == u8) {
235 return formatText(value, fmt, context, Errors, output);
416 return formatText(value, fmt, options, context, Errors, output);
236417 }
237418 return format(context, Errors, output, "{}@{x}", @typeName(ptr_info.child), @ptrToInt(value.ptr));
238419 },
......@@ -242,7 +423,7 @@ pub fn formatType(
242423 },
243424 .Array => |info| {
244425 if (info.child == u8) {
245 return formatText(value, fmt, context, Errors, output);
426 return formatText(value, fmt, options, context, Errors, output);
246427 }
247428 return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(&value));
248429 },
......@@ -256,28 +437,25 @@ pub fn formatType(
256437fn formatValue(
257438 value: var,
258439 comptime fmt: []const u8,
440 comptime options: FormatOptions,
259441 context: var,
260442 comptime Errors: type,
261443 output: fn (@typeOf(context), []const u8) Errors!void,
262444) 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);
445 if (comptime std.mem.eql(u8, fmt, "B")) {
446 // TODO https://github.com/ziglang/zig/issues/2725
447 if (options.width) |w| return formatBytes(value, w, 1000, context, Errors, output);
448 return formatBytes(value, null, 1000, context, Errors, output);
449 } else if (comptime std.mem.eql(u8, fmt, "Bi")) {
450 // TODO https://github.com/ziglang/zig/issues/2725
451 if (options.width) |w| return formatBytes(value, w, 1024, context, Errors, output);
452 return formatBytes(value, null, 1024, context, Errors, output);
275453 }
276454
277455 const T = @typeOf(value);
278456 switch (@typeId(T)) {
279 .Float => return formatFloatValue(value, fmt, context, Errors, output),
280 .Int, .ComptimeInt => return formatIntValue(value, fmt, context, Errors, output),
457 .Float => return formatFloatValue(value, fmt, options, context, Errors, output),
458 .Int, .ComptimeInt => return formatIntValue(value, fmt, options, context, Errors, output),
281459 else => comptime unreachable,
282460 }
283461}
......@@ -285,13 +463,13 @@ fn formatValue(
285463pub fn formatIntValue(
286464 value: var,
287465 comptime fmt: []const u8,
466 comptime options: FormatOptions,
288467 context: var,
289468 comptime Errors: type,
290469 output: fn (@typeOf(context), []const u8) Errors!void,
291470) Errors!void {
292471 comptime var radix = 10;
293472 comptime var uppercase = false;
294 comptime var width = 0;
295473
296474 const int_value = if (@typeOf(value) == comptime_int) blk: {
297475 const Int = math.IntFittingRange(value, value);
......@@ -299,83 +477,75 @@ pub fn formatIntValue(
299477 } else
300478 value;
301479
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]}),
480 if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "d")) {
481 radix = 10;
482 uppercase = false;
483 } else if (comptime std.mem.eql(u8, fmt, "c")) {
484 if (@typeOf(int_value).bit_count <= 8) {
485 return formatAsciiChar(u8(int_value), context, Errors, output);
486 } else {
487 @compileError("Cannot print integer that is larger than 8 bits as a ascii");
332488 }
333 if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable);
489 } else if (comptime std.mem.eql(u8, fmt, "b")) {
490 radix = 2;
491 uppercase = false;
492 } else if (comptime std.mem.eql(u8, fmt, "x")) {
493 radix = 16;
494 uppercase = false;
495 } else if (comptime std.mem.eql(u8, fmt, "X")) {
496 radix = 16;
497 uppercase = true;
498 } else {
499 @compileError("Unknown format string: '" ++ fmt ++ "'");
334500 }
335 return formatInt(int_value, radix, uppercase, width, context, Errors, output);
501
502 // TODO https://github.com/ziglang/zig/issues/2725
503 if (options.width) |w| return formatInt(int_value, radix, uppercase, w, context, Errors, output);
504 return formatInt(int_value, radix, uppercase, 0, context, Errors, output);
336505}
337506
338507fn formatFloatValue(
339508 value: var,
340509 comptime fmt: []const u8,
510 comptime options: FormatOptions,
341511 context: var,
342512 comptime Errors: type,
343513 output: fn (@typeOf(context), []const u8) Errors!void,
344514) 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}),
515 if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "e")) {
516 // TODO https://github.com/ziglang/zig/issues/2725
517 if (options.precision) |p| return formatFloatScientific(value, p, context, Errors, output);
518 return formatFloatScientific(value, null, context, Errors, output);
519 } else if (comptime std.mem.eql(u8, fmt, "d")) {
520 // TODO https://github.com/ziglang/zig/issues/2725
521 if (options.precision) |p| return formatFloatDecimal(value, p, context, Errors, output);
522 return formatFloatDecimal(value, null, context, Errors, output);
523 } else {
524 @compileError("Unknown format string: '" ++ fmt ++ "'");
356525 }
357526}
358527
359528pub fn formatText(
360529 bytes: []const u8,
361530 comptime fmt: []const u8,
531 comptime options: FormatOptions,
362532 context: var,
363533 comptime Errors: type,
364534 output: fn (@typeOf(context), []const u8) Errors!void,
365535) 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]});
536 if (fmt.len == 0) {
537 return output(context, bytes);
538 } else if (comptime std.mem.eql(u8, fmt, "s")) {
539 if (options.width) |w| return formatBuf(bytes, w, context, Errors, output);
540 return formatBuf(bytes, 0, context, Errors, output);
541 } else if (comptime (std.mem.eql(u8, fmt, "x") or std.mem.eql(u8, fmt, "X"))) {
542 for (bytes) |c| {
543 try formatInt(c, 16, fmt[0] == 'X', 2, context, Errors, output);
544 }
545 return;
546 } else {
547 @compileError("Unknown format string: '" ++ fmt ++ "'");
377548 }
378 return output(context, bytes);
379549}
380550
381551pub fn formatAsciiChar(
......@@ -868,7 +1038,7 @@ test "parseUnsigned" {
8681038
8691039pub const parseFloat = @import("fmt/parse_float.zig").parseFloat;
8701040
871test "fmt.parseFloat" {
1041test "parseFloat" {
8721042 _ = @import("fmt/parse_float.zig");
8731043}
8741044
......@@ -960,7 +1130,7 @@ test "parse unsigned comptime" {
9601130 }
9611131}
9621132
963test "fmt.optional" {
1133test "optional" {
9641134 {
9651135 const value: ?i32 = 1234;
9661136 try testFmt("optional: 1234\n", "optional: {}\n", value);
......@@ -971,7 +1141,7 @@ test "fmt.optional" {
9711141 }
9721142}
9731143
974test "fmt.error" {
1144test "error" {
9751145 {
9761146 const value: anyerror!i32 = 1234;
9771147 try testFmt("error union: 1234\n", "error union: {}\n", value);
......@@ -982,14 +1152,14 @@ test "fmt.error" {
9821152 }
9831153}
9841154
985test "fmt.int.small" {
1155test "int.small" {
9861156 {
9871157 const value: u3 = 0b101;
9881158 try testFmt("u3: 5\n", "u3: {}\n", value);
9891159 }
9901160}
9911161
992test "fmt.int.specifier" {
1162test "int.specifier" {
9931163 {
9941164 const value: u8 = 'a';
9951165 try testFmt("u8: a\n", "u8: {c}\n", value);
......@@ -1000,27 +1170,31 @@ test "fmt.int.specifier" {
10001170 }
10011171}
10021172
1003test "fmt.buffer" {
1173test "int.padded" {
1174 try testFmt("u8: '0001'", "u8: '{:4}'", u8(1));
1175}
1176
1177test "buffer" {
10041178 {
10051179 var buf1: [32]u8 = undefined;
10061180 var context = BufPrintContext{ .remaining = buf1[0..] };
1007 try formatType(1234, "", &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth);
1181 try formatType(1234, "", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth);
10081182 var res = buf1[0 .. buf1.len - context.remaining.len];
10091183 testing.expect(mem.eql(u8, res, "1234"));
10101184
10111185 context = BufPrintContext{ .remaining = buf1[0..] };
1012 try formatType('a', "c", &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth);
1186 try formatType('a', "c", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth);
10131187 res = buf1[0 .. buf1.len - context.remaining.len];
10141188 testing.expect(mem.eql(u8, res, "a"));
10151189
10161190 context = BufPrintContext{ .remaining = buf1[0..] };
1017 try formatType(0b1100, "b", &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth);
1191 try formatType(0b1100, "b", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth);
10181192 res = buf1[0 .. buf1.len - context.remaining.len];
10191193 testing.expect(mem.eql(u8, res, "1100"));
10201194 }
10211195}
10221196
1023test "fmt.array" {
1197test "array" {
10241198 {
10251199 const value: [3]u8 = "abc";
10261200 try testFmt("array: abc\n", "array: {}\n", value);
......@@ -1035,7 +1209,7 @@ test "fmt.array" {
10351209 }
10361210}
10371211
1038test "fmt.slice" {
1212test "slice" {
10391213 {
10401214 const value: []const u8 = "abc";
10411215 try testFmt("slice: abc\n", "slice: {}\n", value);
......@@ -1045,11 +1219,11 @@ test "fmt.slice" {
10451219 try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", value);
10461220 }
10471221
1048 try testFmt("buf: Test \n", "buf: {s5}\n", "Test");
1222 try testFmt("buf: Test \n", "buf: {s:5}\n", "Test");
10491223 try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test");
10501224}
10511225
1052test "fmt.pointer" {
1226test "pointer" {
10531227 {
10541228 const value = @intToPtr(*i32, 0xdeadbeef);
10551229 try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", value);
......@@ -1065,17 +1239,17 @@ test "fmt.pointer" {
10651239 }
10661240}
10671241
1068test "fmt.cstr" {
1242test "cstr" {
10691243 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");
1244 try testFmt("cstr: Test C \n", "cstr: {s:10}\n", c"Test C");
10711245}
10721246
1073test "fmt.filesize" {
1247test "filesize" {
10741248 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));
1249 try testFmt("file size: 66.06MB\n", "file size: {B:2}\n", usize(63 * 1024 * 1024));
10761250}
10771251
1078test "fmt.struct" {
1252test "struct" {
10791253 {
10801254 const Struct = struct {
10811255 field: u8,
......@@ -1094,7 +1268,7 @@ test "fmt.struct" {
10941268 }
10951269}
10961270
1097test "fmt.enum" {
1271test "enum" {
10981272 const Enum = enum {
10991273 One,
11001274 Two,
......@@ -1104,229 +1278,71 @@ test "fmt.enum" {
11041278 try testFmt("enum: Enum.Two\n", "enum: {}\n", &value);
11051279}
11061280
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 }
1281test "float.scientific" {
1282 try testFmt("f32: 1.34000003e+00", "f32: {e}", f32(1.34));
1283 try testFmt("f32: 1.23400001e+01", "f32: {e}", f32(12.34));
1284 try testFmt("f64: -1.234e+11", "f64: {e}", f64(-12.34e10));
1285 try testFmt("f64: 9.99996e-40", "f64: {e}", f64(9.999960e-40));
11371286}
11381287
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 }
1288test "float.scientific.precision" {
1289 try testFmt("f64: 1.40971e-42", "f64: {e:.5}", f64(1.409706e-42));
1290 try testFmt("f64: 1.00000e-09", "f64: {e:.5}", f64(@bitCast(f32, u32(814313563))));
1291 try testFmt("f64: 7.81250e-03", "f64: {e:.5}", f64(@bitCast(f32, u32(1006632960))));
1292 // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05.
1293 // In fact, libc doesn't round a lot of 5 cases up when one past the precision point.
1294 try testFmt("f64: 1.00001e+05", "f64: {e:.5}", f64(@bitCast(f32, u32(1203982400))));
11661295}
11671296
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 }
1297test "float.special" {
1298 try testFmt("f64: nan", "f64: {}", math.nan_f64);
1299 // negative nan is not defined by IEE 754,
1300 // and ARM thus normalizes it to positive nan
11741301 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"));
1302 try testFmt("f64: -nan", "f64: {}", -math.nan_f64);
11901303 }
1304 try testFmt("f64: inf", "f64: {}", math.inf_f64);
1305 try testFmt("f64: -inf", "f64: {}", -math.inf_f64);
11911306}
11921307
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 }
1308test "float.decimal" {
1309 try testFmt("f64: 152314000000000000000000000000", "f64: {d}", f64(1.52314e+29));
1310 try testFmt("f32: 1.1", "f32: {d:.1}", f32(1.1234));
1311 try testFmt("f32: 1234.57", "f32: {d:.2}", f32(1234.567));
1312 // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64).
1313 // -11.12339... is rounded back up to -11.1234
1314 try testFmt("f32: -11.1234", "f32: {d:.4}", f32(-11.1234));
1315 try testFmt("f32: 91.12345", "f32: {d:.5}", f32(91.12345));
1316 try testFmt("f64: 91.1234567890", "f64: {d:.10}", f64(91.12345678901235));
1317 try testFmt("f64: 0.00000", "f64: {d:.5}", f64(0.0));
1318 try testFmt("f64: 6", "f64: {d:.0}", f64(5.700));
1319 try testFmt("f64: 10.0", "f64: {d:.1}", f64(9.999));
1320 try testFmt("f64: 1.000", "f64: {d:.3}", f64(1.0));
1321 try testFmt("f64: 0.00030000", "f64: {d:.8}", f64(0.0003));
1322 try testFmt("f64: 0.00000", "f64: {d:.5}", f64(1.40130e-45));
1323 try testFmt("f64: 0.00000", "f64: {d:.5}", f64(9.999960e-40));
12741324}
12751325
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 }
1326test "float.libc.sanity" {
1327 try testFmt("f64: 0.00001", "f64: {d:.5}", f64(@bitCast(f32, u32(916964781))));
1328 try testFmt("f64: 0.00001", "f64: {d:.5}", f64(@bitCast(f32, u32(925353389))));
1329 try testFmt("f64: 0.10000", "f64: {d:.5}", f64(@bitCast(f32, u32(1036831278))));
1330 try testFmt("f64: 1.00000", "f64: {d:.5}", f64(@bitCast(f32, u32(1065353133))));
1331 try testFmt("f64: 10.00000", "f64: {d:.5}", f64(@bitCast(f32, u32(1092616192))));
1332
13071333 // 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 }
1334 //
1335 // This is 0.015625 exactly according to gdb. We thus round down,
1336 // however glibc rounds up for some reason. This occurs for all
1337 // floats of the form x.yyyy25 on a precision point.
1338 try testFmt("f64: 0.01563", "f64: {d:.5}", f64(@bitCast(f32, u32(1015021568))));
1339 // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3
1340 // also rounds to 630 so I'm inclined to believe libc is not
1341 // optimal here.
1342 try testFmt("f64: 18014400656965630.00000", "f64: {d:.5}", f64(@bitCast(f32, u32(1518338049))));
13271343}
13281344
1329test "fmt.custom" {
1345test "custom" {
13301346 const Vec2 = struct {
13311347 const SelfType = @This();
13321348 x: f32,
......@@ -1335,20 +1351,17 @@ test "fmt.custom" {
13351351 pub fn format(
13361352 self: SelfType,
13371353 comptime fmt: []const u8,
1354 comptime options: FormatOptions,
13381355 context: var,
13391356 comptime Errors: type,
13401357 output: fn (@typeOf(context), []const u8) Errors!void,
13411358 ) 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,
1359 if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "p")) {
1360 return std.fmt.format(context, Errors, output, "({d:.3},{d:.3})", self.x, self.y);
1361 } else if (comptime std.mem.eql(u8, fmt, "d")) {
1362 return std.fmt.format(context, Errors, output, "{d:.3}x{d:.3}", self.x, self.y);
1363 } else {
1364 @compileError("Unknown format character: '" ++ fmt ++ "'");
13521365 }
13531366 }
13541367 };
......@@ -1366,7 +1379,7 @@ test "fmt.custom" {
13661379 try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", value);
13671380}
13681381
1369test "fmt.struct" {
1382test "struct" {
13701383 const S = struct {
13711384 a: u32,
13721385 b: anyerror,
......@@ -1380,7 +1393,7 @@ test "fmt.struct" {
13801393 try testFmt("S{ .a = 456, .b = error.Unused }", "{}", inst);
13811394}
13821395
1383test "fmt.union" {
1396test "union" {
13841397 const TU = union(enum) {
13851398 float: f32,
13861399 int: u32,
......@@ -1410,7 +1423,7 @@ test "fmt.union" {
14101423 testing.expect(mem.eql(u8, uu_result[0..3], "EU@"));
14111424}
14121425
1413test "fmt.enum" {
1426test "enum" {
14141427 const E = enum {
14151428 One,
14161429 Two,
......@@ -1422,7 +1435,7 @@ test "fmt.enum" {
14221435 try testFmt("E.Two", "{}", inst);
14231436}
14241437
1425test "fmt.struct.self-referential" {
1438test "struct.self-referential" {
14261439 const S = struct {
14271440 const SelfType = @This();
14281441 a: ?*SelfType,
......@@ -1436,7 +1449,7 @@ test "fmt.struct.self-referential" {
14361449 try testFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", inst);
14371450}
14381451
1439test "fmt.bytes.hex" {
1452test "bytes.hex" {
14401453 const some_bytes = "\xCA\xFE\xBA\xBE";
14411454 try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", some_bytes);
14421455 try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", some_bytes);
......@@ -1478,7 +1491,7 @@ pub fn trim(buf: []const u8) []const u8 {
14781491 return buf[start..end];
14791492}
14801493
1481test "fmt.trim" {
1494test "trim" {
14821495 testing.expect(mem.eql(u8, "abc", trim("\n abc \t")));
14831496 testing.expect(mem.eql(u8, "", trim(" ")));
14841497 testing.expect(mem.eql(u8, "", trim("")));
......@@ -1505,22 +1518,22 @@ pub fn hexToBytes(out: []u8, input: []const u8) !void {
15051518 }
15061519}
15071520
1508test "fmt.hexToBytes" {
1521test "hexToBytes" {
15091522 const test_hex_str = "909A312BB12ED1F819B3521AC4C1E896F2160507FFC1C8381E3B07BB16BD1706";
15101523 var pb: [32]u8 = undefined;
15111524 try hexToBytes(pb[0..], test_hex_str);
15121525 try testFmt(test_hex_str, "{X}", pb);
15131526}
15141527
1515test "fmt.formatIntValue with comptime_int" {
1528test "formatIntValue with comptime_int" {
15161529 const value: comptime_int = 123456789123456789;
15171530
15181531 var buf = try std.Buffer.init(std.debug.global_allocator, "");
1519 try formatIntValue(value, "", &buf, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append);
1532 try formatIntValue(value, "", FormatOptions{}, &buf, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append);
15201533 assert(mem.eql(u8, buf.toSlice(), "123456789123456789"));
15211534}
15221535
1523test "fmt.formatType max_depth" {
1536test "formatType max_depth" {
15241537 const Vec2 = struct {
15251538 const SelfType = @This();
15261539 x: f32,
......@@ -1529,11 +1542,16 @@ test "fmt.formatType max_depth" {
15291542 pub fn format(
15301543 self: SelfType,
15311544 comptime fmt: []const u8,
1545 comptime options: FormatOptions,
15321546 context: var,
15331547 comptime Errors: type,
15341548 output: fn (@typeOf(context), []const u8) Errors!void,
15351549 ) Errors!void {
1536 return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y);
1550 if (fmt.len == 0) {
1551 return std.fmt.format(context, Errors, output, "({d:.3},{d:.3})", self.x, self.y);
1552 } else {
1553 @compileError("Unknown format string: '" ++ fmt ++ "'");
1554 }
15371555 }
15381556 };
15391557 const E = enum {
......@@ -1565,18 +1583,34 @@ test "fmt.formatType max_depth" {
15651583 inst.tu.ptr = &inst.tu;
15661584
15671585 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);
1586 try formatType(inst, "", FormatOptions{}, &buf0, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 0);
15691587 assert(mem.eql(u8, buf0.toSlice(), "S{ ... }"));
15701588
15711589 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);
1590 try formatType(inst, "", FormatOptions{}, &buf1, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 1);
15731591 assert(mem.eql(u8, buf1.toSlice(), "S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }"));
15741592
15751593 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);
1594 try formatType(inst, "", FormatOptions{}, &buf2, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 2);
15771595 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) }"));
15781596
15791597 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);
1598 try formatType(inst, "", FormatOptions{}, &buf3, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 3);
15811599 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) }"));
15821600}
1601
1602test "positional" {
1603 try testFmt("2 1 0", "{2} {1} {0}", usize(0), usize(1), usize(2));
1604 try testFmt("2 1 0", "{2} {1} {}", usize(0), usize(1), usize(2));
1605 try testFmt("0 0", "{0} {0}", usize(0));
1606 try testFmt("0 1", "{} {1}", usize(0), usize(1));
1607 try testFmt("1 0 0 1", "{1} {} {0} {}", usize(0), usize(1));
1608}
1609
1610test "positional with specifier" {
1611 try testFmt("10.0", "{0d:.1}", f64(9.999));
1612}
1613
1614test "positional/alignment/width/precision" {
1615 try testFmt("10.0", "{0d: >3.1}", f64(9.999));
1616}
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
......@@ -167,7 +167,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void {
167167
168168 const allocator = builder.allocator;
169169 for (builder.top_level_steps.toSliceConst()) |top_level_step| {
170 try out_stream.print(" {s22} {}\n", top_level_step.step.name, top_level_step.description);
170 try out_stream.print(" {s:22} {}\n", top_level_step.step.name, top_level_step.description);
171171 }
172172
173173 try out_stream.write(
......@@ -188,7 +188,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void {
188188 for (builder.available_options_list.toSliceConst()) |option| {
189189 const name = try fmt.allocPrint(allocator, " -D{}=[{}]", option.name, Builder.typeIdName(option.type_id));
190190 defer allocator.free(name);
191 try out_stream.print("{s24} {}\n", name, option.description);
191 try out_stream.print("{s:24} {}\n", name, option.description);
192192 }
193193 }
194194
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