mirror of
https://github.com/ziglang/zig.git
synced 2026-02-14 05:20:34 +00:00
std.fmt: add specifier for Zig identifiers
This commit is contained in:
parent
245d98d32d
commit
8d38a91ca8
@ -65,6 +65,8 @@ fn peekIsAlign(comptime fmt: []const u8) bool {
|
||||
/// - format the non-numeric value as a string of bytes in hexadecimal notation ("binary dump") in either lower case or upper case
|
||||
/// - output numeric value in hexadecimal notation
|
||||
/// - `s`: print a pointer-to-many as a c-string, use zero-termination
|
||||
/// - `z`: escape the string with @"" syntax if it is not a valid Zig identifier.
|
||||
/// - `Z`: print the string escaping non-printable characters using Zig escape sequences.
|
||||
/// - `B` and `Bi`: output a memory size in either metric (1000) or power-of-two (1024) based notation. works for both float and integer values.
|
||||
/// - `e` and `E`: if printing a string, escape non-printable characters
|
||||
/// - `e`: output floating point value in scientific notation
|
||||
@ -543,7 +545,14 @@ pub fn formatIntValue(
|
||||
} else {
|
||||
@compileError("Cannot print integer that is larger than 8 bits as a ascii");
|
||||
}
|
||||
} else if (comptime std.mem.eql(u8, fmt, "b")) {
|
||||
} else if (comptime std.mem.eql(u8, fmt, "Z")) {
|
||||
if (@typeInfo(@TypeOf(int_value)).Int.bits <= 8) {
|
||||
const c: u8 = int_value;
|
||||
return formatZigEscapes(@as(*const [1]u8, &c), options, writer);
|
||||
} else {
|
||||
@compileError("Cannot escape character with more than 8 bits");
|
||||
}
|
||||
}else if (comptime std.mem.eql(u8, fmt, "b")) {
|
||||
radix = 2;
|
||||
uppercase = false;
|
||||
} else if (comptime std.mem.eql(u8, fmt, "x")) {
|
||||
@ -612,6 +621,10 @@ pub fn formatText(
|
||||
}
|
||||
}
|
||||
return;
|
||||
} else if (comptime std.mem.eql(u8, fmt, "z")) {
|
||||
return formatZigIdentifier(bytes, options, writer);
|
||||
} else if (comptime std.mem.eql(u8, fmt, "Z")) {
|
||||
return formatZigEscapes(bytes, options, writer);
|
||||
} else {
|
||||
@compileError("Unknown format string: '" ++ fmt ++ "'");
|
||||
}
|
||||
@ -652,9 +665,62 @@ pub fn formatBuf(
|
||||
}
|
||||
}
|
||||
|
||||
// Print a float in scientific notation to the specified precision. Null uses full precision.
|
||||
// It should be the case that every full precision, printed value can be re-parsed back to the
|
||||
// same type unambiguously.
|
||||
/// Print the string as a Zig identifier escaping it with @"" syntax if needed.
|
||||
pub fn formatZigIdentifier(
|
||||
bytes: []const u8,
|
||||
options: FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
if (isValidZigIdentifier(bytes)) {
|
||||
return writer.writeAll(bytes);
|
||||
}
|
||||
try writer.writeAll("@\"");
|
||||
try formatZigEscapes(bytes, options, writer);
|
||||
try writer.writeByte('"');
|
||||
}
|
||||
|
||||
fn isValidZigIdentifier(bytes: []const u8) bool {
|
||||
for (bytes) |c, i| {
|
||||
switch (c) {
|
||||
'_', 'a'...'z', 'A'...'Z' => {},
|
||||
'0'...'9' => if (i == 0) return false,
|
||||
else => return false,
|
||||
}
|
||||
}
|
||||
return std.zig.Token.getKeyword(bytes) == null;
|
||||
}
|
||||
|
||||
pub fn formatZigEscapes(
|
||||
bytes: []const u8,
|
||||
options: FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
for (bytes) |c| {
|
||||
const s: []const u8 = switch (c) {
|
||||
'\"' => "\\\"",
|
||||
'\'' => "\\'",
|
||||
'\\' => "\\\\",
|
||||
'\n' => "\\n",
|
||||
'\r' => "\\r",
|
||||
'\t' => "\\t",
|
||||
// Handle the remaining escapes Zig doesn't support by turning them
|
||||
// into their respective hex representation
|
||||
else => if (std.ascii.isCntrl(c)) {
|
||||
try writer.writeAll("\\x");
|
||||
try formatInt(c, 16, false, .{ .width = 2, .fill = '0' }, writer);
|
||||
continue;
|
||||
} else {
|
||||
try writer.writeByte(c);
|
||||
continue;
|
||||
},
|
||||
};
|
||||
try writer.writeAll(s);
|
||||
}
|
||||
}
|
||||
|
||||
/// Print a float in scientific notation to the specified precision. Null uses full precision.
|
||||
/// It should be the case that every full precision, printed value can be re-parsed back to the
|
||||
/// same type unambiguously.
|
||||
pub fn formatFloatScientific(
|
||||
value: anytype,
|
||||
options: FormatOptions,
|
||||
@ -746,8 +812,8 @@ pub fn formatFloatScientific(
|
||||
}
|
||||
}
|
||||
|
||||
// Print a float of the format x.yyyyy where the number of y is specified by the precision argument.
|
||||
// By default floats are printed at full precision (no rounding).
|
||||
/// Print a float of the format x.yyyyy where the number of y is specified by the precision argument.
|
||||
/// By default floats are printed at full precision (no rounding).
|
||||
pub fn formatFloatDecimal(
|
||||
value: anytype,
|
||||
options: FormatOptions,
|
||||
@ -1136,7 +1202,7 @@ pub fn bufPrintZ(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintErr
|
||||
return result[0 .. result.len - 1 :0];
|
||||
}
|
||||
|
||||
// Count the characters needed for format. Useful for preallocating memory
|
||||
/// Count the characters needed for format. Useful for preallocating memory
|
||||
pub fn count(comptime fmt: []const u8, args: anytype) u64 {
|
||||
var counting_writer = std.io.countingWriter(std.io.null_writer);
|
||||
format(counting_writer.writer(), fmt, args) catch |err| switch (err) {};
|
||||
@ -1334,6 +1400,14 @@ test "escape non-printable" {
|
||||
try testFmt("ab\\xFFc", "{E}", .{"ab\xffc"});
|
||||
}
|
||||
|
||||
test "escape invalid identifiers" {
|
||||
try testFmt("@\"while\"", "{z}", .{"while"});
|
||||
try testFmt("hello", "{z}", .{"hello"});
|
||||
try testFmt("@\"11\\\"23\"", "{z}", .{"11\"23"});
|
||||
try testFmt("@\"11\\x0f23\"", "{z}", .{"11\x0F23"});
|
||||
try testFmt("\\x0f", "{Z}", .{0x0f});
|
||||
}
|
||||
|
||||
test "pointer" {
|
||||
{
|
||||
const value = @intToPtr(*align(1) i32, 0xdeadbeef);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user