| ... | ... | @@ -148,7 +148,7 @@ pub fn format( |
| 148 | 148 | comptime assert(fmt[i] == '}'); |
| 149 | 149 | i += 1; |
| 150 | 150 | |
| 151 | | const placeholder = comptime parsePlaceholder(fmt[fmt_begin..fmt_end].*); |
| 151 | const placeholder = comptime Placeholder.parse(fmt[fmt_begin..fmt_end].*); |
| 152 | 152 | const arg_pos = comptime switch (placeholder.arg) { |
| 153 | 153 | .none => null, |
| 154 | 154 | .number => |pos| pos, |
| ... | ... | @@ -205,102 +205,102 @@ pub fn format( |
| 205 | 205 | } |
| 206 | 206 | } |
| 207 | 207 | |
| 208 | | fn parsePlaceholder(comptime str: anytype) Placeholder { |
| 209 | | comptime var parser = Parser{ .buf = &str }; |
| 208 | fn cacheString(str: anytype) []const u8 { |
| 209 | return &str; |
| 210 | } |
| 210 | 211 | |
| 211 | | // Parse the positional argument number |
| 212 | | const arg = comptime parser.specifier() catch |err| |
| 213 | | @compileError(@errorName(err)); |
| 212 | pub const Placeholder = struct { |
| 213 | specifier_arg: []const u8, |
| 214 | fill: u8, |
| 215 | alignment: Alignment, |
| 216 | arg: Specifier, |
| 217 | width: Specifier, |
| 218 | precision: Specifier, |
| 214 | 219 | |
| 215 | | // Parse the format specifier |
| 216 | | const specifier_arg = comptime parser.until(':'); |
| 220 | pub fn parse(comptime str: anytype) Placeholder { |
| 221 | comptime var parser = Parser{ .buf = &str }; |
| 217 | 222 | |
| 218 | | // Skip the colon, if present |
| 219 | | if (comptime parser.char()) |ch| { |
| 220 | | if (ch != ':') { |
| 221 | | @compileError("expected : or }, found '" ++ [1]u8{ch} ++ "'"); |
| 222 | | } |
| 223 | | } |
| 223 | // Parse the positional argument number |
| 224 | const arg = comptime parser.specifier() catch |err| |
| 225 | @compileError(@errorName(err)); |
| 224 | 226 | |
| 225 | | // Parse the fill character |
| 226 | | // The fill parameter requires the alignment parameter to be specified |
| 227 | | // too |
| 228 | | const fill = comptime if (parser.peek(1)) |ch| |
| 229 | | switch (ch) { |
| 230 | | '<', '^', '>' => parser.char().?, |
| 231 | | else => ' ', |
| 232 | | } |
| 233 | | else |
| 234 | | ' '; |
| 227 | // Parse the format specifier |
| 228 | const specifier_arg = comptime parser.until(':'); |
| 235 | 229 | |
| 236 | | // Parse the alignment parameter |
| 237 | | const alignment: Alignment = comptime if (parser.peek(0)) |ch| init: { |
| 238 | | switch (ch) { |
| 239 | | '<', '^', '>' => _ = parser.char(), |
| 240 | | else => {}, |
| 230 | // Skip the colon, if present |
| 231 | if (comptime parser.char()) |ch| { |
| 232 | if (ch != ':') { |
| 233 | @compileError("expected : or }, found '" ++ [1]u8{ch} ++ "'"); |
| 234 | } |
| 241 | 235 | } |
| 242 | | break :init switch (ch) { |
| 243 | | '<' => .Left, |
| 244 | | '^' => .Center, |
| 245 | | else => .Right, |
| 246 | | }; |
| 247 | | } else .Right; |
| 248 | 236 | |
| 249 | | // Parse the width parameter |
| 250 | | const width = comptime parser.specifier() catch |err| |
| 251 | | @compileError(@errorName(err)); |
| 237 | // Parse the fill character |
| 238 | // The fill parameter requires the alignment parameter to be specified |
| 239 | // too |
| 240 | const fill = comptime if (parser.peek(1)) |ch| |
| 241 | switch (ch) { |
| 242 | '<', '^', '>' => parser.char().?, |
| 243 | else => ' ', |
| 244 | } |
| 245 | else |
| 246 | ' '; |
| 252 | 247 | |
| 253 | | // Skip the dot, if present |
| 254 | | if (comptime parser.char()) |ch| { |
| 255 | | if (ch != '.') { |
| 256 | | @compileError("expected . or }, found '" ++ [1]u8{ch} ++ "'"); |
| 257 | | } |
| 258 | | } |
| 248 | // Parse the alignment parameter |
| 249 | const alignment: Alignment = comptime if (parser.peek(0)) |ch| init: { |
| 250 | switch (ch) { |
| 251 | '<', '^', '>' => _ = parser.char(), |
| 252 | else => {}, |
| 253 | } |
| 254 | break :init switch (ch) { |
| 255 | '<' => .Left, |
| 256 | '^' => .Center, |
| 257 | else => .Right, |
| 258 | }; |
| 259 | } else .Right; |
| 259 | 260 | |
| 260 | | // Parse the precision parameter |
| 261 | | const precision = comptime parser.specifier() catch |err| |
| 262 | | @compileError(@errorName(err)); |
| 261 | // Parse the width parameter |
| 262 | const width = comptime parser.specifier() catch |err| |
| 263 | @compileError(@errorName(err)); |
| 263 | 264 | |
| 264 | | if (comptime parser.char()) |ch| { |
| 265 | | @compileError("extraneous trailing character '" ++ [1]u8{ch} ++ "'"); |
| 266 | | } |
| 265 | // Skip the dot, if present |
| 266 | if (comptime parser.char()) |ch| { |
| 267 | if (ch != '.') { |
| 268 | @compileError("expected . or }, found '" ++ [1]u8{ch} ++ "'"); |
| 269 | } |
| 270 | } |
| 267 | 271 | |
| 268 | | return Placeholder{ |
| 269 | | .specifier_arg = cacheString(specifier_arg[0..specifier_arg.len].*), |
| 270 | | .fill = fill, |
| 271 | | .alignment = alignment, |
| 272 | | .arg = arg, |
| 273 | | .width = width, |
| 274 | | .precision = precision, |
| 275 | | }; |
| 276 | | } |
| 272 | // Parse the precision parameter |
| 273 | const precision = comptime parser.specifier() catch |err| |
| 274 | @compileError(@errorName(err)); |
| 277 | 275 | |
| 278 | | fn cacheString(str: anytype) []const u8 { |
| 279 | | return &str; |
| 280 | | } |
| 276 | if (comptime parser.char()) |ch| { |
| 277 | @compileError("extraneous trailing character '" ++ [1]u8{ch} ++ "'"); |
| 278 | } |
| 281 | 279 | |
| 282 | | const Placeholder = struct { |
| 283 | | specifier_arg: []const u8, |
| 284 | | fill: u8, |
| 285 | | alignment: Alignment, |
| 286 | | arg: Specifier, |
| 287 | | width: Specifier, |
| 288 | | precision: Specifier, |
| 280 | return Placeholder{ |
| 281 | .specifier_arg = cacheString(specifier_arg[0..specifier_arg.len].*), |
| 282 | .fill = fill, |
| 283 | .alignment = alignment, |
| 284 | .arg = arg, |
| 285 | .width = width, |
| 286 | .precision = precision, |
| 287 | }; |
| 288 | } |
| 289 | 289 | }; |
| 290 | 290 | |
| 291 | | const Specifier = union(enum) { |
| 291 | pub const Specifier = union(enum) { |
| 292 | 292 | none, |
| 293 | 293 | number: usize, |
| 294 | 294 | named: []const u8, |
| 295 | 295 | }; |
| 296 | 296 | |
| 297 | | const Parser = struct { |
| 297 | pub const Parser = struct { |
| 298 | 298 | buf: []const u8, |
| 299 | 299 | pos: usize = 0, |
| 300 | 300 | |
| 301 | 301 | // Returns a decimal number or null if the current character is not a |
| 302 | 302 | // digit |
| 303 | | fn number(self: *@This()) ?usize { |
| 303 | pub fn number(self: *@This()) ?usize { |
| 304 | 304 | var r: ?usize = null; |
| 305 | 305 | |
| 306 | 306 | while (self.pos < self.buf.len) : (self.pos += 1) { |
| ... | ... | @@ -319,7 +319,7 @@ const Parser = struct { |
| 319 | 319 | |
| 320 | 320 | // Returns a substring of the input starting from the current position |
| 321 | 321 | // and ending where `ch` is found or until the end if not found |
| 322 | | fn until(self: *@This(), ch: u8) []const u8 { |
| 322 | pub fn until(self: *@This(), ch: u8) []const u8 { |
| 323 | 323 | const start = self.pos; |
| 324 | 324 | |
| 325 | 325 | if (start >= self.buf.len) |
| ... | ... | @@ -332,7 +332,7 @@ const Parser = struct { |
| 332 | 332 | } |
| 333 | 333 | |
| 334 | 334 | // Returns one character, if available |
| 335 | | fn char(self: *@This()) ?u8 { |
| 335 | pub fn char(self: *@This()) ?u8 { |
| 336 | 336 | if (self.pos < self.buf.len) { |
| 337 | 337 | const ch = self.buf[self.pos]; |
| 338 | 338 | self.pos += 1; |
| ... | ... | @@ -341,7 +341,7 @@ const Parser = struct { |
| 341 | 341 | return null; |
| 342 | 342 | } |
| 343 | 343 | |
| 344 | | fn maybe(self: *@This(), val: u8) bool { |
| 344 | pub fn maybe(self: *@This(), val: u8) bool { |
| 345 | 345 | if (self.pos < self.buf.len and self.buf[self.pos] == val) { |
| 346 | 346 | self.pos += 1; |
| 347 | 347 | return true; |
| ... | ... | @@ -351,7 +351,7 @@ const Parser = struct { |
| 351 | 351 | |
| 352 | 352 | // Returns a decimal number or null if the current character is not a |
| 353 | 353 | // digit |
| 354 | | fn specifier(self: *@This()) !Specifier { |
| 354 | pub fn specifier(self: *@This()) !Specifier { |
| 355 | 355 | if (self.maybe('[')) { |
| 356 | 356 | const arg_name = self.until(']'); |
| 357 | 357 | |
| ... | ... | @@ -367,24 +367,24 @@ const Parser = struct { |
| 367 | 367 | } |
| 368 | 368 | |
| 369 | 369 | // Returns the n-th next character or null if that's past the end |
| 370 | | fn peek(self: *@This(), n: usize) ?u8 { |
| 370 | pub fn peek(self: *@This(), n: usize) ?u8 { |
| 371 | 371 | return if (self.pos + n < self.buf.len) self.buf[self.pos + n] else null; |
| 372 | 372 | } |
| 373 | 373 | }; |
| 374 | 374 | |
| 375 | | const ArgSetType = u32; |
| 375 | pub const ArgSetType = u32; |
| 376 | 376 | const max_format_args = @typeInfo(ArgSetType).Int.bits; |
| 377 | 377 | |
| 378 | | const ArgState = struct { |
| 378 | pub const ArgState = struct { |
| 379 | 379 | next_arg: usize = 0, |
| 380 | 380 | used_args: ArgSetType = 0, |
| 381 | 381 | args_len: usize, |
| 382 | 382 | |
| 383 | | fn hasUnusedArgs(self: *@This()) bool { |
| 383 | pub fn hasUnusedArgs(self: *@This()) bool { |
| 384 | 384 | return @popCount(self.used_args) != self.args_len; |
| 385 | 385 | } |
| 386 | 386 | |
| 387 | | fn nextArg(self: *@This(), arg_index: ?usize) ?usize { |
| 387 | pub fn nextArg(self: *@This(), arg_index: ?usize) ?usize { |
| 388 | 388 | const next_index = arg_index orelse init: { |
| 389 | 389 | const arg = self.next_arg; |
| 390 | 390 | self.next_arg += 1; |
| ... | ... | @@ -430,7 +430,7 @@ pub fn formatAddress(value: anytype, options: FormatOptions, writer: anytype) @T |
| 430 | 430 | // This ANY const is a workaround for: https://github.com/ziglang/zig/issues/7948 |
| 431 | 431 | const ANY = "any"; |
| 432 | 432 | |
| 433 | | fn defaultSpec(comptime T: type) [:0]const u8 { |
| 433 | pub fn defaultSpec(comptime T: type) [:0]const u8 { |
| 434 | 434 | switch (@typeInfo(T)) { |
| 435 | 435 | .Array => |_| return ANY, |
| 436 | 436 | .Pointer => |ptr_info| switch (ptr_info.size) { |