blob 56f480dc (3341B) - Raw
1 const std = @import("std"); 2 const mem = std.mem; 3 4 /// Print the string as a Zig identifier escaping it with @"" syntax if needed. 5 pub fn formatId( 6 bytes: []const u8, 7 comptime fmt: []const u8, 8 options: std.fmt.FormatOptions, 9 writer: anytype, 10 ) !void { 11 if (isValidId(bytes)) { 12 return writer.writeAll(bytes); 13 } 14 try writer.writeAll("@\""); 15 try formatEscapes(bytes, "", options, writer); 16 try writer.writeByte('"'); 17 } 18 19 /// Return a Formatter for a Zig identifier 20 pub fn fmtId(bytes: []const u8) std.fmt.Formatter(formatId) { 21 return .{ .data = bytes }; 22 } 23 24 pub fn isValidId(bytes: []const u8) bool { 25 for (bytes) |c, i| { 26 switch (c) { 27 '_', 'a'...'z', 'A'...'Z' => {}, 28 '0'...'9' => if (i == 0) return false, 29 else => return false, 30 } 31 } 32 return std.zig.Token.getKeyword(bytes) == null; 33 } 34 35 /// Print the string as escaped contents of a double quoted or single-quoted string. 36 /// Format `{}` treats contents as a double-quoted string. 37 /// Format `{'}` treats contents as a single-quoted string. 38 pub fn formatEscapes( 39 bytes: []const u8, 40 comptime fmt: []const u8, 41 options: std.fmt.FormatOptions, 42 writer: anytype, 43 ) !void { 44 for (bytes) |byte| switch (byte) { 45 '\n' => try writer.writeAll("\\n"), 46 '\r' => try writer.writeAll("\\r"), 47 '\t' => try writer.writeAll("\\t"), 48 '\\' => try writer.writeAll("\\\\"), 49 '"' => { 50 if (fmt.len == 1 and fmt[0] == '\'') { 51 try writer.writeByte('"'); 52 } else if (fmt.len == 0) { 53 try writer.writeAll("\\\""); 54 } else { 55 @compileError("expected {} or {'}, found {" ++ fmt ++ "}"); 56 } 57 }, 58 '\'' => { 59 if (fmt.len == 1 and fmt[0] == '\'') { 60 try writer.writeAll("\\'"); 61 } else if (fmt.len == 0) { 62 try writer.writeByte('\''); 63 } else { 64 @compileError("expected {} or {'}, found {" ++ fmt ++ "}"); 65 } 66 }, 67 ' ', '!', '#'...'&', '('...'[', ']'...'~' => try writer.writeByte(byte), 68 // Use hex escapes for rest any unprintable characters. 69 else => { 70 try writer.writeAll("\\x"); 71 try std.fmt.formatInt(byte, 16, false, .{ .width = 2, .fill = '0' }, writer); 72 }, 73 }; 74 } 75 76 /// Return a Formatter for Zig Escapes of a double quoted string. 77 /// The format specifier must be one of: 78 /// * `{}` treats contents as a double-quoted string. 79 /// * `{'}` treats contents as a single-quoted string. 80 pub fn fmtEscapes(bytes: []const u8) std.fmt.Formatter(formatEscapes) { 81 return .{ .data = bytes }; 82 } 83 84 test "escape invalid identifiers" { 85 const expectFmt = std.testing.expectFmt; 86 try expectFmt("@\"while\"", "{}", .{fmtId("while")}); 87 try expectFmt("hello", "{}", .{fmtId("hello")}); 88 try expectFmt("@\"11\\\"23\"", "{}", .{fmtId("11\"23")}); 89 try expectFmt("@\"11\\x0f23\"", "{}", .{fmtId("11\x0F23")}); 90 try expectFmt("\\x0f", "{}", .{fmtEscapes("\x0f")}); 91 try expectFmt( 92 \\" \\ hi \x07 \x11 " derp \'" 93 , "\"{'}\"", .{fmtEscapes(" \\ hi \x07 \x11 \" derp '")}); 94 try expectFmt( 95 \\" \\ hi \x07 \x11 \" derp '" 96 , "\"{}\"", .{fmtEscapes(" \\ hi \x07 \x11 \" derp '")}); 97 }