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.
master
Marc Tiehuis 2019-06-25 17:11:04 +12:00
parent 948dc7b304
commit 08e8d30dd6
1 changed files with 45 additions and 2 deletions

View File

@ -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));
}