Commit 3f844cba0b

Andrew Kelley <andrew@ziglang.org>
2021-02-27 08:26:25
std.zig.fmt escaped string formatting recognizes single quote style
This introduces {'} to indicate escape for a single-quoted string, and {} to indicate escape for a double quoted string. Without this, there would be unnecessary \' inside double quoted strings, and unnecessary \" inside single quoted strings. Motivated by the llvm12 branch, in the new tool I am writing for updating target CPU features.
1 parent 0816981
Changed files (1)
lib
std
lib/std/zig/fmt.zig
@@ -12,7 +12,7 @@ pub fn formatId(
         return writer.writeAll(bytes);
     }
     try writer.writeAll("@\"");
-    try formatEscapes(bytes, fmt, options, writer);
+    try formatEscapes(bytes, "", options, writer);
     try writer.writeByte('"');
 }
 
@@ -32,6 +32,9 @@ pub fn isValidId(bytes: []const u8) bool {
     return std.zig.Token.getKeyword(bytes) == null;
 }
 
+/// 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 formatEscapes(
     bytes: []const u8,
     comptime fmt: []const u8,
@@ -43,8 +46,24 @@ pub fn formatEscapes(
         '\r' => try writer.writeAll("\\r"),
         '\t' => try writer.writeAll("\\t"),
         '\\' => try writer.writeAll("\\\\"),
-        '"' => try writer.writeAll("\\\""),
-        '\'' => try writer.writeAll("\\'"),
+        '"' => {
+            if (fmt.len == 1 and fmt[0] == '\'') {
+                try writer.writeByte('"');
+            } else if (fmt.len == 0) {
+                try writer.writeAll("\\\"");
+            } else {
+                @compileError("expected {} or {'}, found {" ++ fmt ++ "}");
+            }
+        },
+        '\'' => {
+            if (fmt.len == 1 and fmt[0] == '\'') {
+                try writer.writeAll("\\'");
+            } else if (fmt.len == 0) {
+                try writer.writeByte('\'');
+            } else {
+                @compileError("expected {} or {'}, found {" ++ fmt ++ "}");
+            }
+        },
         ' ', '!', '#'...'&', '('...'[', ']'...'~' => try writer.writeByte(byte),
         // Use hex escapes for rest any unprintable characters.
         else => {
@@ -54,7 +73,10 @@ pub fn formatEscapes(
     };
 }
 
-/// Return a Formatter for Zig Escapes
+/// 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(formatEscapes) {
     return .{ .data = bytes };
 }
@@ -67,6 +89,9 @@ test "escape invalid identifiers" {
     try expectFmt("@\"11\\x0f23\"", "{}", .{fmtId("11\x0F23")});
     try expectFmt("\\x0f", "{}", .{fmtEscapes("\x0f")});
     try expectFmt(
-        \\" \\ hi \x07 \x11 \" derp \'"
+        \\" \\ hi \x07 \x11 " derp \'"
+    , "\"{'}\"", .{fmtEscapes(" \\ hi \x07 \x11 \" derp '")});
+    try expectFmt(
+        \\" \\ hi \x07 \x11 \" derp '"
     , "\"{}\"", .{fmtEscapes(" \\ hi \x07 \x11 \" derp '")});
 }