zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

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 }