diff options
| author | Andrew Kelley <superjoe30@gmail.com> | 2018-06-04 11:07:47 -0400 |
|---|---|---|
| committer | Andrew Kelley <superjoe30@gmail.com> | 2018-06-04 11:07:47 -0400 |
| commit | 8dfa66fee30fa6fa9a7e22780d71be2253eabb21 (patch) | |
| tree | a9b00c4f3b41c73227d1c506fa2ef374af56b8ce | |
| parent | 96164ce61377b36bcaf0c4087ca9b1ab822b9457 (diff) | |
| parent | 11e7e03139596f16b1ee1b1d96b035ec9f94ab48 (diff) | |
| download | zig-8dfa66fee30fa6fa9a7e22780d71be2253eabb21.tar.gz zig-8dfa66fee30fa6fa9a7e22780d71be2253eabb21.zip | |
Merge branch 'tgschultz-zig-custom-format'
| -rw-r--r-- | std/fmt/index.zig | 450 | ||||
| -rw-r--r-- | std/os/time.zig | 2 |
2 files changed, 243 insertions, 209 deletions
diff --git a/std/fmt/index.zig b/std/fmt/index.zig index b522d9d37d..21991e9ba3 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -16,27 +16,12 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), Start, OpenBrace, CloseBrace, - Integer, - IntegerWidth, - Float, - FloatWidth, - FloatScientific, - FloatScientificWidth, - Character, - Buf, - BufWidth, - Bytes, - BytesBase, - BytesWidth, + FormatString, }; comptime var start_index = 0; comptime var state = State.Start; comptime var next_arg = 0; - comptime var radix = 0; - comptime var uppercase = false; - comptime var width = 0; - comptime var width_start = 0; inline for (fmt) |c, i| { switch (state) { @@ -45,8 +30,10 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), if (start_index < i) { try output(context, fmt[start_index..i]); } + start_index = i; state = State.OpenBrace; }, + '}' => { if (start_index < i) { try output(context, fmt[start_index..i]); @@ -61,57 +48,14 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), start_index = i; }, '}' => { - try formatValue(args[next_arg], context, Errors, output); + try formatType(args[next_arg], fmt[0..0], context, Errors, output); next_arg += 1; state = State.Start; start_index = i + 1; }, - 'd' => { - radix = 10; - uppercase = false; - width = 0; - state = State.Integer; - }, - 'x' => { - radix = 16; - uppercase = false; - width = 0; - state = State.Integer; - }, - 'X' => { - radix = 16; - uppercase = true; - width = 0; - state = State.Integer; - }, - 'c' => { - state = State.Character; - }, - 's' => { - state = State.Buf; - }, - 'e' => { - state = State.FloatScientific; - }, - '.' => { - state = State.Float; - }, - 'B' => { - width = 0; - radix = 1000; - state = State.Bytes; - }, - else => @compileError("Unknown format character: " ++ []u8{c}), - }, - State.Buf => switch (c) { - '}' => { - return output(context, args[next_arg]); - }, - '0'...'9' => { - width_start = i; - state = State.BufWidth; + else => { + state = State.FormatString; }, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), }, State.CloseBrace => switch (c) { '}' => { @@ -120,138 +64,15 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), }, else => @compileError("Single '}' encountered in format string"), }, - State.Integer => switch (c) { - '}' => { - try formatInt(args[next_arg], radix, uppercase, width, context, Errors, output); - next_arg += 1; - state = State.Start; - start_index = i + 1; - }, - '0'...'9' => { - width_start = i; - state = State.IntegerWidth; - }, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), - }, - State.IntegerWidth => switch (c) { + State.FormatString => switch (c) { '}' => { - width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable); - try formatInt(args[next_arg], radix, uppercase, width, context, Errors, output); + const s = start_index + 1; + try formatType(args[next_arg], fmt[s..i], context, Errors, output); next_arg += 1; state = State.Start; start_index = i + 1; }, - '0'...'9' => {}, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), - }, - State.FloatScientific => switch (c) { - '}' => { - try formatFloatScientific(args[next_arg], null, context, Errors, output); - next_arg += 1; - state = State.Start; - start_index = i + 1; - }, - '0'...'9' => { - width_start = i; - state = State.FloatScientificWidth; - }, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), - }, - State.FloatScientificWidth => switch (c) { - '}' => { - width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable); - try formatFloatScientific(args[next_arg], width, context, Errors, output); - next_arg += 1; - state = State.Start; - start_index = i + 1; - }, - '0'...'9' => {}, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), - }, - State.Float => switch (c) { - '}' => { - try formatFloatDecimal(args[next_arg], null, context, Errors, output); - next_arg += 1; - state = State.Start; - start_index = i + 1; - }, - '0'...'9' => { - width_start = i; - state = State.FloatWidth; - }, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), - }, - State.FloatWidth => switch (c) { - '}' => { - width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable); - try formatFloatDecimal(args[next_arg], width, context, Errors, output); - next_arg += 1; - state = State.Start; - start_index = i + 1; - }, - '0'...'9' => {}, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), - }, - State.BufWidth => switch (c) { - '}' => { - width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable); - try formatBuf(args[next_arg], width, context, Errors, output); - next_arg += 1; - state = State.Start; - start_index = i + 1; - }, - '0'...'9' => {}, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), - }, - State.Character => switch (c) { - '}' => { - try formatAsciiChar(args[next_arg], context, Errors, output); - next_arg += 1; - state = State.Start; - start_index = i + 1; - }, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), - }, - State.Bytes => switch (c) { - '}' => { - try formatBytes(args[next_arg], 0, radix, context, Errors, output); - next_arg += 1; - state = State.Start; - start_index = i + 1; - }, - 'i' => { - radix = 1024; - state = State.BytesBase; - }, - '0'...'9' => { - width_start = i; - state = State.BytesWidth; - }, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), - }, - State.BytesBase => switch (c) { - '}' => { - try formatBytes(args[next_arg], 0, radix, context, Errors, output); - next_arg += 1; - state = State.Start; - start_index = i + 1; - }, - '0'...'9' => { - width_start = i; - state = State.BytesWidth; - }, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), - }, - State.BytesWidth => switch (c) { - '}' => { - width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable); - try formatBytes(args[next_arg], width, radix, context, Errors, output); - next_arg += 1; - state = State.Start; - start_index = i + 1; - }, - '0'...'9' => {}, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), + else => {}, }, } } @@ -268,14 +89,17 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), } } -pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void) Errors!void { +pub fn formatType( + value: var, + comptime fmt: []const u8, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, +) Errors!void { const T = @typeOf(value); switch (@typeId(T)) { - builtin.TypeId.Int => { - return formatInt(value, 10, false, 0, context, Errors, output); - }, - builtin.TypeId.Float => { - return formatFloatScientific(value, null, context, Errors, output); + builtin.TypeId.Int, builtin.TypeId.Float => { + return formatValue(value, fmt, context, Errors, output); }, builtin.TypeId.Void => { return output(context, "void"); @@ -285,16 +109,16 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn ( }, builtin.TypeId.Nullable => { if (value) |payload| { - return formatValue(payload, context, Errors, output); + return formatType(payload, fmt, context, Errors, output); } else { return output(context, "null"); } }, builtin.TypeId.ErrorUnion => { if (value) |payload| { - return formatValue(payload, context, Errors, output); + return formatType(payload, fmt, context, Errors, output); } else |err| { - return formatValue(err, context, Errors, output); + return formatType(err, fmt, context, Errors, output); } }, builtin.TypeId.ErrorSet => { @@ -302,10 +126,34 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn ( return output(context, @errorName(value)); }, builtin.TypeId.Pointer => { - if (@typeId(T.Child) == builtin.TypeId.Array and T.Child.Child == u8) { - return output(context, (value.*)[0..]); - } else { - return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); + switch (@typeId(T.Child)) { + builtin.TypeId.Array => { + if (T.Child.Child == u8) { + return formatText(value, fmt, context, Errors, output); + } + }, + builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => { + const has_cust_fmt = comptime cf: { + const info = @typeInfo(T.Child); + const defs = switch (info) { + builtin.TypeId.Struct => |s| s.defs, + builtin.TypeId.Union => |u| u.defs, + builtin.TypeId.Enum => |e| e.defs, + else => unreachable, + }; + + for (defs) |def| { + if (mem.eql(u8, def.name, "format")) { + break :cf true; + } + } + break :cf false; + }; + + if (has_cust_fmt) return value.format(fmt, context, Errors, output); + return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); + }, + else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)), } }, else => if (@canImplicitCast([]const u8, value)) { @@ -317,11 +165,129 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn ( } } -pub fn formatAsciiChar(c: u8, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void) Errors!void { +fn formatValue( + value: var, + comptime fmt: []const u8, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, +) Errors!void { + if (fmt.len > 0) { + if (fmt[0] == 'B') { + comptime var width: ?usize = null; + if (fmt.len > 1) { + if (fmt[1] == 'i') { + if (fmt.len > 2) width = comptime (parseUnsigned(usize, fmt[2..], 10) catch unreachable); + return formatBytes(value, width, 1024, context, Errors, output); + } + width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); + } + return formatBytes(value, width, 1000, context, Errors, output); + } + } + + comptime var T = @typeOf(value); + switch (@typeId(T)) { + builtin.TypeId.Float => return formatFloatValue(value, fmt, context, Errors, output), + builtin.TypeId.Int => return formatIntValue(value, fmt, context, Errors, output), + else => unreachable, + } +} + +pub fn formatIntValue( + value: var, + comptime fmt: []const u8, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, +) Errors!void { + comptime var radix = 10; + comptime var uppercase = false; + comptime var width = 0; + if (fmt.len > 0) { + switch (fmt[0]) { + 'c' => { + if (@typeOf(value) == u8) { + if (fmt.len > 1) @compileError("Unknown format character: " ++ []u8{fmt[1]}); + return formatAsciiChar(value, context, Errors, output); + } + }, + 'd' => { + radix = 10; + uppercase = false; + width = 0; + }, + 'x' => { + radix = 16; + uppercase = false; + width = 0; + }, + 'X' => { + radix = 16; + uppercase = true; + width = 0; + }, + else => @compileError("Unknown format character: " ++ []u8{fmt[0]}), + } + if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); + } + return formatInt(value, radix, uppercase, width, context, Errors, output); +} + +fn formatFloatValue( + value: var, + comptime fmt: []const u8, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, +) Errors!void { + comptime var width: ?usize = null; + comptime var float_fmt = 'e'; + if (fmt.len > 0) { + float_fmt = fmt[0]; + if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); + } + + switch (float_fmt) { + 'e' => try formatFloatScientific(value, width, context, Errors, output), + '.' => try formatFloatDecimal(value, width, context, Errors, output), + else => @compileError("Unknown format character: " ++ []u8{float_fmt}), + } +} + +pub fn formatText( + bytes: []const u8, + comptime fmt: []const u8, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, +) Errors!void { + if (fmt.len > 0) { + if (fmt[0] == 's') { + comptime var width = 0; + if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); + return formatBuf(bytes, width, context, Errors, output); + } else @compileError("Unknown format character: " ++ []u8{fmt[0]}); + } + return output(context, bytes); +} + +pub fn formatAsciiChar( + c: u8, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, +) Errors!void { return output(context, (&c)[0..1]); } -pub fn formatBuf(buf: []const u8, width: usize, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void) Errors!void { +pub fn formatBuf( + buf: []const u8, + width: usize, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, +) Errors!void { try output(context, buf); 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 // Print a float in scientific notation to the specified precision. Null uses full precision. // It should be the case that every full precision, printed value can be re-parsed back to the // same type unambiguously. -pub fn formatFloatScientific(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void) Errors!void { +pub fn formatFloatScientific( + value: var, + maybe_precision: ?usize, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, +) Errors!void { var x = f64(value); // Errol doesn't handle these special cases. @@ -423,7 +395,13 @@ pub fn formatFloatScientific(value: var, maybe_precision: ?usize, context: var, // Print a float of the format x.yyyyy where the number of y is specified by the precision argument. // By default floats are printed at full precision (no rounding). -pub fn formatFloatDecimal(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void) Errors!void { +pub fn formatFloatDecimal( + value: var, + maybe_precision: ?usize, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, +) Errors!void { var x = f64(value); // Errol doesn't handle these special cases. @@ -613,7 +591,15 @@ pub fn formatInt( } } -fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void) Errors!void { +fn formatIntSigned( + value: var, + base: u8, + uppercase: bool, + width: usize, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, +) Errors!void { const uint = @IntType(false, @typeOf(value).bit_count); if (value < 0) { const minus_sign: u8 = '-'; @@ -632,7 +618,15 @@ fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize, context: } } -fn formatIntUnsigned(value: var, base: u8, uppercase: bool, width: usize, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void) Errors!void { +fn formatIntUnsigned( + value: var, + base: u8, + uppercase: bool, + width: usize, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, +) Errors!void { // max_int_digits accounts for the minus sign. when printing an unsigned // number we don't need to do that. var buf: [max_int_digits - 1]u8 = undefined; @@ -831,6 +825,10 @@ test "fmt.format" { const value: u3 = 0b101; try testFmt("u3: 5\n", "u3: {}\n", value); } + { + const value: u8 = 'a'; + try testFmt("u8: a\n", "u8: {c}\n", value); + } try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024)); try testFmt("file size: 66.06MB\n", "file size: {B2}\n", usize(63 * 1024 * 1024)); { @@ -1048,6 +1046,42 @@ test "fmt.format" { const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); assert(mem.eql(u8, result, "f64: 18014400656965630.00000\n")); } + //custom type format + { + const Vec2 = struct { + const SelfType = this; + x: f32, + y: f32, + + pub fn format( + self: *SelfType, + comptime fmt: []const u8, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, + ) Errors!void { + if (fmt.len > 0) { + if (fmt.len > 1) unreachable; + switch (fmt[0]) { + //point format + 'p' => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y), + //dimension format + 'd' => return std.fmt.format(context, Errors, output, "{.3}x{.3}", self.x, self.y), + else => unreachable, + } + } + return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y); + } + }; + + var buf1: [32]u8 = undefined; + var value = Vec2{ + .x = 10.2, + .y = 2.22, + }; + try testFmt("point: (10.200,2.220)\n", "point: {}\n", &value); + try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", &value); + } } fn testFmt(expected: []const u8, comptime template: []const u8, args: ...) !void { diff --git a/std/os/time.zig b/std/os/time.zig index 8629504323..dd64df2156 100644 --- a/std/os/time.zig +++ b/std/os/time.zig @@ -266,7 +266,7 @@ test "os.time.timestamp" { test "os.time.Timer" { const ns_per_ms = (ns_per_s / ms_per_s); - const margin = ns_per_ms * 50; + const margin = ns_per_ms * 150; var timer = try Timer.start(); sleep(0, 10 * ns_per_ms); |
