| ... | @@ -10,6 +10,7 @@ pub const Tokenizer = tokenizer.Tokenizer; | ... | @@ -10,6 +10,7 @@ pub const Tokenizer = tokenizer.Tokenizer; |
| 10 | pub const string_literal = @import("zig/string_literal.zig"); | 10 | pub const string_literal = @import("zig/string_literal.zig"); |
| 11 | pub const number_literal = @import("zig/number_literal.zig"); | 11 | pub const number_literal = @import("zig/number_literal.zig"); |
| 12 | pub const primitives = @import("zig/primitives.zig"); | 12 | pub const primitives = @import("zig/primitives.zig"); |
| | 13 | pub const isPrimitive = primitives.isPrimitive; |
| 13 | pub const Ast = @import("zig/Ast.zig"); | 14 | pub const Ast = @import("zig/Ast.zig"); |
| 14 | pub const AstGen = @import("zig/AstGen.zig"); | 15 | pub const AstGen = @import("zig/AstGen.zig"); |
| 15 | pub const Zir = @import("zig/Zir.zig"); | 16 | pub const Zir = @import("zig/Zir.zig"); |
| ... | @@ -728,20 +729,87 @@ const tokenizer = @import("zig/tokenizer.zig"); | ... | @@ -728,20 +729,87 @@ const tokenizer = @import("zig/tokenizer.zig"); |
| 728 | const assert = std.debug.assert; | 729 | const assert = std.debug.assert; |
| 729 | const Allocator = std.mem.Allocator; | 730 | const Allocator = std.mem.Allocator; |
| 730 | | 731 | |
| 731 | /// Return a Formatter for a Zig identifier | 732 | /// Return a Formatter for a Zig identifier, escaping it with `@""` syntax if needed. |
| | 733 | /// |
| | 734 | /// - An empty `{}` format specifier escapes invalid identifiers, identifiers that shadow primitives |
| | 735 | /// and the reserved `_` identifier. |
| | 736 | /// - Add `p` to the specifier to render identifiers that shadow primitives unescaped. |
| | 737 | /// - Add `_` to the specifier to render the reserved `_` identifier unescaped. |
| | 738 | /// - `p` and `_` can be combined, e.g. `{p_}`. |
| | 739 | /// |
| 732 | pub fn fmtId(bytes: []const u8) std.fmt.Formatter(formatId) { | 740 | pub fn fmtId(bytes: []const u8) std.fmt.Formatter(formatId) { |
| 733 | return .{ .data = bytes }; | 741 | return .{ .data = bytes }; |
| 734 | } | 742 | } |
| 735 | | 743 | |
| 736 | /// Print the string as a Zig identifier escaping it with @"" syntax if needed. | 744 | test fmtId { |
| | 745 | const expectFmt = std.testing.expectFmt; |
| | 746 | try expectFmt("@\"while\"", "{}", .{fmtId("while")}); |
| | 747 | try expectFmt("@\"while\"", "{p}", .{fmtId("while")}); |
| | 748 | try expectFmt("@\"while\"", "{_}", .{fmtId("while")}); |
| | 749 | try expectFmt("@\"while\"", "{p_}", .{fmtId("while")}); |
| | 750 | try expectFmt("@\"while\"", "{_p}", .{fmtId("while")}); |
| | 751 | |
| | 752 | try expectFmt("hello", "{}", .{fmtId("hello")}); |
| | 753 | try expectFmt("hello", "{p}", .{fmtId("hello")}); |
| | 754 | try expectFmt("hello", "{_}", .{fmtId("hello")}); |
| | 755 | try expectFmt("hello", "{p_}", .{fmtId("hello")}); |
| | 756 | try expectFmt("hello", "{_p}", .{fmtId("hello")}); |
| | 757 | |
| | 758 | try expectFmt("@\"type\"", "{}", .{fmtId("type")}); |
| | 759 | try expectFmt("type", "{p}", .{fmtId("type")}); |
| | 760 | try expectFmt("@\"type\"", "{_}", .{fmtId("type")}); |
| | 761 | try expectFmt("type", "{p_}", .{fmtId("type")}); |
| | 762 | try expectFmt("type", "{_p}", .{fmtId("type")}); |
| | 763 | |
| | 764 | try expectFmt("@\"_\"", "{}", .{fmtId("_")}); |
| | 765 | try expectFmt("@\"_\"", "{p}", .{fmtId("_")}); |
| | 766 | try expectFmt("_", "{_}", .{fmtId("_")}); |
| | 767 | try expectFmt("_", "{p_}", .{fmtId("_")}); |
| | 768 | try expectFmt("_", "{_p}", .{fmtId("_")}); |
| | 769 | |
| | 770 | try expectFmt("@\"i123\"", "{}", .{fmtId("i123")}); |
| | 771 | try expectFmt("i123", "{p}", .{fmtId("i123")}); |
| | 772 | try expectFmt("@\"4four\"", "{}", .{fmtId("4four")}); |
| | 773 | try expectFmt("_underscore", "{}", .{fmtId("_underscore")}); |
| | 774 | try expectFmt("@\"11\\\"23\"", "{}", .{fmtId("11\"23")}); |
| | 775 | try expectFmt("@\"11\\x0f23\"", "{}", .{fmtId("11\x0F23")}); |
| | 776 | |
| | 777 | // These are technically not currently legal in Zig. |
| | 778 | try expectFmt("@\"\"", "{}", .{fmtId("")}); |
| | 779 | try expectFmt("@\"\\x00\"", "{}", .{fmtId("\x00")}); |
| | 780 | } |
| | 781 | |
| | 782 | /// Print the string as a Zig identifier, escaping it with `@""` syntax if needed. |
| 737 | fn formatId( | 783 | fn formatId( |
| 738 | bytes: []const u8, | 784 | bytes: []const u8, |
| 739 | comptime unused_format_string: []const u8, | 785 | comptime fmt: []const u8, |
| 740 | options: std.fmt.FormatOptions, | 786 | options: std.fmt.FormatOptions, |
| 741 | writer: anytype, | 787 | writer: anytype, |
| 742 | ) !void { | 788 | ) !void { |
| 743 | _ = unused_format_string; | 789 | const allow_primitive, const allow_underscore = comptime parse_fmt: { |
| 744 | if (isValidId(bytes)) { | 790 | var allow_primitive = false; |
| | 791 | var allow_underscore = false; |
| | 792 | for (fmt) |char| { |
| | 793 | switch (char) { |
| | 794 | 'p' => if (!allow_primitive) { |
| | 795 | allow_primitive = true; |
| | 796 | continue; |
| | 797 | }, |
| | 798 | '_' => if (!allow_underscore) { |
| | 799 | allow_underscore = true; |
| | 800 | continue; |
| | 801 | }, |
| | 802 | else => {}, |
| | 803 | } |
| | 804 | @compileError("expected {}, {p}, {_}, {p_} or {_p}, found {" ++ fmt ++ "}"); |
| | 805 | } |
| | 806 | break :parse_fmt .{ allow_primitive, allow_underscore }; |
| | 807 | }; |
| | 808 | |
| | 809 | if (isValidId(bytes) and |
| | 810 | (allow_primitive or !std.zig.isPrimitive(bytes)) and |
| | 811 | (allow_underscore or !isUnderscore(bytes))) |
| | 812 | { |
| 745 | return writer.writeAll(bytes); | 813 | return writer.writeAll(bytes); |
| 746 | } | 814 | } |
| 747 | try writer.writeAll("@\""); | 815 | try writer.writeAll("@\""); |
| ... | @@ -757,12 +825,8 @@ pub fn fmtEscapes(bytes: []const u8) std.fmt.Formatter(stringEscape) { | ... | @@ -757,12 +825,8 @@ pub fn fmtEscapes(bytes: []const u8) std.fmt.Formatter(stringEscape) { |
| 757 | return .{ .data = bytes }; | 825 | return .{ .data = bytes }; |
| 758 | } | 826 | } |
| 759 | | 827 | |
| 760 | test "escape invalid identifiers" { | 828 | test fmtEscapes { |
| 761 | const expectFmt = std.testing.expectFmt; | 829 | const expectFmt = std.testing.expectFmt; |
| 762 | try expectFmt("@\"while\"", "{}", .{fmtId("while")}); | | |
| 763 | try expectFmt("hello", "{}", .{fmtId("hello")}); | | |
| 764 | try expectFmt("@\"11\\\"23\"", "{}", .{fmtId("11\"23")}); | | |
| 765 | try expectFmt("@\"11\\x0f23\"", "{}", .{fmtId("11\x0F23")}); | | |
| 766 | try expectFmt("\\x0f", "{}", .{fmtEscapes("\x0f")}); | 830 | try expectFmt("\\x0f", "{}", .{fmtEscapes("\x0f")}); |
| 767 | try expectFmt( | 831 | try expectFmt( |
| 768 | \\" \\ hi \x07 \x11 " derp \'" | 832 | \\" \\ hi \x07 \x11 " derp \'" |
| ... | @@ -816,7 +880,6 @@ pub fn stringEscape( | ... | @@ -816,7 +880,6 @@ pub fn stringEscape( |
| 816 | | 880 | |
| 817 | pub fn isValidId(bytes: []const u8) bool { | 881 | pub fn isValidId(bytes: []const u8) bool { |
| 818 | if (bytes.len == 0) return false; | 882 | if (bytes.len == 0) return false; |
| 819 | if (std.mem.eql(u8, bytes, "_")) return false; | | |
| 820 | for (bytes, 0..) |c, i| { | 883 | for (bytes, 0..) |c, i| { |
| 821 | switch (c) { | 884 | switch (c) { |
| 822 | '_', 'a'...'z', 'A'...'Z' => {}, | 885 | '_', 'a'...'z', 'A'...'Z' => {}, |
| ... | @@ -836,6 +899,18 @@ test isValidId { | ... | @@ -836,6 +899,18 @@ test isValidId { |
| 836 | try std.testing.expect(isValidId("i386")); | 899 | try std.testing.expect(isValidId("i386")); |
| 837 | } | 900 | } |
| 838 | | 901 | |
| | 902 | pub fn isUnderscore(bytes: []const u8) bool { |
| | 903 | return bytes.len == 1 and bytes[0] == '_'; |
| | 904 | } |
| | 905 | |
| | 906 | test isUnderscore { |
| | 907 | try std.testing.expect(isUnderscore("_")); |
| | 908 | try std.testing.expect(!isUnderscore("__")); |
| | 909 | try std.testing.expect(!isUnderscore("_foo")); |
| | 910 | try std.testing.expect(isUnderscore("\x5f")); |
| | 911 | try std.testing.expect(!isUnderscore("\\x5f")); |
| | 912 | } |
| | 913 | |
| 839 | pub fn readSourceFileToEndAlloc( | 914 | pub fn readSourceFileToEndAlloc( |
| 840 | allocator: Allocator, | 915 | allocator: Allocator, |
| 841 | input: std.fs.File, | 916 | input: std.fs.File, |