authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-09-22 15:15:41+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-10-17 10:26:59+03:00
log8d38a91ca8b52d8e209db5041bd6f351da9cac22
tree2dc5fbeeb3979b59587da81a6eb6ba1667130e72
parent245d98d32dd29e80de9732f415a4731748008acf
signaturelock-open Commit is signed but in an unrecognized format.

std.fmt: add specifier for Zig identifiers


1 files changed, 81 insertions(+), 7 deletions(-)

lib/std/fmt.zig+81-7
...@@ -65,6 +65,8 @@ fn peekIsAlign(comptime fmt: []const u8) bool {...@@ -65,6 +65,8 @@ fn peekIsAlign(comptime fmt: []const u8) bool {
65/// - format the non-numeric value as a string of bytes in hexadecimal notation ("binary dump") in either lower case or upper case65/// - format the non-numeric value as a string of bytes in hexadecimal notation ("binary dump") in either lower case or upper case
66/// - output numeric value in hexadecimal notation66/// - output numeric value in hexadecimal notation
67/// - `s`: print a pointer-to-many as a c-string, use zero-termination67/// - `s`: print a pointer-to-many as a c-string, use zero-termination
68/// - `z`: escape the string with @"" syntax if it is not a valid Zig identifier.
69/// - `Z`: print the string escaping non-printable characters using Zig escape sequences.
68/// - `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.70/// - `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.
69/// - `e` and `E`: if printing a string, escape non-printable characters71/// - `e` and `E`: if printing a string, escape non-printable characters
70/// - `e`: output floating point value in scientific notation72/// - `e`: output floating point value in scientific notation
...@@ -543,7 +545,14 @@ pub fn formatIntValue(...@@ -543,7 +545,14 @@ pub fn formatIntValue(
543 } else {545 } else {
544 @compileError("Cannot print integer that is larger than 8 bits as a ascii");546 @compileError("Cannot print integer that is larger than 8 bits as a ascii");
545 }547 }
546 } else if (comptime std.mem.eql(u8, fmt, "b")) {548 } else if (comptime std.mem.eql(u8, fmt, "Z")) {
549 if (@typeInfo(@TypeOf(int_value)).Int.bits <= 8) {
550 const c: u8 = int_value;
551 return formatZigEscapes(@as(*const [1]u8, &c), options, writer);
552 } else {
553 @compileError("Cannot escape character with more than 8 bits");
554 }
555 }else if (comptime std.mem.eql(u8, fmt, "b")) {
547 radix = 2;556 radix = 2;
548 uppercase = false;557 uppercase = false;
549 } else if (comptime std.mem.eql(u8, fmt, "x")) {558 } else if (comptime std.mem.eql(u8, fmt, "x")) {
...@@ -612,6 +621,10 @@ pub fn formatText(...@@ -612,6 +621,10 @@ pub fn formatText(
612 }621 }
613 }622 }
614 return;623 return;
624 } else if (comptime std.mem.eql(u8, fmt, "z")) {
625 return formatZigIdentifier(bytes, options, writer);
626 } else if (comptime std.mem.eql(u8, fmt, "Z")) {
627 return formatZigEscapes(bytes, options, writer);
615 } else {628 } else {
616 @compileError("Unknown format string: '" ++ fmt ++ "'");629 @compileError("Unknown format string: '" ++ fmt ++ "'");
617 }630 }
...@@ -652,9 +665,62 @@ pub fn formatBuf(...@@ -652,9 +665,62 @@ pub fn formatBuf(
652 }665 }
653}666}
654667
655// Print a float in scientific notation to the specified precision. Null uses full precision.668/// Print the string as a Zig identifier escaping it with @"" syntax if needed.
656// It should be the case that every full precision, printed value can be re-parsed back to the669pub fn formatZigIdentifier(
657// same type unambiguously.670 bytes: []const u8,
671 options: FormatOptions,
672 writer: anytype,
673) !void {
674 if (isValidZigIdentifier(bytes)) {
675 return writer.writeAll(bytes);
676 }
677 try writer.writeAll("@\"");
678 try formatZigEscapes(bytes, options, writer);
679 try writer.writeByte('"');
680}
681
682fn isValidZigIdentifier(bytes: []const u8) bool {
683 for (bytes) |c, i| {
684 switch (c) {
685 '_', 'a'...'z', 'A'...'Z' => {},
686 '0'...'9' => if (i == 0) return false,
687 else => return false,
688 }
689 }
690 return std.zig.Token.getKeyword(bytes) == null;
691}
692
693pub fn formatZigEscapes(
694 bytes: []const u8,
695 options: FormatOptions,
696 writer: anytype,
697) !void {
698 for (bytes) |c| {
699 const s: []const u8 = switch (c) {
700 '\"' => "\\\"",
701 '\'' => "\\'",
702 '\\' => "\\\\",
703 '\n' => "\\n",
704 '\r' => "\\r",
705 '\t' => "\\t",
706 // Handle the remaining escapes Zig doesn't support by turning them
707 // into their respective hex representation
708 else => if (std.ascii.isCntrl(c)) {
709 try writer.writeAll("\\x");
710 try formatInt(c, 16, false, .{ .width = 2, .fill = '0' }, writer);
711 continue;
712 } else {
713 try writer.writeByte(c);
714 continue;
715 },
716 };
717 try writer.writeAll(s);
718 }
719}
720
721/// Print a float in scientific notation to the specified precision. Null uses full precision.
722/// It should be the case that every full precision, printed value can be re-parsed back to the
723/// same type unambiguously.
658pub fn formatFloatScientific(724pub fn formatFloatScientific(
659 value: anytype,725 value: anytype,
660 options: FormatOptions,726 options: FormatOptions,
...@@ -746,8 +812,8 @@ pub fn formatFloatScientific(...@@ -746,8 +812,8 @@ pub fn formatFloatScientific(
746 }812 }
747}813}
748814
749// Print a float of the format x.yyyyy where the number of y is specified by the precision argument.815/// Print a float of the format x.yyyyy where the number of y is specified by the precision argument.
750// By default floats are printed at full precision (no rounding).816/// By default floats are printed at full precision (no rounding).
751pub fn formatFloatDecimal(817pub fn formatFloatDecimal(
752 value: anytype,818 value: anytype,
753 options: FormatOptions,819 options: FormatOptions,
...@@ -1136,7 +1202,7 @@ pub fn bufPrintZ(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintErr...@@ -1136,7 +1202,7 @@ pub fn bufPrintZ(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintErr
1136 return result[0 .. result.len - 1 :0];1202 return result[0 .. result.len - 1 :0];
1137}1203}
11381204
1139// Count the characters needed for format. Useful for preallocating memory1205/// Count the characters needed for format. Useful for preallocating memory
1140pub fn count(comptime fmt: []const u8, args: anytype) u64 {1206pub fn count(comptime fmt: []const u8, args: anytype) u64 {
1141 var counting_writer = std.io.countingWriter(std.io.null_writer);1207 var counting_writer = std.io.countingWriter(std.io.null_writer);
1142 format(counting_writer.writer(), fmt, args) catch |err| switch (err) {};1208 format(counting_writer.writer(), fmt, args) catch |err| switch (err) {};
...@@ -1334,6 +1400,14 @@ test "escape non-printable" {...@@ -1334,6 +1400,14 @@ test "escape non-printable" {
1334 try testFmt("ab\\xFFc", "{E}", .{"ab\xffc"});1400 try testFmt("ab\\xFFc", "{E}", .{"ab\xffc"});
1335}1401}
13361402
1403test "escape invalid identifiers" {
1404 try testFmt("@\"while\"", "{z}", .{"while"});
1405 try testFmt("hello", "{z}", .{"hello"});
1406 try testFmt("@\"11\\\"23\"", "{z}", .{"11\"23"});
1407 try testFmt("@\"11\\x0f23\"", "{z}", .{"11\x0F23"});
1408 try testFmt("\\x0f", "{Z}", .{0x0f});
1409}
1410
1337test "pointer" {1411test "pointer" {
1338 {1412 {
1339 const value = @intToPtr(*align(1) i32, 0xdeadbeef);1413 const value = @intToPtr(*align(1) i32, 0xdeadbeef);