authorgravatar for tgschultz@gmail.comtgschultz <tgschultz@gmail.com> 2018-05-30 10:18:11-05:00
committergravatar for tgschultz@gmail.comtgschultz <tgschultz@gmail.com> 2018-05-30 10:18:11-05:00
log8fc52a94f46295ee821708c44a165803207e85a6
tree383d1652ef5dd4cf0d0508082a5e893e48bf6df8
parent8174f972a779384b287528e46ea086c714ce5553

Added custom formatter support, refactored fmt.format


1 files changed, 201 insertions(+), 203 deletions(-)

std/fmt/index.zig+201-203
......@@ -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;
56 else => {
57 state = State.FormatString;
9558 },
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;
113 },
114 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
11559 },
11660 State.CloseBrace => switch (c) {
11761 '}' => {
......@@ -120,139 +64,16 @@ 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) {
67 State.FormatString => switch(c) {
12468 '}' => {
125 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);
12671 next_arg += 1;
12772 state = State.Start;
12873 start_index = i + 1;
12974 },
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) {
137 '}' => {
138 width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable);
139 try formatInt(args[next_arg], radix, uppercase, width, context, Errors, output);
140 next_arg += 1;
141 state = State.Start;
142 start_index = i + 1;
143 },
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}),
255 },
75 else => {},
76 }
25677 }
25778 }
25879 comptime {
......@@ -268,14 +89,14 @@ 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(value: var, comptime fmt: []const u8, context: var, comptime Errors: type,
93 output: fn(@typeOf(context), []const u8)Errors!void) Errors!void
94{
27295 const T = @typeOf(value);
27396 switch (@typeId(T)) {
274 builtin.TypeId.Int => {
275 return formatInt(value, 10, false, 0, context, Errors, output);
276 },
97 builtin.TypeId.Int,
27798 builtin.TypeId.Float => {
278 return formatFloatScientific(value, null, context, Errors, output);
99 return formatValue(value, fmt, context, Errors, output);
279100 },
280101 builtin.TypeId.Void => {
281102 return output(context, "void");
......@@ -285,16 +106,16 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@
285106 },
286107 builtin.TypeId.Nullable => {
287108 if (value) |payload| {
288 return formatValue(payload, context, Errors, output);
109 return formatType(payload, fmt, context, Errors, output);
289110 } else {
290111 return output(context, "null");
291112 }
292113 },
293114 builtin.TypeId.ErrorUnion => {
294115 if (value) |payload| {
295 return formatValue(payload, context, Errors, output);
116 return formatType(payload, fmt, context, Errors, output);
296117 } else |err| {
297 return formatValue(err, context, Errors, output);
118 return formatType(err, fmt, context, Errors, output);
298119 }
299120 },
300121 builtin.TypeId.ErrorSet => {
......@@ -302,10 +123,60 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@
302123 return output(context, @errorName(value));
303124 },
304125 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));
126 switch(@typeId(T.Child)) {
127 builtin.TypeId.Array => {
128 if(T.Child.Child == u8) {
129 return formatText(value, fmt, context, Errors, output);
130 }
131 },
132 builtin.TypeId.Enum,
133 builtin.TypeId.Union,
134 builtin.TypeId.Struct => {
135 const has_cust_fmt = comptime cf: {
136 const info = @typeInfo(T.Child);
137 const defs = switch (info) {
138 builtin.TypeId.Struct => |s| s.defs,
139 builtin.TypeId.Union => |u| u.defs,
140 builtin.TypeId.Enum => |e| e.defs,
141 else => unreachable,
142 };
143
144 for (defs) |def| {
145 if (mem.eql(u8, def.name, "format") and def.is_pub) {
146 const data = def.data;
147 switch (data) {
148 builtin.TypeInfo.Definition.Data.Type,
149 builtin.TypeInfo.Definition.Data.Var => continue,
150 builtin.TypeInfo.Definition.Data.Fn => |*fn_def| {
151 //const FmtType = fn(@typeOf(context), []const u8)Errors!void;
152 //// for some reason, fn_type sees the arg `comptime []const u8` as `var`
153 //const TargetType = fn(T, var, var, type, FmtType) Errors!void;
154
155 // This hack is because fn_def.fn_type != TargetType
156 // for reasons I have yet to determine.
157
158 const fn_type_name = @typeName(@typeOf(value.format));
159 const value_type_name = @typeName(@typeOf(value));
160 const target_type_name = "(bound fn("
161 ++ value_type_name ++ ",var,var,var,var)var)";
162 if (mem.eql(u8, fn_type_name, target_type_name))
163 {
164 break :cf true;
165 }
166
167 },
168 }
169 }
170 }
171 break :cf false;
172 };
173
174 if (has_cust_fmt) return value.format(fmt, context, Errors, output);
175 return format(context, Errors, output, "{}@{x}", @typeName(T.Child),
176 @ptrToInt(value));
177 },
178 else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child),
179 @ptrToInt(value)),
309180 }
310181 },
311182 else => if (@canImplicitCast([]const u8, value)) {
......@@ -317,11 +188,106 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@
317188 }
318189}
319190
191fn formatValue(value: var, comptime fmt: []const u8, context: var, comptime Errors: type,
192 output: fn(@typeOf(context), []const u8)Errors!void) Errors!void
193{
194 if (fmt.len > 0) {
195 if (fmt[0] == 'B') {
196 comptime var width: ?usize = null;
197 if (fmt.len > 1) {
198 if (fmt[1] == 'i') {
199 if (fmt.len > 2) width = comptime (parseUnsigned(usize, fmt[2..], 10) catch unreachable);
200 return formatBytes(value, width, 1024, context, Errors, output);
201 }
202 width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable);
203 }
204 return formatBytes(value, width, 1000, context, Errors, output);
205 }
206 }
207
208 comptime var T = @typeOf(value);
209 switch (@typeId(T)) {
210 builtin.TypeId.Float => return formatFloatValue(value, fmt, context, Errors, output),
211 builtin.TypeId.Int => return formatIntValue(value, fmt, context, Errors, output),
212 else => unreachable,
213 }
214}
215
216pub fn formatIntValue(value: var, comptime fmt: []const u8, context: var, comptime Errors: type,
217 output: fn(@typeOf(context), []const u8)Errors!void) Errors!void
218{
219 comptime var radix = 10;
220 comptime var uppercase = false;
221 comptime var width = 0;
222 if (fmt.len > 0) {
223 switch (fmt[0]) {
224 'c' => {
225 if(@typeOf(value) == u8) {
226 if(fmt.len > 1) @compileError("Unknown format character: " ++ []u8{fmt[1]});
227 return formatAsciiChar(fmt[0], context, Errors, output);
228 }
229 },
230 'd' => {
231 radix = 10;
232 uppercase = false;
233 width = 0;
234 },
235 'x' => {
236 radix = 16;
237 uppercase = false;
238 width = 0;
239 },
240 'X' => {
241 radix = 16;
242 uppercase = true;
243 width = 0;
244 },
245 else => @compileError("Unknown format character: " ++ []u8{fmt[0]}),
246 }
247 if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable);
248 }
249 return formatInt(value, radix, uppercase, width, context, Errors, output);
250}
251
252fn formatFloatValue(value: var, comptime fmt: []const u8, context: var, comptime Errors: type,
253 output: fn(@typeOf(context), []const u8)Errors!void) Errors!void
254{
255 comptime var width: ?usize = null;
256 comptime var float_fmt = 'e';
257 if (fmt.len > 0) {
258 float_fmt = fmt[0];
259 if(fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable);
260 }
261
262 switch (float_fmt) {
263 'e' => try formatFloatScientific(value, width, context, Errors, output),
264 '.' => try formatFloatDecimal(value, width, context, Errors, output),
265 else => @compileError("Unknown format character: " ++ []u8{float_fmt}),
266 }
267
268}
269
270pub fn formatText(bytes: []const u8, comptime fmt: []const u8, context: var,
271 comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void
272{
273 if (fmt.len > 0) {
274 if (fmt[0] == 's') {
275 comptime var width = 0;
276 if(fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable);
277 return formatBuf(bytes, width, context, Errors, output);
278 }
279 else @compileError("Unknown format character: " ++ []u8{fmt[0]});
280 }
281 return output(context, bytes);
282}
283
320284pub fn formatAsciiChar(c: u8, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void {
321285 return output(context, (&c)[0..1]);
322286}
323287
324pub fn formatBuf(buf: []const u8, width: usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void {
288pub fn formatBuf(buf: []const u8, width: usize, context: var,
289 comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void
290{
325291 try output(context, buf);
326292
327293 var leftover_padding = if (width > buf.len) (width - buf.len) else return;
......@@ -1048,6 +1014,38 @@ test "fmt.format" {
10481014 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
10491015 assert(mem.eql(u8, result, "f64: 18014400656965630.00000\n"));
10501016 }
1017 //custom type format
1018 {
1019 const Vec2 = struct {
1020 const SelfType = this;
1021 x: f32,
1022 y: f32,
1023
1024 pub fn format(self: &SelfType, comptime fmt: []const u8, context: var,
1025 comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void)
1026 Errors!void
1027 {
1028 if (fmt.len > 0) {
1029 if (fmt.len > 1) unreachable;
1030 switch (fmt[0]) {
1031 //point format
1032 'p' => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y),
1033 //dimension format
1034 'd' => return std.fmt.format(context, Errors, output, "{.3}x{.3}", self.x, self.y),
1035 else => unreachable,
1036 }
1037 }
1038 return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y);
1039 }
1040 };
1041
1042 var buf1: [32]u8 = undefined;
1043 var value = Vec2{.x = 10.2, .y = 2.22,};
1044 const point_result = try bufPrint(buf1[0..], "point: {}\n", &value);
1045 assert(mem.eql(u8, point_result, "point: (10.200,2.220)\n"));
1046 const dim_result = try bufPrint(buf1[0..], "dim: {d}\n", &value);
1047 assert(mem.eql(u8, dim_result, "dim: 10.200x2.220\n"));
1048 }
10511049}
10521050
10531051fn testFmt(expected: []const u8, comptime template: []const u8, args: ...) !void {