| author | |
| committer | |
| log | e51bc19e4a45211476491f29d2beff73ff6be570 |
| tree | 8558a6b83f13526edb618f8c8e968a69b34f615a |
| parent | 71ac5b151524288562bb78d9b0924bb3b0ba5e1c |
| parent | e8ca1b254d41d5711dc5294d99b8d81c74f36add |
| signature |
std.fmt add specifier for printing Zig identifiers7 files changed, 94 insertions(+), 121 deletions(-)
lib/std/build.zig+9-16| ... | ... | @@ -1767,26 +1767,21 @@ pub const LibExeObjStep = struct { |
| 1767 | 1767 | const out = self.build_options_contents.outStream(); |
| 1768 | 1768 | switch (T) { |
| 1769 | 1769 | []const []const u8 => { |
| 1770 | out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{name}) catch unreachable; | |
| 1770 | out.print("pub const {z}: []const []const u8 = &[_][]const u8{{\n", .{name}) catch unreachable; | |
| 1771 | 1771 | for (value) |slice| { |
| 1772 | out.writeAll(" ") catch unreachable; | |
| 1773 | std.zig.renderStringLiteral(slice, out) catch unreachable; | |
| 1774 | out.writeAll(",\n") catch unreachable; | |
| 1772 | out.print(" \"{Z}\",\n", .{slice}) catch unreachable; | |
| 1775 | 1773 | } |
| 1776 | 1774 | out.writeAll("};\n") catch unreachable; |
| 1777 | 1775 | return; |
| 1778 | 1776 | }, |
| 1779 | 1777 | []const u8 => { |
| 1780 | out.print("pub const {}: []const u8 = ", .{name}) catch unreachable; | |
| 1781 | std.zig.renderStringLiteral(value, out) catch unreachable; | |
| 1782 | out.writeAll(";\n") catch unreachable; | |
| 1778 | out.print("pub const {z}: []const u8 = \"{Z}\";\n", .{ name, value }) catch unreachable; | |
| 1783 | 1779 | return; |
| 1784 | 1780 | }, |
| 1785 | 1781 | ?[]const u8 => { |
| 1786 | out.print("pub const {}: ?[]const u8 = ", .{name}) catch unreachable; | |
| 1782 | out.print("pub const {z}: ?[]const u8 = ", .{name}) catch unreachable; | |
| 1787 | 1783 | if (value) |payload| { |
| 1788 | std.zig.renderStringLiteral(payload, out) catch unreachable; | |
| 1789 | out.writeAll(";\n") catch unreachable; | |
| 1784 | out.print("\"{Z}\";\n", .{payload}) catch unreachable; | |
| 1790 | 1785 | } else { |
| 1791 | 1786 | out.writeAll("null;\n") catch unreachable; |
| 1792 | 1787 | } |
| ... | ... | @@ -1796,15 +1791,15 @@ pub const LibExeObjStep = struct { |
| 1796 | 1791 | } |
| 1797 | 1792 | switch (@typeInfo(T)) { |
| 1798 | 1793 | .Enum => |enum_info| { |
| 1799 | out.print("pub const {} = enum {{\n", .{@typeName(T)}) catch unreachable; | |
| 1794 | out.print("pub const {z} = enum {{\n", .{@typeName(T)}) catch unreachable; | |
| 1800 | 1795 | inline for (enum_info.fields) |field| { |
| 1801 | out.print(" {},\n", .{field.name}) catch unreachable; | |
| 1796 | out.print(" {z},\n", .{field.name}) catch unreachable; | |
| 1802 | 1797 | } |
| 1803 | 1798 | out.writeAll("};\n") catch unreachable; |
| 1804 | 1799 | }, |
| 1805 | 1800 | else => {}, |
| 1806 | 1801 | } |
| 1807 | out.print("pub const {} = {};\n", .{ name, value }) catch unreachable; | |
| 1802 | out.print("pub const {z} = {};\n", .{ name, value }) catch unreachable; | |
| 1808 | 1803 | } |
| 1809 | 1804 | |
| 1810 | 1805 | /// The value is the path in the cache dir. |
| ... | ... | @@ -2017,9 +2012,7 @@ pub const LibExeObjStep = struct { |
| 2017 | 2012 | // Render build artifact options at the last minute, now that the path is known. |
| 2018 | 2013 | for (self.build_options_artifact_args.items) |item| { |
| 2019 | 2014 | const out = self.build_options_contents.writer(); |
| 2020 | out.print("pub const {}: []const u8 = ", .{item.name}) catch unreachable; | |
| 2021 | std.zig.renderStringLiteral(item.artifact.getOutputPath(), out) catch unreachable; | |
| 2022 | out.writeAll(";\n") catch unreachable; | |
| 2015 | out.print("pub const {}: []const u8 = \"{Z}\";\n", .{ item.name, item.artifact.getOutputPath() }) catch unreachable; | |
| 2023 | 2016 | } |
| 2024 | 2017 | |
| 2025 | 2018 | const build_options_file = try fs.path.join( |
lib/std/fmt.zig+76-6| ... | ... | @@ -65,6 +65,8 @@ fn peekIsAlign(comptime fmt: []const u8) bool { |
| 65 | 65 | /// - format the non-numeric value as a string of bytes in hexadecimal notation ("binary dump") in either lower case or upper case |
| 66 | 66 | /// - output numeric value in hexadecimal notation |
| 67 | 67 | /// - `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 | 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 | 71 | /// - `e` and `E`: if printing a string, escape non-printable characters |
| 70 | 72 | /// - `e`: output floating point value in scientific notation |
| ... | ... | @@ -543,6 +545,13 @@ pub fn formatIntValue( |
| 543 | 545 | } else { |
| 544 | 546 | @compileError("Cannot print integer that is larger than 8 bits as a ascii"); |
| 545 | 547 | } |
| 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 | } | |
| 546 | 555 | } else if (comptime std.mem.eql(u8, fmt, "b")) { |
| 547 | 556 | radix = 2; |
| 548 | 557 | uppercase = false; |
| ... | ... | @@ -612,6 +621,10 @@ pub fn formatText( |
| 612 | 621 | } |
| 613 | 622 | } |
| 614 | 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 | 628 | } else { |
| 616 | 629 | @compileError("Unknown format string: '" ++ fmt ++ "'"); |
| 617 | 630 | } |
| ... | ... | @@ -652,9 +665,55 @@ pub fn formatBuf( |
| 652 | 665 | } |
| 653 | 666 | } |
| 654 | 667 | |
| 655 | // Print a float in scientific notation to the specified precision. Null uses full precision. | |
| 656 | // It should be the case that every full precision, printed value can be re-parsed back to the | |
| 657 | // same type unambiguously. | |
| 668 | /// Print the string as a Zig identifier escaping it with @"" syntax if needed. | |
| 669 | pub fn formatZigIdentifier( | |
| 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 | ||
| 682 | fn 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 | ||
| 693 | pub fn formatZigEscapes( | |
| 694 | bytes: []const u8, | |
| 695 | options: FormatOptions, | |
| 696 | writer: anytype, | |
| 697 | ) !void { | |
| 698 | for (bytes) |byte| switch (byte) { | |
| 699 | '\n' => try writer.writeAll("\\n"), | |
| 700 | '\r' => try writer.writeAll("\\r"), | |
| 701 | '\t' => try writer.writeAll("\\t"), | |
| 702 | '\\' => try writer.writeAll("\\\\"), | |
| 703 | '"' => try writer.writeAll("\\\""), | |
| 704 | '\'' => try writer.writeAll("\\'"), | |
| 705 | ' ', '!', '#'...'&', '('...'[', ']'...'~' => try writer.writeByte(byte), | |
| 706 | // Use hex escapes for rest any unprintable characters. | |
| 707 | else => { | |
| 708 | try writer.writeAll("\\x"); | |
| 709 | try formatInt(byte, 16, false, .{ .width = 2, .fill = '0' }, writer); | |
| 710 | }, | |
| 711 | }; | |
| 712 | } | |
| 713 | ||
| 714 | /// Print a float in scientific notation to the specified precision. Null uses full precision. | |
| 715 | /// It should be the case that every full precision, printed value can be re-parsed back to the | |
| 716 | /// same type unambiguously. | |
| 658 | 717 | pub fn formatFloatScientific( |
| 659 | 718 | value: anytype, |
| 660 | 719 | options: FormatOptions, |
| ... | ... | @@ -746,8 +805,8 @@ pub fn formatFloatScientific( |
| 746 | 805 | } |
| 747 | 806 | } |
| 748 | 807 | |
| 749 | // 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). | |
| 808 | /// Print a float of the format x.yyyyy where the number of y is specified by the precision argument. | |
| 809 | /// By default floats are printed at full precision (no rounding). | |
| 751 | 810 | pub fn formatFloatDecimal( |
| 752 | 811 | value: anytype, |
| 753 | 812 | options: FormatOptions, |
| ... | ... | @@ -1136,7 +1195,7 @@ pub fn bufPrintZ(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintErr |
| 1136 | 1195 | return result[0 .. result.len - 1 :0]; |
| 1137 | 1196 | } |
| 1138 | 1197 | |
| 1139 | // Count the characters needed for format. Useful for preallocating memory | |
| 1198 | /// Count the characters needed for format. Useful for preallocating memory | |
| 1140 | 1199 | pub fn count(comptime fmt: []const u8, args: anytype) u64 { |
| 1141 | 1200 | var counting_writer = std.io.countingWriter(std.io.null_writer); |
| 1142 | 1201 | format(counting_writer.writer(), fmt, args) catch |err| switch (err) {}; |
| ... | ... | @@ -1334,6 +1393,17 @@ test "escape non-printable" { |
| 1334 | 1393 | try testFmt("ab\\xFFc", "{E}", .{"ab\xffc"}); |
| 1335 | 1394 | } |
| 1336 | 1395 | |
| 1396 | test "escape invalid identifiers" { | |
| 1397 | try testFmt("@\"while\"", "{z}", .{"while"}); | |
| 1398 | try testFmt("hello", "{z}", .{"hello"}); | |
| 1399 | try testFmt("@\"11\\\"23\"", "{z}", .{"11\"23"}); | |
| 1400 | try testFmt("@\"11\\x0f23\"", "{z}", .{"11\x0F23"}); | |
| 1401 | try testFmt("\\x0f", "{Z}", .{0x0f}); | |
| 1402 | try testFmt( | |
| 1403 | \\" \\ hi \x07 \x11 \" derp \'" | |
| 1404 | , "\"{Z}\"", .{" \\ hi \x07 \x11 \" derp '"}); | |
| 1405 | } | |
| 1406 | ||
| 1337 | 1407 | test "pointer" { |
| 1338 | 1408 | { |
| 1339 | 1409 | const value = @intToPtr(*align(1) i32, 0xdeadbeef); |
lib/std/zig.zig-1| ... | ... | @@ -11,7 +11,6 @@ pub const Tokenizer = tokenizer.Tokenizer; |
| 11 | 11 | pub const parse = @import("zig/parse.zig").parse; |
| 12 | 12 | pub const parseStringLiteral = @import("zig/string_literal.zig").parse; |
| 13 | 13 | pub const render = @import("zig/render.zig").render; |
| 14 | pub const renderStringLiteral = @import("zig/string_literal.zig").render; | |
| 15 | 14 | pub const ast = @import("zig/ast.zig"); |
| 16 | 15 | pub const system = @import("zig/system.zig"); |
| 17 | 16 | pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget; |
lib/std/zig/string_literal.zig-30| ... | ... | @@ -127,33 +127,3 @@ test "parse" { |
| 127 | 127 | expect(eql(u8, "foo", try parse(alloc, "\"f\x6f\x6f\"", &bad_index))); |
| 128 | 128 | expect(eql(u8, "f💯", try parse(alloc, "\"f\u{1f4af}\"", &bad_index))); |
| 129 | 129 | } |
| 130 | ||
| 131 | /// Writes a Zig-syntax escaped string literal to the stream. Includes the double quotes. | |
| 132 | pub fn render(utf8: []const u8, out_stream: anytype) !void { | |
| 133 | try out_stream.writeByte('"'); | |
| 134 | for (utf8) |byte| switch (byte) { | |
| 135 | '\n' => try out_stream.writeAll("\\n"), | |
| 136 | '\r' => try out_stream.writeAll("\\r"), | |
| 137 | '\t' => try out_stream.writeAll("\\t"), | |
| 138 | '\\' => try out_stream.writeAll("\\\\"), | |
| 139 | '"' => try out_stream.writeAll("\\\""), | |
| 140 | ' ', '!', '#'...'[', ']'...'~' => try out_stream.writeByte(byte), | |
| 141 | else => try out_stream.print("\\x{x:0>2}", .{byte}), | |
| 142 | }; | |
| 143 | try out_stream.writeByte('"'); | |
| 144 | } | |
| 145 | ||
| 146 | test "render" { | |
| 147 | const expect = std.testing.expect; | |
| 148 | const eql = std.mem.eql; | |
| 149 | ||
| 150 | var fixed_buf_mem: [32]u8 = undefined; | |
| 151 | ||
| 152 | { | |
| 153 | var fbs = std.io.fixedBufferStream(&fixed_buf_mem); | |
| 154 | try render(" \\ hi \x07 \x11 \" derp", fbs.outStream()); | |
| 155 | expect(eql(u8, | |
| 156 | \\" \\ hi \x07 \x11 \" derp" | |
| 157 | , fbs.getWritten())); | |
| 158 | } | |
| 159 | } |
src/translate_c.zig+3-63| ... | ... | @@ -1972,16 +1972,7 @@ fn transStringLiteral( |
| 1972 | 1972 | const bytes_ptr = stmt.getString_bytes_begin_size(&len); |
| 1973 | 1973 | const str = bytes_ptr[0..len]; |
| 1974 | 1974 | |
| 1975 | var char_buf: [4]u8 = undefined; | |
| 1976 | len = 0; | |
| 1977 | for (str) |c| len += escapeChar(c, &char_buf).len; | |
| 1978 | ||
| 1979 | const buf = try rp.c.arena.alloc(u8, len + "\"\"".len); | |
| 1980 | buf[0] = '"'; | |
| 1981 | writeEscapedString(buf[1..], str); | |
| 1982 | buf[buf.len - 1] = '"'; | |
| 1983 | ||
| 1984 | const token = try appendToken(rp.c, .StringLiteral, buf); | |
| 1975 | const token = try appendTokenFmt(rp.c, .StringLiteral, "\"{Z}\"", .{str}); | |
| 1985 | 1976 | const node = try rp.c.arena.create(ast.Node.OneToken); |
| 1986 | 1977 | node.* = .{ |
| 1987 | 1978 | .base = .{ .tag = .StringLiteral }, |
| ... | ... | @@ -1999,41 +1990,6 @@ fn transStringLiteral( |
| 1999 | 1990 | } |
| 2000 | 1991 | } |
| 2001 | 1992 | |
| 2002 | fn escapedStringLen(s: []const u8) usize { | |
| 2003 | var len: usize = 0; | |
| 2004 | var char_buf: [4]u8 = undefined; | |
| 2005 | for (s) |c| len += escapeChar(c, &char_buf).len; | |
| 2006 | return len; | |
| 2007 | } | |
| 2008 | ||
| 2009 | fn writeEscapedString(buf: []u8, s: []const u8) void { | |
| 2010 | var char_buf: [4]u8 = undefined; | |
| 2011 | var i: usize = 0; | |
| 2012 | for (s) |c| { | |
| 2013 | const escaped = escapeChar(c, &char_buf); | |
| 2014 | mem.copy(u8, buf[i..], escaped); | |
| 2015 | i += escaped.len; | |
| 2016 | } | |
| 2017 | } | |
| 2018 | ||
| 2019 | // Returns either a string literal or a slice of `buf`. | |
| 2020 | fn escapeChar(c: u8, char_buf: *[4]u8) []const u8 { | |
| 2021 | return switch (c) { | |
| 2022 | '\"' => "\\\"", | |
| 2023 | '\'' => "\\'", | |
| 2024 | '\\' => "\\\\", | |
| 2025 | '\n' => "\\n", | |
| 2026 | '\r' => "\\r", | |
| 2027 | '\t' => "\\t", | |
| 2028 | // Handle the remaining escapes Zig doesn't support by turning them | |
| 2029 | // into their respective hex representation | |
| 2030 | else => if (std.ascii.isCntrl(c)) | |
| 2031 | std.fmt.bufPrint(char_buf, "\\x{x:0>2}", .{c}) catch unreachable | |
| 2032 | else | |
| 2033 | std.fmt.bufPrint(char_buf, "{c}", .{c}) catch unreachable, | |
| 2034 | }; | |
| 2035 | } | |
| 2036 | ||
| 2037 | 1993 | fn transCCast( |
| 2038 | 1994 | rp: RestorePoint, |
| 2039 | 1995 | scope: *Scope, |
| ... | ... | @@ -2922,8 +2878,7 @@ fn transCharLiteral( |
| 2922 | 2878 | if (val > 255) |
| 2923 | 2879 | break :blk try transCreateNodeInt(rp.c, val); |
| 2924 | 2880 | } |
| 2925 | var char_buf: [4]u8 = undefined; | |
| 2926 | const token = try appendTokenFmt(rp.c, .CharLiteral, "'{}'", .{escapeChar(@intCast(u8, val), &char_buf)}); | |
| 2881 | const token = try appendTokenFmt(rp.c, .CharLiteral, "'{Z}'", .{@intCast(u8, val)}); | |
| 2927 | 2882 | const node = try rp.c.arena.create(ast.Node.OneToken); |
| 2928 | 2883 | node.* = .{ |
| 2929 | 2884 | .base = .{ .tag = .CharLiteral }, |
| ... | ... | @@ -5247,23 +5202,8 @@ fn isZigPrimitiveType(name: []const u8) bool { |
| 5247 | 5202 | mem.eql(u8, name, "c_ulonglong"); |
| 5248 | 5203 | } |
| 5249 | 5204 | |
| 5250 | fn isValidZigIdentifier(name: []const u8) bool { | |
| 5251 | for (name) |c, i| { | |
| 5252 | switch (c) { | |
| 5253 | '_', 'a'...'z', 'A'...'Z' => {}, | |
| 5254 | '0'...'9' => if (i == 0) return false, | |
| 5255 | else => return false, | |
| 5256 | } | |
| 5257 | } | |
| 5258 | return true; | |
| 5259 | } | |
| 5260 | ||
| 5261 | 5205 | fn appendIdentifier(c: *Context, name: []const u8) !ast.TokenIndex { |
| 5262 | if (!isValidZigIdentifier(name) or std.zig.Token.getKeyword(name) != null) { | |
| 5263 | return appendTokenFmt(c, .Identifier, "@\"{}\"", .{name}); | |
| 5264 | } else { | |
| 5265 | return appendTokenFmt(c, .Identifier, "{}", .{name}); | |
| 5266 | } | |
| 5206 | return appendTokenFmt(c, .Identifier, "{z}", .{name}); | |
| 5267 | 5207 | } |
| 5268 | 5208 | |
| 5269 | 5209 | fn transCreateNodeIdentifier(c: *Context, name: []const u8) !*ast.Node { |
src/value.zig+2-1| ... | ... | @@ -350,7 +350,8 @@ pub const Value = extern union { |
| 350 | 350 | val = elem_ptr.array_ptr; |
| 351 | 351 | }, |
| 352 | 352 | .empty_array => return out_stream.writeAll(".{}"), |
| 353 | .enum_literal, .bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream), | |
| 353 | .enum_literal => return out_stream.print(".{z}", .{self.cast(Payload.Bytes).?.data}), | |
| 354 | .bytes => return out_stream.print("\"{Z}\"", .{self.cast(Payload.Bytes).?.data}), | |
| 354 | 355 | .repeated => { |
| 355 | 356 | try out_stream.writeAll("(repeated) "); |
| 356 | 357 | val = val.cast(Payload.Repeated).?.val; |
src/zir.zig+4-4| ... | ... | @@ -1216,17 +1216,17 @@ const Writer = struct { |
| 1216 | 1216 | try stream.writeByte('}'); |
| 1217 | 1217 | }, |
| 1218 | 1218 | bool => return stream.writeByte("01"[@boolToInt(param)]), |
| 1219 | []u8, []const u8 => return std.zig.renderStringLiteral(param, stream), | |
| 1219 | []u8, []const u8 => return stream.print("\"{Z}\"", .{param}), | |
| 1220 | 1220 | BigIntConst, usize => return stream.print("{}", .{param}), |
| 1221 | 1221 | TypedValue => unreachable, // this is a special case |
| 1222 | 1222 | *IrModule.Decl => unreachable, // this is a special case |
| 1223 | 1223 | *Inst.Block => { |
| 1224 | 1224 | const name = self.block_table.get(param).?; |
| 1225 | return std.zig.renderStringLiteral(name, stream); | |
| 1225 | return stream.print("\"{Z}\"", .{name}); | |
| 1226 | 1226 | }, |
| 1227 | 1227 | *Inst.Loop => { |
| 1228 | 1228 | const name = self.loop_table.get(param).?; |
| 1229 | return std.zig.renderStringLiteral(name, stream); | |
| 1229 | return stream.print("\"{Z}\"", .{name}); | |
| 1230 | 1230 | }, |
| 1231 | 1231 | [][]const u8 => { |
| 1232 | 1232 | try stream.writeByte('['); |
| ... | ... | @@ -1234,7 +1234,7 @@ const Writer = struct { |
| 1234 | 1234 | if (i != 0) { |
| 1235 | 1235 | try stream.writeAll(", "); |
| 1236 | 1236 | } |
| 1237 | try std.zig.renderStringLiteral(str, stream); | |
| 1237 | try stream.print("\"{Z}\"", .{str}); | |
| 1238 | 1238 | } |
| 1239 | 1239 | try stream.writeByte(']'); |
| 1240 | 1240 | }, |