Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fmt: Refactor the arg fetching code #4331

Merged
merged 1 commit into from Jan 30, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
59 changes: 31 additions & 28 deletions lib/std/fmt.zig
Expand Up @@ -21,17 +21,6 @@ pub const FormatOptions = struct {
fill: u8 = ' ',
};

fn nextArg(comptime used_pos_args: *u32, comptime maybe_pos_arg: ?comptime_int, comptime next_arg: *comptime_int) comptime_int {
if (maybe_pos_arg) |pos_arg| {
used_pos_args.* |= 1 << pos_arg;
return pos_arg;
} else {
const arg = next_arg.*;
next_arg.* += 1;
return arg;
}
}

fn peekIsAlign(comptime fmt: []const u8) bool {
// Should only be called during a state transition to the format segment.
comptime assert(fmt[0] == ':');
Expand Down Expand Up @@ -113,12 +102,36 @@ pub fn format(

comptime var start_index = 0;
comptime var state = State.Start;
comptime var next_arg = 0;
comptime var maybe_pos_arg: ?comptime_int = null;
comptime var used_pos_args: ArgSetType = 0;
comptime var specifier_start = 0;
comptime var specifier_end = 0;
comptime var options = FormatOptions{};
comptime var arg_state: struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh wow, that's a really cool pattern

next_arg: usize = 0,
used_args: ArgSetType = 0,
args_len: usize = args.len,

fn hasUnusedArgs(comptime self: *@This()) bool {
return (@popCount(ArgSetType, self.used_args) != self.args_len);
}

fn nextArg(comptime self: *@This(), comptime pos_arg: ?comptime_int) comptime_int {
const next_idx = pos_arg orelse blk: {
const arg = self.next_arg;
self.next_arg += 1;
break :blk arg;
};

if (next_idx >= self.args_len) {
@compileError("Too few arguments");
}

// Mark this argument as used
self.used_args |= 1 << next_idx;

return next_idx;
}
} = .{};

inline for (fmt) |c, i| {
switch (state) {
Expand Down Expand Up @@ -166,11 +179,7 @@ pub fn format(
}
},
'}' => {
const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg);

if (arg_to_print >= args.len) {
@compileError("Too few arguments");
}
const arg_to_print = comptime arg_state.nextArg(maybe_pos_arg);

try formatType(
args[arg_to_print],
Expand Down Expand Up @@ -203,7 +212,7 @@ pub fn format(
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);
const arg_to_print = comptime arg_state.nextArg(maybe_pos_arg);

try formatType(
args[arg_to_print],
Expand Down Expand Up @@ -250,7 +259,7 @@ pub fn format(
state = .FormatPrecision;
},
'}' => {
const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg);
const arg_to_print = comptime arg_state.nextArg(maybe_pos_arg);

try formatType(
args[arg_to_print],
Expand Down Expand Up @@ -278,7 +287,7 @@ pub fn format(
options.precision.? += c - '0';
},
'}' => {
const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg);
const arg_to_print = comptime arg_state.nextArg(maybe_pos_arg);

try formatType(
args[arg_to_print],
Expand All @@ -299,13 +308,7 @@ pub fn format(
}
}
comptime {
// All arguments must have been printed but we allow mixing positional and fixed to achieve this.
var i: usize = 0;
inline while (i < next_arg) : (i += 1) {
used_pos_args |= 1 << i;
}

if (@popCount(ArgSetType, used_pos_args) != args.len) {
if (comptime arg_state.hasUnusedArgs()) {
@compileError("Unused arguments");
}
if (state != State.Start) {
Expand Down