diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2025-06-27 20:05:22 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2025-07-07 22:43:51 -0700 |
| commit | 0e37ff0d591dd75ceec9208196bec29efaec607a (patch) | |
| tree | c126fa823a1f3864e9c363aac70e3a3db0219957 /lib/std/zig.zig | |
| parent | 0b3f0124dc33403d329fb8ee63a93215d9af1f1e (diff) | |
| download | zig-0e37ff0d591dd75ceec9208196bec29efaec607a.tar.gz zig-0e37ff0d591dd75ceec9208196bec29efaec607a.zip | |
std.fmt: breaking API changes
added adapter to AnyWriter and GenericWriter to help bridge the gap
between old and new API
make std.testing.expectFmt work at compile-time
std.fmt no longer has a dependency on std.unicode. Formatted printing
was never properly unicode-aware. Now it no longer pretends to be.
Breakage/deprecations:
* std.fs.File.reader -> std.fs.File.deprecatedReader
* std.fs.File.writer -> std.fs.File.deprecatedWriter
* std.io.GenericReader -> std.io.Reader
* std.io.GenericWriter -> std.io.Writer
* std.io.AnyReader -> std.io.Reader
* std.io.AnyWriter -> std.io.Writer
* std.fmt.format -> std.fmt.deprecatedFormat
* std.fmt.fmtSliceEscapeLower -> std.ascii.hexEscape
* std.fmt.fmtSliceEscapeUpper -> std.ascii.hexEscape
* std.fmt.fmtSliceHexLower -> {x}
* std.fmt.fmtSliceHexUpper -> {X}
* std.fmt.fmtIntSizeDec -> {B}
* std.fmt.fmtIntSizeBin -> {Bi}
* std.fmt.fmtDuration -> {D}
* std.fmt.fmtDurationSigned -> {D}
* {} -> {f} when there is a format method
* format method signature
- anytype -> *std.io.Writer
- inferred error set -> error{WriteFailed}
- options -> (deleted)
* std.fmt.Formatted
- now takes context type explicitly
- no fmt string
Diffstat (limited to 'lib/std/zig.zig')
| -rw-r--r-- | lib/std/zig.zig | 225 |
1 files changed, 106 insertions, 119 deletions
diff --git a/lib/std/zig.zig b/lib/std/zig.zig index da4973b109..5d7f16a2a0 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -363,149 +363,136 @@ const Allocator = std.mem.Allocator; /// Return a Formatter for a Zig identifier, escaping it with `@""` syntax if needed. /// -/// - An empty `{}` format specifier escapes invalid identifiers, identifiers that shadow primitives -/// and the reserved `_` identifier. -/// - Add `p` to the specifier to render identifiers that shadow primitives unescaped. -/// - Add `_` to the specifier to render the reserved `_` identifier unescaped. -/// - `p` and `_` can be combined, e.g. `{p_}`. +/// See also `fmtIdFlags`. +pub fn fmtId(bytes: []const u8) std.fmt.Formatter(FormatId, FormatId.render) { + return .{ .data = .{ .bytes = bytes, .flags = .{} } }; +} + +/// Return a Formatter for a Zig identifier, escaping it with `@""` syntax if needed. /// -pub fn fmtId(bytes: []const u8) std.fmt.Formatter(formatId) { - return .{ .data = bytes }; +/// See also `fmtId`. +pub fn fmtIdFlags(bytes: []const u8, flags: FormatId.Flags) std.fmt.Formatter(FormatId, FormatId.render) { + return .{ .data = .{ .bytes = bytes, .flags = flags } }; +} + +pub fn fmtIdPU(bytes: []const u8) std.fmt.Formatter(FormatId, FormatId.render) { + return .{ .data = .{ .bytes = bytes, .flags = .{ .allow_primitive = true, .allow_underscore = true } } }; +} + +pub fn fmtIdP(bytes: []const u8) std.fmt.Formatter(FormatId, FormatId.render) { + return .{ .data = .{ .bytes = bytes, .flags = .{ .allow_primitive = true } } }; } test fmtId { const expectFmt = std.testing.expectFmt; - try expectFmt("@\"while\"", "{}", .{fmtId("while")}); - try expectFmt("@\"while\"", "{p}", .{fmtId("while")}); - try expectFmt("@\"while\"", "{_}", .{fmtId("while")}); - try expectFmt("@\"while\"", "{p_}", .{fmtId("while")}); - try expectFmt("@\"while\"", "{_p}", .{fmtId("while")}); - - try expectFmt("hello", "{}", .{fmtId("hello")}); - try expectFmt("hello", "{p}", .{fmtId("hello")}); - try expectFmt("hello", "{_}", .{fmtId("hello")}); - try expectFmt("hello", "{p_}", .{fmtId("hello")}); - try expectFmt("hello", "{_p}", .{fmtId("hello")}); - - try expectFmt("@\"type\"", "{}", .{fmtId("type")}); - try expectFmt("type", "{p}", .{fmtId("type")}); - try expectFmt("@\"type\"", "{_}", .{fmtId("type")}); - try expectFmt("type", "{p_}", .{fmtId("type")}); - try expectFmt("type", "{_p}", .{fmtId("type")}); - - try expectFmt("@\"_\"", "{}", .{fmtId("_")}); - try expectFmt("@\"_\"", "{p}", .{fmtId("_")}); - try expectFmt("_", "{_}", .{fmtId("_")}); - try expectFmt("_", "{p_}", .{fmtId("_")}); - try expectFmt("_", "{_p}", .{fmtId("_")}); - - try expectFmt("@\"i123\"", "{}", .{fmtId("i123")}); - try expectFmt("i123", "{p}", .{fmtId("i123")}); - try expectFmt("@\"4four\"", "{}", .{fmtId("4four")}); - try expectFmt("_underscore", "{}", .{fmtId("_underscore")}); - try expectFmt("@\"11\\\"23\"", "{}", .{fmtId("11\"23")}); - try expectFmt("@\"11\\x0f23\"", "{}", .{fmtId("11\x0F23")}); + try expectFmt("@\"while\"", "{f}", .{fmtId("while")}); + try expectFmt("@\"while\"", "{f}", .{fmtIdFlags("while", .{ .allow_primitive = true })}); + try expectFmt("@\"while\"", "{f}", .{fmtIdFlags("while", .{ .allow_underscore = true })}); + try expectFmt("@\"while\"", "{f}", .{fmtIdFlags("while", .{ .allow_primitive = true, .allow_underscore = true })}); + + try expectFmt("hello", "{f}", .{fmtId("hello")}); + try expectFmt("hello", "{f}", .{fmtIdFlags("hello", .{ .allow_primitive = true })}); + try expectFmt("hello", "{f}", .{fmtIdFlags("hello", .{ .allow_underscore = true })}); + try expectFmt("hello", "{f}", .{fmtIdFlags("hello", .{ .allow_primitive = true, .allow_underscore = true })}); + + try expectFmt("@\"type\"", "{f}", .{fmtId("type")}); + try expectFmt("type", "{f}", .{fmtIdFlags("type", .{ .allow_primitive = true })}); + try expectFmt("@\"type\"", "{f}", .{fmtIdFlags("type", .{ .allow_underscore = true })}); + try expectFmt("type", "{f}", .{fmtIdFlags("type", .{ .allow_primitive = true, .allow_underscore = true })}); + + try expectFmt("@\"_\"", "{f}", .{fmtId("_")}); + try expectFmt("@\"_\"", "{f}", .{fmtIdFlags("_", .{ .allow_primitive = true })}); + try expectFmt("_", "{f}", .{fmtIdFlags("_", .{ .allow_underscore = true })}); + try expectFmt("_", "{f}", .{fmtIdFlags("_", .{ .allow_primitive = true, .allow_underscore = true })}); + + try expectFmt("@\"i123\"", "{f}", .{fmtId("i123")}); + try expectFmt("i123", "{f}", .{fmtIdFlags("i123", .{ .allow_primitive = true })}); + try expectFmt("@\"4four\"", "{f}", .{fmtId("4four")}); + try expectFmt("_underscore", "{f}", .{fmtId("_underscore")}); + try expectFmt("@\"11\\\"23\"", "{f}", .{fmtId("11\"23")}); + try expectFmt("@\"11\\x0f23\"", "{f}", .{fmtId("11\x0F23")}); // These are technically not currently legal in Zig. - try expectFmt("@\"\"", "{}", .{fmtId("")}); - try expectFmt("@\"\\x00\"", "{}", .{fmtId("\x00")}); + try expectFmt("@\"\"", "{f}", .{fmtId("")}); + try expectFmt("@\"\\x00\"", "{f}", .{fmtId("\x00")}); } -/// Print the string as a Zig identifier, escaping it with `@""` syntax if needed. -fn formatId( +pub const FormatId = struct { bytes: []const u8, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, -) !void { - const allow_primitive, const allow_underscore = comptime parse_fmt: { - var allow_primitive = false; - var allow_underscore = false; - for (fmt) |char| { - switch (char) { - 'p' => if (!allow_primitive) { - allow_primitive = true; - continue; - }, - '_' => if (!allow_underscore) { - allow_underscore = true; - continue; - }, - else => {}, - } - @compileError("expected {}, {p}, {_}, {p_} or {_p}, found {" ++ fmt ++ "}"); - } - break :parse_fmt .{ allow_primitive, allow_underscore }; + flags: Flags, + pub const Flags = struct { + allow_primitive: bool = false, + allow_underscore: bool = false, }; - if (isValidId(bytes) and - (allow_primitive or !std.zig.isPrimitive(bytes)) and - (allow_underscore or !isUnderscore(bytes))) - { - return writer.writeAll(bytes); + /// Print the string as a Zig identifier, escaping it with `@""` syntax if needed. + fn render(ctx: FormatId, writer: *std.io.Writer) std.io.Writer.Error!void { + const bytes = ctx.bytes; + if (isValidId(bytes) and + (ctx.flags.allow_primitive or !std.zig.isPrimitive(bytes)) and + (ctx.flags.allow_underscore or !isUnderscore(bytes))) + { + return writer.writeAll(bytes); + } + try writer.writeAll("@\""); + try stringEscape(bytes, writer); + try writer.writeByte('"'); } - try writer.writeAll("@\""); - try stringEscape(bytes, "", options, writer); - try writer.writeByte('"'); +}; + +/// Return a formatter for escaping a double quoted Zig string. +pub fn fmtString(bytes: []const u8) std.fmt.Formatter([]const u8, stringEscape) { + return .{ .data = bytes }; } -/// Return a Formatter for Zig Escapes of a double quoted string. -/// The format specifier must be one of: -/// * `{}` treats contents as a double-quoted string. -/// * `{'}` treats contents as a single-quoted string. -pub fn fmtEscapes(bytes: []const u8) std.fmt.Formatter(stringEscape) { +/// Return a formatter for escaping a single quoted Zig string. +pub fn fmtChar(bytes: []const u8) std.fmt.Formatter([]const u8, charEscape) { return .{ .data = bytes }; } -test fmtEscapes { - const expectFmt = std.testing.expectFmt; - try expectFmt("\\x0f", "{}", .{fmtEscapes("\x0f")}); - try expectFmt( - \\" \\ hi \x07 \x11 " derp \'" - , "\"{'}\"", .{fmtEscapes(" \\ hi \x07 \x11 \" derp '")}); - try expectFmt( +test fmtString { + try std.testing.expectFmt("\\x0f", "{f}", .{fmtString("\x0f")}); + try std.testing.expectFmt( \\" \\ hi \x07 \x11 \" derp '" - , "\"{}\"", .{fmtEscapes(" \\ hi \x07 \x11 \" derp '")}); + , "\"{f}\"", .{fmtString(" \\ hi \x07 \x11 \" derp '")}); } -/// Print the string as escaped contents of a double quoted or single-quoted string. -/// Format `{}` treats contents as a double-quoted string. -/// Format `{'}` treats contents as a single-quoted string. -pub fn stringEscape( - bytes: []const u8, - comptime f: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, -) !void { - _ = options; +test fmtChar { + try std.testing.expectFmt( + \\" \\ hi \x07 \x11 " derp \'" + , "\"{f}\"", .{fmtChar(" \\ hi \x07 \x11 \" derp '")}); +} + +/// Print the string as escaped contents of a double quoted string. +pub fn stringEscape(bytes: []const u8, w: *std.io.Writer) std.io.Writer.Error!void { for (bytes) |byte| switch (byte) { - '\n' => try writer.writeAll("\\n"), - '\r' => try writer.writeAll("\\r"), - '\t' => try writer.writeAll("\\t"), - '\\' => try writer.writeAll("\\\\"), - '"' => { - if (f.len == 1 and f[0] == '\'') { - try writer.writeByte('"'); - } else if (f.len == 0) { - try writer.writeAll("\\\""); - } else { - @compileError("expected {} or {'}, found {" ++ f ++ "}"); - } - }, - '\'' => { - if (f.len == 1 and f[0] == '\'') { - try writer.writeAll("\\'"); - } else if (f.len == 0) { - try writer.writeByte('\''); - } else { - @compileError("expected {} or {'}, found {" ++ f ++ "}"); - } + '\n' => try w.writeAll("\\n"), + '\r' => try w.writeAll("\\r"), + '\t' => try w.writeAll("\\t"), + '\\' => try w.writeAll("\\\\"), + '"' => try w.writeAll("\\\""), + '\'' => try w.writeByte('\''), + ' ', '!', '#'...'&', '('...'[', ']'...'~' => try w.writeByte(byte), + else => { + try w.writeAll("\\x"); + try w.printIntOptions(byte, 16, .lower, .{ .width = 2, .fill = '0' }); }, - ' ', '!', '#'...'&', '('...'[', ']'...'~' => try writer.writeByte(byte), - // Use hex escapes for rest any unprintable characters. + }; +} + +/// Print the string as escaped contents of a single-quoted string. +pub fn charEscape(bytes: []const u8, w: *std.io.Writer) std.io.Writer.Error!void { + for (bytes) |byte| switch (byte) { + '\n' => try w.writeAll("\\n"), + '\r' => try w.writeAll("\\r"), + '\t' => try w.writeAll("\\t"), + '\\' => try w.writeAll("\\\\"), + '"' => try w.writeByte('"'), + '\'' => try w.writeAll("\\'"), + ' ', '!', '#'...'&', '('...'[', ']'...'~' => try w.writeByte(byte), else => { - try writer.writeAll("\\x"); - try std.fmt.formatInt(byte, 16, .lower, .{ .width = 2, .fill = '0' }, writer); + try w.writeAll("\\x"); + try w.printIntOptions(byte, 16, .lower, .{ .width = 2, .fill = '0' }); }, }; } |
