aboutsummaryrefslogtreecommitdiff
path: root/lib/std/zig.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2025-06-27 20:05:22 -0700
committerAndrew Kelley <andrew@ziglang.org>2025-07-07 22:43:51 -0700
commit0e37ff0d591dd75ceec9208196bec29efaec607a (patch)
treec126fa823a1f3864e9c363aac70e3a3db0219957 /lib/std/zig.zig
parent0b3f0124dc33403d329fb8ee63a93215d9af1f1e (diff)
downloadzig-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.zig225
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' });
},
};
}