From 8fc52a94f46295ee821708c44a165803207e85a6 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Wed, 30 May 2018 10:18:11 -0500 Subject: Added custom formatter support, refactored fmt.format --- std/fmt/index.zig | 404 +++++++++++++++++++++++++++--------------------------- 1 file changed, 201 insertions(+), 203 deletions(-) (limited to 'std') diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 71ac764b0b..011b5d7c25 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; + else => { + state = State.FormatString; }, - '.' => { - 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 => @compileError("Unexpected character in format string: " ++ []u8{c}), }, State.CloseBrace => switch (c) { '}' => { @@ -120,139 +64,16 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), }, else => @compileError("Single '}' encountered in format string"), }, - State.Integer => switch (c) { + State.FormatString => switch(c) { '}' => { - 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' => { - width_start = i; - state = State.IntegerWidth; - }, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), - }, - State.IntegerWidth => switch (c) { - '}' => { - width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable); - try formatInt(args[next_arg], radix, uppercase, 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.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 => {}, + } } } comptime { @@ -268,14 +89,14 @@ 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.Int, builtin.TypeId.Float => { - return formatFloatScientific(value, null, context, Errors, output); + return formatValue(value, fmt, context, Errors, output); }, builtin.TypeId.Void => { return output(context, "void"); @@ -285,16 +106,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 +123,60 @@ 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") and def.is_pub) { + const data = def.data; + switch (data) { + builtin.TypeInfo.Definition.Data.Type, + builtin.TypeInfo.Definition.Data.Var => continue, + builtin.TypeInfo.Definition.Data.Fn => |*fn_def| { + //const FmtType = fn(@typeOf(context), []const u8)Errors!void; + //// for some reason, fn_type sees the arg `comptime []const u8` as `var` + //const TargetType = fn(T, var, var, type, FmtType) Errors!void; + + // This hack is because fn_def.fn_type != TargetType + // for reasons I have yet to determine. + + const fn_type_name = @typeName(@typeOf(value.format)); + const value_type_name = @typeName(@typeOf(value)); + const target_type_name = "(bound fn(" + ++ value_type_name ++ ",var,var,var,var)var)"; + if (mem.eql(u8, fn_type_name, target_type_name)) + { + 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 +188,106 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@ } } +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(fmt[0], 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; @@ -1048,6 +1014,38 @@ 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,}; + const point_result = try bufPrint(buf1[0..], "point: {}\n", &value); + assert(mem.eql(u8, point_result, "point: (10.200,2.220)\n")); + const dim_result = try bufPrint(buf1[0..], "dim: {d}\n", &value); + assert(mem.eql(u8, dim_result, "dim: 10.200x2.220\n")); + } } fn testFmt(expected: []const u8, comptime template: []const u8, args: ...) !void { -- cgit v1.2.3 From 4e1d0a59faea8ca9a9c28a876bc448f1d2f46a17 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Wed, 30 May 2018 10:24:27 -0500 Subject: Minor typo --- std/fmt/index.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'std') diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 011b5d7c25..eb52cead90 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -64,7 +64,7 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), }, else => @compileError("Single '}' encountered in format string"), }, - State.FormatString => switch(c) { + State.FormatString => switch (c) { '}' => { const s = start_index + 1; try formatType(args[next_arg], fmt[s..i], context, Errors, output); -- cgit v1.2.3 From 8938c16f38866a7149c32463dea44f884b265643 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Wed, 30 May 2018 10:41:48 -0500 Subject: Formatting --- std/fmt/index.zig | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'std') diff --git a/std/fmt/index.zig b/std/fmt/index.zig index eb52cead90..0eed6613a6 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -11,7 +11,10 @@ const max_int_digits = 65; /// Renders fmt string with args, calling output with slices of bytes. /// If `output` returns an error, the error is returned from `format` and /// `output` is not called again. -pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void, comptime fmt: []const u8, args: ...) Errors!void { +pub fn format(context: var, comptime Errors: type, + output: fn(@typeOf(context), []const u8) Errors!void, comptime fmt: []const u8, + args: ...) Errors!void +{ const State = enum { Start, OpenBrace, @@ -281,7 +284,9 @@ pub fn formatText(bytes: []const u8, comptime fmt: []const u8, context: var, return output(context, bytes); } -pub fn formatAsciiChar(c: u8, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void { +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]); } @@ -300,7 +305,9 @@ pub fn formatBuf(buf: []const u8, width: usize, context: var, // 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. @@ -389,7 +396,9 @@ 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. @@ -579,7 +588,9 @@ 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 = '-'; @@ -598,7 +609,9 @@ 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; -- cgit v1.2.3 From fb001f5e903ddf3774a673af0923841a57197a17 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Wed, 30 May 2018 12:18:24 -0500 Subject: Fixed character handling --- std/fmt/index.zig | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'std') diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 0eed6613a6..0f663c88bd 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -227,7 +227,7 @@ pub fn formatIntValue(value: var, comptime fmt: []const u8, context: var, compti 'c' => { if(@typeOf(value) == u8) { if(fmt.len > 1) @compileError("Unknown format character: " ++ []u8{fmt[1]}); - return formatAsciiChar(fmt[0], context, Errors, output); + return formatAsciiChar(value, context, Errors, output); } }, 'd' => { @@ -810,6 +810,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)); { @@ -1054,10 +1058,8 @@ test "fmt.format" { var buf1: [32]u8 = undefined; var value = Vec2{.x = 10.2, .y = 2.22,}; - const point_result = try bufPrint(buf1[0..], "point: {}\n", &value); - assert(mem.eql(u8, point_result, "point: (10.200,2.220)\n")); - const dim_result = try bufPrint(buf1[0..], "dim: {d}\n", &value); - assert(mem.eql(u8, dim_result, "dim: 10.200x2.220\n")); + try testFmt("point: (10.200,2.220)\n", "point: {}\n", &value); + try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", &value); } } -- cgit v1.2.3 From 940a8544486d24e69162b9ed1dcf1495ba0c4bd3 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Wed, 30 May 2018 13:38:41 -0500 Subject: Fix MacOS CI Timer test failing...? --- std/os/time.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'std') diff --git a/std/os/time.zig b/std/os/time.zig index 9a7c682483..6b062f3f45 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); -- cgit v1.2.3