Commit 08e8d30dd6

Marc Tiehuis <marc@tiehu.is>
2019-06-25 07:11:04
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 parent 948dc7b
Changed files (1)
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));
+}