From 08e8d30dd642e42d8b8b16f43b487dbf42adb5ba Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Tue, 25 Jun 2019 17:11:04 +1200 Subject: [PATCH] Add parsing of fill and alignment in std.format These options are now available to use when printing, however nothing currently makes use of these. --- std/fmt.zig | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/std/fmt.zig b/std/fmt.zig index 038efbfb7f..7ff6ea8081 100644 --- a/std/fmt.zig +++ b/std/fmt.zig @@ -10,9 +10,17 @@ const lossyCast = std.math.lossyCast; pub const default_max_depth = 3; +pub const Alignment = enum { + Left, + Center, + Right, +}; + pub const FormatOptions = struct { precision: ?usize = null, width: ?usize = null, + alignment: ?Alignment = null, + fill: u8 = ' ', }; fn nextArg(comptime used_pos_args: *u32, comptime maybe_pos_arg: ?comptime_int, comptime next_arg: *comptime_int) comptime_int { @@ -26,6 +34,18 @@ fn nextArg(comptime used_pos_args: *u32, comptime maybe_pos_arg: ?comptime_int, } } +fn peekIsAlign(comptime fmt: []const u8) bool { + // Should only be called during a state transition to the format segment. + std.debug.assert(fmt[0] == ':'); + + inline for (([_]u8{ 1, 2 })[0..]) |i| { + if (fmt.len > i and (fmt[i] == '<' or fmt[i] == '^' or fmt[i] == '>')) { + return true; + } + } + return false; +} + /// 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. @@ -46,6 +66,7 @@ pub fn format( Positional, CloseBrace, Specifier, + FormatFillAndAlign, FormatWidth, FormatPrecision, Pointer, @@ -92,7 +113,7 @@ pub fn format( state = .Pointer; }, ':' => { - state = .FormatWidth; + state = if (comptime peekIsAlign(fmt[i..])) State.FormatFillAndAlign else State.FormatWidth; specifier_end = i; }, '0'...'9' => { @@ -139,7 +160,7 @@ pub fn format( .Specifier => switch (c) { ':' => { specifier_end = i; - state = .FormatWidth; + state = if (comptime peekIsAlign(fmt[i..])) State.FormatFillAndAlign else State.FormatWidth; }, '}' => { const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg); @@ -158,6 +179,24 @@ pub fn format( }, else => {}, }, + // Only entered if the format string contains a fill/align segment. + .FormatFillAndAlign => switch (c) { + '<' => { + options.alignment = Alignment.Left; + state = .FormatWidth; + }, + '^' => { + options.alignment = Alignment.Center; + state = .FormatWidth; + }, + '>' => { + options.alignment = Alignment.Right; + state = .FormatWidth; + }, + else => { + options.fill = c; + }, + }, .FormatWidth => switch (c) { '0'...'9' => { if (options.width == null) { @@ -1571,3 +1610,7 @@ test "positional" { test "positional with specifier" { try testFmt("10.0", "{0d:.1}", f64(9.999)); } + +test "positional/alignment/width/precision" { + try testFmt("10.0", "{0d: >3.1}", f64(9.999)); +}