authorgravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2020-01-30 22:25:33+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-01-30 18:07:02-05:00
logd27678fe83f8c7d61cc41c7300d68fe502dd4999
treecbae08db94e94949f681d7a0246dabb951aa43c7
parent13f782426660f49eb6ee7561ecee9237c22b4dc9

fmt: Refactor the arg fetching code

* Error out if the requested index is out-of-bound * Tidy-up all the arg-related variables in a struct

1 files changed, 31 insertions(+), 28 deletions(-)

lib/std/fmt.zig+31-28
...@@ -21,17 +21,6 @@ pub const FormatOptions = struct {...@@ -21,17 +21,6 @@ pub const FormatOptions = struct {
21 fill: u8 = ' ',21 fill: u8 = ' ',
22};22};
2323
24fn nextArg(comptime used_pos_args: *u32, comptime maybe_pos_arg: ?comptime_int, comptime next_arg: *comptime_int) comptime_int {
25 if (maybe_pos_arg) |pos_arg| {
26 used_pos_args.* |= 1 << pos_arg;
27 return pos_arg;
28 } else {
29 const arg = next_arg.*;
30 next_arg.* += 1;
31 return arg;
32 }
33}
34
35fn peekIsAlign(comptime fmt: []const u8) bool {24fn peekIsAlign(comptime fmt: []const u8) bool {
36 // Should only be called during a state transition to the format segment.25 // Should only be called during a state transition to the format segment.
37 comptime assert(fmt[0] == ':');26 comptime assert(fmt[0] == ':');
...@@ -113,12 +102,36 @@ pub fn format(...@@ -113,12 +102,36 @@ pub fn format(
113102
114 comptime var start_index = 0;103 comptime var start_index = 0;
115 comptime var state = State.Start;104 comptime var state = State.Start;
116 comptime var next_arg = 0;
117 comptime var maybe_pos_arg: ?comptime_int = null;105 comptime var maybe_pos_arg: ?comptime_int = null;
118 comptime var used_pos_args: ArgSetType = 0;
119 comptime var specifier_start = 0;106 comptime var specifier_start = 0;
120 comptime var specifier_end = 0;107 comptime var specifier_end = 0;
121 comptime var options = FormatOptions{};108 comptime var options = FormatOptions{};
109 comptime var arg_state: struct {
110 next_arg: usize = 0,
111 used_args: ArgSetType = 0,
112 args_len: usize = args.len,
113
114 fn hasUnusedArgs(comptime self: *@This()) bool {
115 return (@popCount(ArgSetType, self.used_args) != self.args_len);
116 }
117
118 fn nextArg(comptime self: *@This(), comptime pos_arg: ?comptime_int) comptime_int {
119 const next_idx = pos_arg orelse blk: {
120 const arg = self.next_arg;
121 self.next_arg += 1;
122 break :blk arg;
123 };
124
125 if (next_idx >= self.args_len) {
126 @compileError("Too few arguments");
127 }
128
129 // Mark this argument as used
130 self.used_args |= 1 << next_idx;
131
132 return next_idx;
133 }
134 } = .{};
122135
123 inline for (fmt) |c, i| {136 inline for (fmt) |c, i| {
124 switch (state) {137 switch (state) {
...@@ -166,11 +179,7 @@ pub fn format(...@@ -166,11 +179,7 @@ pub fn format(
166 }179 }
167 },180 },
168 '}' => {181 '}' => {
169 const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg);182 const arg_to_print = comptime arg_state.nextArg(maybe_pos_arg);
170
171 if (arg_to_print >= args.len) {
172 @compileError("Too few arguments");
173 }
174183
175 try formatType(184 try formatType(
176 args[arg_to_print],185 args[arg_to_print],
...@@ -203,7 +212,7 @@ pub fn format(...@@ -203,7 +212,7 @@ pub fn format(
203 state = if (comptime peekIsAlign(fmt[i..])) State.FormatFillAndAlign else State.FormatWidth;212 state = if (comptime peekIsAlign(fmt[i..])) State.FormatFillAndAlign else State.FormatWidth;
204 },213 },
205 '}' => {214 '}' => {
206 const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg);215 const arg_to_print = comptime arg_state.nextArg(maybe_pos_arg);
207216
208 try formatType(217 try formatType(
209 args[arg_to_print],218 args[arg_to_print],
...@@ -250,7 +259,7 @@ pub fn format(...@@ -250,7 +259,7 @@ pub fn format(
250 state = .FormatPrecision;259 state = .FormatPrecision;
251 },260 },
252 '}' => {261 '}' => {
253 const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg);262 const arg_to_print = comptime arg_state.nextArg(maybe_pos_arg);
254263
255 try formatType(264 try formatType(
256 args[arg_to_print],265 args[arg_to_print],
...@@ -278,7 +287,7 @@ pub fn format(...@@ -278,7 +287,7 @@ pub fn format(
278 options.precision.? += c - '0';287 options.precision.? += c - '0';
279 },288 },
280 '}' => {289 '}' => {
281 const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg);290 const arg_to_print = comptime arg_state.nextArg(maybe_pos_arg);
282291
283 try formatType(292 try formatType(
284 args[arg_to_print],293 args[arg_to_print],
...@@ -299,13 +308,7 @@ pub fn format(...@@ -299,13 +308,7 @@ pub fn format(
299 }308 }
300 }309 }
301 comptime {310 comptime {
302 // All arguments must have been printed but we allow mixing positional and fixed to achieve this.311 if (comptime arg_state.hasUnusedArgs()) {
303 var i: usize = 0;
304 inline while (i < next_arg) : (i += 1) {
305 used_pos_args |= 1 << i;
306 }
307
308 if (@popCount(ArgSetType, used_pos_args) != args.len) {
309 @compileError("Unused arguments");312 @compileError("Unused arguments");
310 }313 }
311 if (state != State.Start) {314 if (state != State.Start) {