aboutsummaryrefslogtreecommitdiff
path: root/std
diff options
context:
space:
mode:
authorAndrew Kelley <superjoe30@gmail.com>2018-06-04 11:07:47 -0400
committerAndrew Kelley <superjoe30@gmail.com>2018-06-04 11:07:47 -0400
commit8dfa66fee30fa6fa9a7e22780d71be2253eabb21 (patch)
treea9b00c4f3b41c73227d1c506fa2ef374af56b8ce /std
parent96164ce61377b36bcaf0c4087ca9b1ab822b9457 (diff)
parent11e7e03139596f16b1ee1b1d96b035ec9f94ab48 (diff)
downloadzig-8dfa66fee30fa6fa9a7e22780d71be2253eabb21.tar.gz
zig-8dfa66fee30fa6fa9a7e22780d71be2253eabb21.zip
Merge branch 'tgschultz-zig-custom-format'
Diffstat (limited to 'std')
-rw-r--r--std/fmt/index.zig450
-rw-r--r--std/os/time.zig2
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);