authorgravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2019-06-25 17:11:04+12:00
committergravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2019-06-25 17:11:18+12:00
log08e8d30dd642e42d8b8b16f43b487dbf42adb5ba
treea024bb348559c788de481975b17a378930772ecd
parent948dc7b304f966d8402f63261a8b8dd43691647a

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.

1 files changed, 45 insertions(+), 2 deletions(-)

std/fmt.zig+45-2
......@@ -10,9 +10,17 @@ const lossyCast = std.math.lossyCast;
1010
1111pub const default_max_depth = 3;
1212
13pub const Alignment = enum {
14 Left,
15 Center,
16 Right,
17};
18
1319pub const FormatOptions = struct {
1420 precision: ?usize = null,
1521 width: ?usize = null,
22 alignment: ?Alignment = null,
23 fill: u8 = ' ',
1624};
1725
1826fn 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,
2634 }
2735}
2836
37fn peekIsAlign(comptime fmt: []const u8) bool {
38 // Should only be called during a state transition to the format segment.
39 std.debug.assert(fmt[0] == ':');
40
41 inline for (([_]u8{ 1, 2 })[0..]) |i| {
42 if (fmt.len > i and (fmt[i] == '<' or fmt[i] == '^' or fmt[i] == '>')) {
43 return true;
44 }
45 }
46 return false;
47}
48
2949/// Renders fmt string with args, calling output with slices of bytes.
3050/// If `output` returns an error, the error is returned from `format` and
3151/// `output` is not called again.
......@@ -46,6 +66,7 @@ pub fn format(
4666 Positional,
4767 CloseBrace,
4868 Specifier,
69 FormatFillAndAlign,
4970 FormatWidth,
5071 FormatPrecision,
5172 Pointer,
......@@ -92,7 +113,7 @@ pub fn format(
92113 state = .Pointer;
93114 },
94115 ':' => {
95 state = .FormatWidth;
116 state = if (comptime peekIsAlign(fmt[i..])) State.FormatFillAndAlign else State.FormatWidth;
96117 specifier_end = i;
97118 },
98119 '0'...'9' => {
......@@ -139,7 +160,7 @@ pub fn format(
139160 .Specifier => switch (c) {
140161 ':' => {
141162 specifier_end = i;
142 state = .FormatWidth;
163 state = if (comptime peekIsAlign(fmt[i..])) State.FormatFillAndAlign else State.FormatWidth;
143164 },
144165 '}' => {
145166 const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg);
......@@ -158,6 +179,24 @@ pub fn format(
158179 },
159180 else => {},
160181 },
182 // Only entered if the format string contains a fill/align segment.
183 .FormatFillAndAlign => switch (c) {
184 '<' => {
185 options.alignment = Alignment.Left;
186 state = .FormatWidth;
187 },
188 '^' => {
189 options.alignment = Alignment.Center;
190 state = .FormatWidth;
191 },
192 '>' => {
193 options.alignment = Alignment.Right;
194 state = .FormatWidth;
195 },
196 else => {
197 options.fill = c;
198 },
199 },
161200 .FormatWidth => switch (c) {
162201 '0'...'9' => {
163202 if (options.width == null) {
......@@ -1571,3 +1610,7 @@ test "positional" {
15711610test "positional with specifier" {
15721611 try testFmt("10.0", "{0d:.1}", f64(9.999));
15731612}
1613
1614test "positional/alignment/width/precision" {
1615 try testFmt("10.0", "{0d: >3.1}", f64(9.999));
1616}