authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-06-04 11:06:55-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-06-04 11:06:55-04:00
log11e7e03139596f16b1ee1b1d96b035ec9f94ab48
treea9b00c4f3b41c73227d1c506fa2ef374af56b8ce
parent96164ce61377b36bcaf0c4087ca9b1ab822b9457
parent940a8544486d24e69162b9ed1dcf1495ba0c4bd3

Merge branch 'zig-custom-format' of https://github.com/tgschultz/zig into tgschultz-zig-custom-format

I removed the code that checks for type signature and type. A function named `format` is enough for zig to give it a try.

2 files changed, 243 insertions(+), 209 deletions(-)

std/fmt/index.zig+242-208
......@@ -16,27 +16,12 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
1616 Start,
1717 OpenBrace,
1818 CloseBrace,
19 Integer,
20 IntegerWidth,
21 Float,
22 FloatWidth,
23 FloatScientific,
24 FloatScientificWidth,
25 Character,
26 Buf,
27 BufWidth,
28 Bytes,
29 BytesBase,
30 BytesWidth,
19 FormatString,
3120 };
3221
3322 comptime var start_index = 0;
3423 comptime var state = State.Start;
3524 comptime var next_arg = 0;
36 comptime var radix = 0;
37 comptime var uppercase = false;
38 comptime var width = 0;
39 comptime var width_start = 0;
4025
4126 inline for (fmt) |c, i| {
4227 switch (state) {
......@@ -45,8 +30,10 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
4530 if (start_index < i) {
4631 try output(context, fmt[start_index..i]);
4732 }
33 start_index = i;
4834 state = State.OpenBrace;
4935 },
36
5037 '}' => {
5138 if (start_index < i) {
5239 try output(context, fmt[start_index..i]);
......@@ -61,57 +48,14 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
6148 start_index = i;
6249 },
6350 '}' => {
64 try formatValue(args[next_arg], context, Errors, output);
51 try formatType(args[next_arg], fmt[0..0], context, Errors, output);
6552 next_arg += 1;
6653 state = State.Start;
6754 start_index = i + 1;
6855 },
69 'd' => {
70 radix = 10;
71 uppercase = false;
72 width = 0;
73 state = State.Integer;
74 },
75 'x' => {
76 radix = 16;
77 uppercase = false;
78 width = 0;
79 state = State.Integer;
80 },
81 'X' => {
82 radix = 16;
83 uppercase = true;
84 width = 0;
85 state = State.Integer;
86 },
87 'c' => {
88 state = State.Character;
89 },
90 's' => {
91 state = State.Buf;
92 },
93 'e' => {
94 state = State.FloatScientific;
95 },
96 '.' => {
97 state = State.Float;
98 },
99 'B' => {
100 width = 0;
101 radix = 1000;
102 state = State.Bytes;
103 },
104 else => @compileError("Unknown format character: " ++ []u8{c}),
105 },
106 State.Buf => switch (c) {
107 '}' => {
108 return output(context, args[next_arg]);
109 },
110 '0'...'9' => {
111 width_start = i;
112 state = State.BufWidth;
56 else => {
57 state = State.FormatString;
11358 },
114 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
11559 },
11660 State.CloseBrace => switch (c) {
11761 '}' => {
......@@ -120,138 +64,15 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
12064 },
12165 else => @compileError("Single '}' encountered in format string"),
12266 },
123 State.Integer => switch (c) {
124 '}' => {
125 try formatInt(args[next_arg], radix, uppercase, width, context, Errors, output);
126 next_arg += 1;
127 state = State.Start;
128 start_index = i + 1;
129 },
130 '0'...'9' => {
131 width_start = i;
132 state = State.IntegerWidth;
133 },
134 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
135 },
136 State.IntegerWidth => switch (c) {
67 State.FormatString => switch (c) {
13768 '}' => {
138 width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable);
139 try formatInt(args[next_arg], radix, uppercase, width, context, Errors, output);
69 const s = start_index + 1;
70 try formatType(args[next_arg], fmt[s..i], context, Errors, output);
14071 next_arg += 1;
14172 state = State.Start;
14273 start_index = i + 1;
14374 },
144 '0'...'9' => {},
145 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
146 },
147 State.FloatScientific => switch (c) {
148 '}' => {
149 try formatFloatScientific(args[next_arg], null, context, Errors, output);
150 next_arg += 1;
151 state = State.Start;
152 start_index = i + 1;
153 },
154 '0'...'9' => {
155 width_start = i;
156 state = State.FloatScientificWidth;
157 },
158 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
159 },
160 State.FloatScientificWidth => switch (c) {
161 '}' => {
162 width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable);
163 try formatFloatScientific(args[next_arg], width, context, Errors, output);
164 next_arg += 1;
165 state = State.Start;
166 start_index = i + 1;
167 },
168 '0'...'9' => {},
169 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
170 },
171 State.Float => switch (c) {
172 '}' => {
173 try formatFloatDecimal(args[next_arg], null, context, Errors, output);
174 next_arg += 1;
175 state = State.Start;
176 start_index = i + 1;
177 },
178 '0'...'9' => {
179 width_start = i;
180 state = State.FloatWidth;
181 },
182 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
183 },
184 State.FloatWidth => switch (c) {
185 '}' => {
186 width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable);
187 try formatFloatDecimal(args[next_arg], width, context, Errors, output);
188 next_arg += 1;
189 state = State.Start;
190 start_index = i + 1;
191 },
192 '0'...'9' => {},
193 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
194 },
195 State.BufWidth => switch (c) {
196 '}' => {
197 width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable);
198 try formatBuf(args[next_arg], width, context, Errors, output);
199 next_arg += 1;
200 state = State.Start;
201 start_index = i + 1;
202 },
203 '0'...'9' => {},
204 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
205 },
206 State.Character => switch (c) {
207 '}' => {
208 try formatAsciiChar(args[next_arg], context, Errors, output);
209 next_arg += 1;
210 state = State.Start;
211 start_index = i + 1;
212 },
213 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
214 },
215 State.Bytes => switch (c) {
216 '}' => {
217 try formatBytes(args[next_arg], 0, radix, context, Errors, output);
218 next_arg += 1;
219 state = State.Start;
220 start_index = i + 1;
221 },
222 'i' => {
223 radix = 1024;
224 state = State.BytesBase;
225 },
226 '0'...'9' => {
227 width_start = i;
228 state = State.BytesWidth;
229 },
230 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
231 },
232 State.BytesBase => switch (c) {
233 '}' => {
234 try formatBytes(args[next_arg], 0, radix, context, Errors, output);
235 next_arg += 1;
236 state = State.Start;
237 start_index = i + 1;
238 },
239 '0'...'9' => {
240 width_start = i;
241 state = State.BytesWidth;
242 },
243 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
244 },
245 State.BytesWidth => switch (c) {
246 '}' => {
247 width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable);
248 try formatBytes(args[next_arg], width, radix, context, Errors, output);
249 next_arg += 1;
250 state = State.Start;
251 start_index = i + 1;
252 },
253 '0'...'9' => {},
254 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
75 else => {},
25576 },
25677 }
25778 }
......@@ -268,14 +89,17 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
26889 }
26990}
27091
271pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void) Errors!void {
92pub fn formatType(
93 value: var,
94 comptime fmt: []const u8,
95 context: var,
96 comptime Errors: type,
97 output: fn (@typeOf(context), []const u8) Errors!void,
98) Errors!void {
27299 const T = @typeOf(value);
273100 switch (@typeId(T)) {
274 builtin.TypeId.Int => {
275 return formatInt(value, 10, false, 0, context, Errors, output);
276 },
277 builtin.TypeId.Float => {
278 return formatFloatScientific(value, null, context, Errors, output);
101 builtin.TypeId.Int, builtin.TypeId.Float => {
102 return formatValue(value, fmt, context, Errors, output);
279103 },
280104 builtin.TypeId.Void => {
281105 return output(context, "void");
......@@ -285,16 +109,16 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn (
285109 },
286110 builtin.TypeId.Nullable => {
287111 if (value) |payload| {
288 return formatValue(payload, context, Errors, output);
112 return formatType(payload, fmt, context, Errors, output);
289113 } else {
290114 return output(context, "null");
291115 }
292116 },
293117 builtin.TypeId.ErrorUnion => {
294118 if (value) |payload| {
295 return formatValue(payload, context, Errors, output);
119 return formatType(payload, fmt, context, Errors, output);
296120 } else |err| {
297 return formatValue(err, context, Errors, output);
121 return formatType(err, fmt, context, Errors, output);
298122 }
299123 },
300124 builtin.TypeId.ErrorSet => {
......@@ -302,10 +126,34 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn (
302126 return output(context, @errorName(value));
303127 },
304128 builtin.TypeId.Pointer => {
305 if (@typeId(T.Child) == builtin.TypeId.Array and T.Child.Child == u8) {
306 return output(context, (value.*)[0..]);
307 } else {
308 return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
129 switch (@typeId(T.Child)) {
130 builtin.TypeId.Array => {
131 if (T.Child.Child == u8) {
132 return formatText(value, fmt, context, Errors, output);
133 }
134 },
135 builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => {
136 const has_cust_fmt = comptime cf: {
137 const info = @typeInfo(T.Child);
138 const defs = switch (info) {
139 builtin.TypeId.Struct => |s| s.defs,
140 builtin.TypeId.Union => |u| u.defs,
141 builtin.TypeId.Enum => |e| e.defs,
142 else => unreachable,
143 };
144
145 for (defs) |def| {
146 if (mem.eql(u8, def.name, "format")) {
147 break :cf true;
148 }
149 }
150 break :cf false;
151 };
152
153 if (has_cust_fmt) return value.format(fmt, context, Errors, output);
154 return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
155 },
156 else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)),
309157 }
310158 },
311159 else => if (@canImplicitCast([]const u8, value)) {
......@@ -317,11 +165,129 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn (
317165 }
318166}
319167
320pub fn formatAsciiChar(c: u8, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void) Errors!void {
168fn formatValue(
169 value: var,
170 comptime fmt: []const u8,
171 context: var,
172 comptime Errors: type,
173 output: fn (@typeOf(context), []const u8) Errors!void,
174) Errors!void {
175 if (fmt.len > 0) {
176 if (fmt[0] == 'B') {
177 comptime var width: ?usize = null;
178 if (fmt.len > 1) {
179 if (fmt[1] == 'i') {
180 if (fmt.len > 2) width = comptime (parseUnsigned(usize, fmt[2..], 10) catch unreachable);
181 return formatBytes(value, width, 1024, context, Errors, output);
182 }
183 width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable);
184 }
185 return formatBytes(value, width, 1000, context, Errors, output);
186 }
187 }
188
189 comptime var T = @typeOf(value);
190 switch (@typeId(T)) {
191 builtin.TypeId.Float => return formatFloatValue(value, fmt, context, Errors, output),
192 builtin.TypeId.Int => return formatIntValue(value, fmt, context, Errors, output),
193 else => unreachable,
194 }
195}
196
197pub fn formatIntValue(
198 value: var,
199 comptime fmt: []const u8,
200 context: var,
201 comptime Errors: type,
202 output: fn (@typeOf(context), []const u8) Errors!void,
203) Errors!void {
204 comptime var radix = 10;
205 comptime var uppercase = false;
206 comptime var width = 0;
207 if (fmt.len > 0) {
208 switch (fmt[0]) {
209 'c' => {
210 if (@typeOf(value) == u8) {
211 if (fmt.len > 1) @compileError("Unknown format character: " ++ []u8{fmt[1]});
212 return formatAsciiChar(value, context, Errors, output);
213 }
214 },
215 'd' => {
216 radix = 10;
217 uppercase = false;
218 width = 0;
219 },
220 'x' => {
221 radix = 16;
222 uppercase = false;
223 width = 0;
224 },
225 'X' => {
226 radix = 16;
227 uppercase = true;
228 width = 0;
229 },
230 else => @compileError("Unknown format character: " ++ []u8{fmt[0]}),
231 }
232 if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable);
233 }
234 return formatInt(value, radix, uppercase, width, context, Errors, output);
235}
236
237fn formatFloatValue(
238 value: var,
239 comptime fmt: []const u8,
240 context: var,
241 comptime Errors: type,
242 output: fn (@typeOf(context), []const u8) Errors!void,
243) Errors!void {
244 comptime var width: ?usize = null;
245 comptime var float_fmt = 'e';
246 if (fmt.len > 0) {
247 float_fmt = fmt[0];
248 if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable);
249 }
250
251 switch (float_fmt) {
252 'e' => try formatFloatScientific(value, width, context, Errors, output),
253 '.' => try formatFloatDecimal(value, width, context, Errors, output),
254 else => @compileError("Unknown format character: " ++ []u8{float_fmt}),
255 }
256}
257
258pub fn formatText(
259 bytes: []const u8,
260 comptime fmt: []const u8,
261 context: var,
262 comptime Errors: type,
263 output: fn (@typeOf(context), []const u8) Errors!void,
264) Errors!void {
265 if (fmt.len > 0) {
266 if (fmt[0] == 's') {
267 comptime var width = 0;
268 if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable);
269 return formatBuf(bytes, width, context, Errors, output);
270 } else @compileError("Unknown format character: " ++ []u8{fmt[0]});
271 }
272 return output(context, bytes);
273}
274
275pub fn formatAsciiChar(
276 c: u8,
277 context: var,
278 comptime Errors: type,
279 output: fn (@typeOf(context), []const u8) Errors!void,
280) Errors!void {
321281 return output(context, (&c)[0..1]);
322282}
323283
324pub fn formatBuf(buf: []const u8, width: usize, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void) Errors!void {
284pub fn formatBuf(
285 buf: []const u8,
286 width: usize,
287 context: var,
288 comptime Errors: type,
289 output: fn (@typeOf(context), []const u8) Errors!void,
290) Errors!void {
325291 try output(context, buf);
326292
327293 var leftover_padding = if (width > buf.len) (width - buf.len) else return;
......@@ -334,7 +300,13 @@ pub fn formatBuf(buf: []const u8, width: usize, context: var, comptime Errors: t
334300// Print a float in scientific notation to the specified precision. Null uses full precision.
335301// It should be the case that every full precision, printed value can be re-parsed back to the
336302// same type unambiguously.
337pub fn formatFloatScientific(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void) Errors!void {
303pub fn formatFloatScientific(
304 value: var,
305 maybe_precision: ?usize,
306 context: var,
307 comptime Errors: type,
308 output: fn (@typeOf(context), []const u8) Errors!void,
309) Errors!void {
338310 var x = f64(value);
339311
340312 // Errol doesn't handle these special cases.
......@@ -423,7 +395,13 @@ pub fn formatFloatScientific(value: var, maybe_precision: ?usize, context: var,
423395
424396// Print a float of the format x.yyyyy where the number of y is specified by the precision argument.
425397// By default floats are printed at full precision (no rounding).
426pub fn formatFloatDecimal(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void) Errors!void {
398pub fn formatFloatDecimal(
399 value: var,
400 maybe_precision: ?usize,
401 context: var,
402 comptime Errors: type,
403 output: fn (@typeOf(context), []const u8) Errors!void,
404) Errors!void {
427405 var x = f64(value);
428406
429407 // Errol doesn't handle these special cases.
......@@ -613,7 +591,15 @@ pub fn formatInt(
613591 }
614592}
615593
616fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void) Errors!void {
594fn formatIntSigned(
595 value: var,
596 base: u8,
597 uppercase: bool,
598 width: usize,
599 context: var,
600 comptime Errors: type,
601 output: fn (@typeOf(context), []const u8) Errors!void,
602) Errors!void {
617603 const uint = @IntType(false, @typeOf(value).bit_count);
618604 if (value < 0) {
619605 const minus_sign: u8 = '-';
......@@ -632,7 +618,15 @@ fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize, context:
632618 }
633619}
634620
635fn formatIntUnsigned(value: var, base: u8, uppercase: bool, width: usize, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void) Errors!void {
621fn formatIntUnsigned(
622 value: var,
623 base: u8,
624 uppercase: bool,
625 width: usize,
626 context: var,
627 comptime Errors: type,
628 output: fn (@typeOf(context), []const u8) Errors!void,
629) Errors!void {
636630 // max_int_digits accounts for the minus sign. when printing an unsigned
637631 // number we don't need to do that.
638632 var buf: [max_int_digits - 1]u8 = undefined;
......@@ -831,6 +825,10 @@ test "fmt.format" {
831825 const value: u3 = 0b101;
832826 try testFmt("u3: 5\n", "u3: {}\n", value);
833827 }
828 {
829 const value: u8 = 'a';
830 try testFmt("u8: a\n", "u8: {c}\n", value);
831 }
834832 try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024));
835833 try testFmt("file size: 66.06MB\n", "file size: {B2}\n", usize(63 * 1024 * 1024));
836834 {
......@@ -1048,6 +1046,42 @@ test "fmt.format" {
10481046 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
10491047 assert(mem.eql(u8, result, "f64: 18014400656965630.00000\n"));
10501048 }
1049 //custom type format
1050 {
1051 const Vec2 = struct {
1052 const SelfType = this;
1053 x: f32,
1054 y: f32,
1055
1056 pub fn format(
1057 self: *SelfType,
1058 comptime fmt: []const u8,
1059 context: var,
1060 comptime Errors: type,
1061 output: fn (@typeOf(context), []const u8) Errors!void,
1062 ) Errors!void {
1063 if (fmt.len > 0) {
1064 if (fmt.len > 1) unreachable;
1065 switch (fmt[0]) {
1066 //point format
1067 'p' => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y),
1068 //dimension format
1069 'd' => return std.fmt.format(context, Errors, output, "{.3}x{.3}", self.x, self.y),
1070 else => unreachable,
1071 }
1072 }
1073 return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y);
1074 }
1075 };
1076
1077 var buf1: [32]u8 = undefined;
1078 var value = Vec2{
1079 .x = 10.2,
1080 .y = 2.22,
1081 };
1082 try testFmt("point: (10.200,2.220)\n", "point: {}\n", &value);
1083 try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", &value);
1084 }
10511085}
10521086
10531087fn testFmt(expected: []const u8, comptime template: []const u8, args: ...) !void {
std/os/time.zig+1-1
......@@ -266,7 +266,7 @@ test "os.time.timestamp" {
266266
267267test "os.time.Timer" {
268268 const ns_per_ms = (ns_per_s / ms_per_s);
269 const margin = ns_per_ms * 50;
269 const margin = ns_per_ms * 150;
270270
271271 var timer = try Timer.start();
272272 sleep(0, 10 * ns_per_ms);